cssX/roojs-all.css
[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        
6039         var o = /* eval: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 jj = 0; jj < fl; jj++){
6119                 f = fi[jj];
6120                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6121                 this.ef[jj] = 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 vt = parseInt(this.getTotal(o), 10);
6128             if(!isNaN(vt)){
6129                 totalRecords = vt;
6130             }
6131         }
6132         if(s.successProperty){
6133             var vs = this.getSuccess(o);
6134             if(vs === false || vs === '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) {
6557                     return i;
6558                 }
6559             }
6560             return -1;
6561         };
6562     }
6563     /**
6564      * The parent node for this node. @type Node
6565      */
6566     this.parentNode = null;
6567     /**
6568      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6569      */
6570     this.firstChild = null;
6571     /**
6572      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6573      */
6574     this.lastChild = null;
6575     /**
6576      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6577      */
6578     this.previousSibling = null;
6579     /**
6580      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6581      */
6582     this.nextSibling = null;
6583
6584     this.addEvents({
6585        /**
6586         * @event append
6587         * Fires when a new child node is appended
6588         * @param {Tree} tree The owner tree
6589         * @param {Node} this This node
6590         * @param {Node} node The newly appended node
6591         * @param {Number} index The index of the newly appended node
6592         */
6593        "append" : true,
6594        /**
6595         * @event remove
6596         * Fires when a child node is removed
6597         * @param {Tree} tree The owner tree
6598         * @param {Node} this This node
6599         * @param {Node} node The removed node
6600         */
6601        "remove" : true,
6602        /**
6603         * @event move
6604         * Fires when this node is moved to a new location in the tree
6605         * @param {Tree} tree The owner tree
6606         * @param {Node} this This node
6607         * @param {Node} oldParent The old parent of this node
6608         * @param {Node} newParent The new parent of this node
6609         * @param {Number} index The index it was moved to
6610         */
6611        "move" : true,
6612        /**
6613         * @event insert
6614         * Fires when a new child node is inserted.
6615         * @param {Tree} tree The owner tree
6616         * @param {Node} this This node
6617         * @param {Node} node The child node inserted
6618         * @param {Node} refNode The child node the node was inserted before
6619         */
6620        "insert" : true,
6621        /**
6622         * @event beforeappend
6623         * Fires before a new child is appended, return false to cancel the append.
6624         * @param {Tree} tree The owner tree
6625         * @param {Node} this This node
6626         * @param {Node} node The child node to be appended
6627         */
6628        "beforeappend" : true,
6629        /**
6630         * @event beforeremove
6631         * Fires before a child is removed, return false to cancel the remove.
6632         * @param {Tree} tree The owner tree
6633         * @param {Node} this This node
6634         * @param {Node} node The child node to be removed
6635         */
6636        "beforeremove" : true,
6637        /**
6638         * @event beforemove
6639         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6640         * @param {Tree} tree The owner tree
6641         * @param {Node} this This node
6642         * @param {Node} oldParent The parent of this node
6643         * @param {Node} newParent The new parent this node is moving to
6644         * @param {Number} index The index it is being moved to
6645         */
6646        "beforemove" : true,
6647        /**
6648         * @event beforeinsert
6649         * Fires before a new child is inserted, return false to cancel the insert.
6650         * @param {Tree} tree The owner tree
6651         * @param {Node} this This node
6652         * @param {Node} node The child node to be inserted
6653         * @param {Node} refNode The child node the node is being inserted before
6654         */
6655        "beforeinsert" : true
6656    });
6657     this.listeners = this.attributes.listeners;
6658     Roo.data.Node.superclass.constructor.call(this);
6659 };
6660
6661 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6662     fireEvent : function(evtName){
6663         // first do standard event for this node
6664         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6665             return false;
6666         }
6667         // then bubble it up to the tree if the event wasn't cancelled
6668         var ot = this.getOwnerTree();
6669         if(ot){
6670             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6671                 return false;
6672             }
6673         }
6674         return true;
6675     },
6676
6677     /**
6678      * Returns true if this node is a leaf
6679      * @return {Boolean}
6680      */
6681     isLeaf : function(){
6682         return this.leaf === true;
6683     },
6684
6685     // private
6686     setFirstChild : function(node){
6687         this.firstChild = node;
6688     },
6689
6690     //private
6691     setLastChild : function(node){
6692         this.lastChild = node;
6693     },
6694
6695
6696     /**
6697      * Returns true if this node is the last child of its parent
6698      * @return {Boolean}
6699      */
6700     isLast : function(){
6701        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6702     },
6703
6704     /**
6705      * Returns true if this node is the first child of its parent
6706      * @return {Boolean}
6707      */
6708     isFirst : function(){
6709        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6710     },
6711
6712     hasChildNodes : function(){
6713         return !this.isLeaf() && this.childNodes.length > 0;
6714     },
6715
6716     /**
6717      * Insert node(s) as the last child node of this node.
6718      * @param {Node/Array} node The node or Array of nodes to append
6719      * @return {Node} The appended node if single append, or null if an array was passed
6720      */
6721     appendChild : function(node){
6722         var multi = false;
6723         if(node instanceof Array){
6724             multi = node;
6725         }else if(arguments.length > 1){
6726             multi = arguments;
6727         }
6728         // if passed an array or multiple args do them one by one
6729         if(multi){
6730             for(var i = 0, len = multi.length; i < len; i++) {
6731                 this.appendChild(multi[i]);
6732             }
6733         }else{
6734             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6735                 return false;
6736             }
6737             var index = this.childNodes.length;
6738             var oldParent = node.parentNode;
6739             // it's a move, make sure we move it cleanly
6740             if(oldParent){
6741                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6742                     return false;
6743                 }
6744                 oldParent.removeChild(node);
6745             }
6746             index = this.childNodes.length;
6747             if(index == 0){
6748                 this.setFirstChild(node);
6749             }
6750             this.childNodes.push(node);
6751             node.parentNode = this;
6752             var ps = this.childNodes[index-1];
6753             if(ps){
6754                 node.previousSibling = ps;
6755                 ps.nextSibling = node;
6756             }else{
6757                 node.previousSibling = null;
6758             }
6759             node.nextSibling = null;
6760             this.setLastChild(node);
6761             node.setOwnerTree(this.getOwnerTree());
6762             this.fireEvent("append", this.ownerTree, this, node, index);
6763             if(oldParent){
6764                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6765             }
6766             return node;
6767         }
6768     },
6769
6770     /**
6771      * Removes a child node from this node.
6772      * @param {Node} node The node to remove
6773      * @return {Node} The removed node
6774      */
6775     removeChild : function(node){
6776         var index = this.childNodes.indexOf(node);
6777         if(index == -1){
6778             return false;
6779         }
6780         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6781             return false;
6782         }
6783
6784         // remove it from childNodes collection
6785         this.childNodes.splice(index, 1);
6786
6787         // update siblings
6788         if(node.previousSibling){
6789             node.previousSibling.nextSibling = node.nextSibling;
6790         }
6791         if(node.nextSibling){
6792             node.nextSibling.previousSibling = node.previousSibling;
6793         }
6794
6795         // update child refs
6796         if(this.firstChild == node){
6797             this.setFirstChild(node.nextSibling);
6798         }
6799         if(this.lastChild == node){
6800             this.setLastChild(node.previousSibling);
6801         }
6802
6803         node.setOwnerTree(null);
6804         // clear any references from the node
6805         node.parentNode = null;
6806         node.previousSibling = null;
6807         node.nextSibling = null;
6808         this.fireEvent("remove", this.ownerTree, this, node);
6809         return node;
6810     },
6811
6812     /**
6813      * Inserts the first node before the second node in this nodes childNodes collection.
6814      * @param {Node} node The node to insert
6815      * @param {Node} refNode The node to insert before (if null the node is appended)
6816      * @return {Node} The inserted node
6817      */
6818     insertBefore : function(node, refNode){
6819         if(!refNode){ // like standard Dom, refNode can be null for append
6820             return this.appendChild(node);
6821         }
6822         // nothing to do
6823         if(node == refNode){
6824             return false;
6825         }
6826
6827         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6828             return false;
6829         }
6830         var index = this.childNodes.indexOf(refNode);
6831         var oldParent = node.parentNode;
6832         var refIndex = index;
6833
6834         // when moving internally, indexes will change after remove
6835         if(oldParent == this && this.childNodes.indexOf(node) < index){
6836             refIndex--;
6837         }
6838
6839         // it's a move, make sure we move it cleanly
6840         if(oldParent){
6841             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6842                 return false;
6843             }
6844             oldParent.removeChild(node);
6845         }
6846         if(refIndex == 0){
6847             this.setFirstChild(node);
6848         }
6849         this.childNodes.splice(refIndex, 0, node);
6850         node.parentNode = this;
6851         var ps = this.childNodes[refIndex-1];
6852         if(ps){
6853             node.previousSibling = ps;
6854             ps.nextSibling = node;
6855         }else{
6856             node.previousSibling = null;
6857         }
6858         node.nextSibling = refNode;
6859         refNode.previousSibling = node;
6860         node.setOwnerTree(this.getOwnerTree());
6861         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6862         if(oldParent){
6863             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6864         }
6865         return node;
6866     },
6867
6868     /**
6869      * Returns the child node at the specified index.
6870      * @param {Number} index
6871      * @return {Node}
6872      */
6873     item : function(index){
6874         return this.childNodes[index];
6875     },
6876
6877     /**
6878      * Replaces one child node in this node with another.
6879      * @param {Node} newChild The replacement node
6880      * @param {Node} oldChild The node to replace
6881      * @return {Node} The replaced node
6882      */
6883     replaceChild : function(newChild, oldChild){
6884         this.insertBefore(newChild, oldChild);
6885         this.removeChild(oldChild);
6886         return oldChild;
6887     },
6888
6889     /**
6890      * Returns the index of a child node
6891      * @param {Node} node
6892      * @return {Number} The index of the node or -1 if it was not found
6893      */
6894     indexOf : function(child){
6895         return this.childNodes.indexOf(child);
6896     },
6897
6898     /**
6899      * Returns the tree this node is in.
6900      * @return {Tree}
6901      */
6902     getOwnerTree : function(){
6903         // if it doesn't have one, look for one
6904         if(!this.ownerTree){
6905             var p = this;
6906             while(p){
6907                 if(p.ownerTree){
6908                     this.ownerTree = p.ownerTree;
6909                     break;
6910                 }
6911                 p = p.parentNode;
6912             }
6913         }
6914         return this.ownerTree;
6915     },
6916
6917     /**
6918      * Returns depth of this node (the root node has a depth of 0)
6919      * @return {Number}
6920      */
6921     getDepth : function(){
6922         var depth = 0;
6923         var p = this;
6924         while(p.parentNode){
6925             ++depth;
6926             p = p.parentNode;
6927         }
6928         return depth;
6929     },
6930
6931     // private
6932     setOwnerTree : function(tree){
6933         // if it's move, we need to update everyone
6934         if(tree != this.ownerTree){
6935             if(this.ownerTree){
6936                 this.ownerTree.unregisterNode(this);
6937             }
6938             this.ownerTree = tree;
6939             var cs = this.childNodes;
6940             for(var i = 0, len = cs.length; i < len; i++) {
6941                 cs[i].setOwnerTree(tree);
6942             }
6943             if(tree){
6944                 tree.registerNode(this);
6945             }
6946         }
6947     },
6948
6949     /**
6950      * Returns the path for this node. The path can be used to expand or select this node programmatically.
6951      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
6952      * @return {String} The path
6953      */
6954     getPath : function(attr){
6955         attr = attr || "id";
6956         var p = this.parentNode;
6957         var b = [this.attributes[attr]];
6958         while(p){
6959             b.unshift(p.attributes[attr]);
6960             p = p.parentNode;
6961         }
6962         var sep = this.getOwnerTree().pathSeparator;
6963         return sep + b.join(sep);
6964     },
6965
6966     /**
6967      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6968      * function call will be the scope provided or the current node. The arguments to the function
6969      * will be the args provided or the current node. If the function returns false at any point,
6970      * the bubble is stopped.
6971      * @param {Function} fn The function to call
6972      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6973      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6974      */
6975     bubble : function(fn, scope, args){
6976         var p = this;
6977         while(p){
6978             if(fn.call(scope || p, args || p) === false){
6979                 break;
6980             }
6981             p = p.parentNode;
6982         }
6983     },
6984
6985     /**
6986      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6987      * function call will be the scope provided or the current node. The arguments to the function
6988      * will be the args provided or the current node. If the function returns false at any point,
6989      * the cascade is stopped on that branch.
6990      * @param {Function} fn The function to call
6991      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6992      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6993      */
6994     cascade : function(fn, scope, args){
6995         if(fn.call(scope || this, args || this) !== false){
6996             var cs = this.childNodes;
6997             for(var i = 0, len = cs.length; i < len; i++) {
6998                 cs[i].cascade(fn, scope, args);
6999             }
7000         }
7001     },
7002
7003     /**
7004      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7005      * function call will be the scope provided or the current node. The arguments to the function
7006      * will be the args provided or the current node. If the function returns false at any point,
7007      * the iteration stops.
7008      * @param {Function} fn The function to call
7009      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7010      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7011      */
7012     eachChild : function(fn, scope, args){
7013         var cs = this.childNodes;
7014         for(var i = 0, len = cs.length; i < len; i++) {
7015                 if(fn.call(scope || this, args || cs[i]) === false){
7016                     break;
7017                 }
7018         }
7019     },
7020
7021     /**
7022      * Finds the first child that has the attribute with the specified value.
7023      * @param {String} attribute The attribute name
7024      * @param {Mixed} value The value to search for
7025      * @return {Node} The found child or null if none was found
7026      */
7027     findChild : function(attribute, value){
7028         var cs = this.childNodes;
7029         for(var i = 0, len = cs.length; i < len; i++) {
7030                 if(cs[i].attributes[attribute] == value){
7031                     return cs[i];
7032                 }
7033         }
7034         return null;
7035     },
7036
7037     /**
7038      * Finds the first child by a custom function. The child matches if the function passed
7039      * returns true.
7040      * @param {Function} fn
7041      * @param {Object} scope (optional)
7042      * @return {Node} The found child or null if none was found
7043      */
7044     findChildBy : function(fn, scope){
7045         var cs = this.childNodes;
7046         for(var i = 0, len = cs.length; i < len; i++) {
7047                 if(fn.call(scope||cs[i], cs[i]) === true){
7048                     return cs[i];
7049                 }
7050         }
7051         return null;
7052     },
7053
7054     /**
7055      * Sorts this nodes children using the supplied sort function
7056      * @param {Function} fn
7057      * @param {Object} scope (optional)
7058      */
7059     sort : function(fn, scope){
7060         var cs = this.childNodes;
7061         var len = cs.length;
7062         if(len > 0){
7063             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7064             cs.sort(sortFn);
7065             for(var i = 0; i < len; i++){
7066                 var n = cs[i];
7067                 n.previousSibling = cs[i-1];
7068                 n.nextSibling = cs[i+1];
7069                 if(i == 0){
7070                     this.setFirstChild(n);
7071                 }
7072                 if(i == len-1){
7073                     this.setLastChild(n);
7074                 }
7075             }
7076         }
7077     },
7078
7079     /**
7080      * Returns true if this node is an ancestor (at any point) of the passed node.
7081      * @param {Node} node
7082      * @return {Boolean}
7083      */
7084     contains : function(node){
7085         return node.isAncestor(this);
7086     },
7087
7088     /**
7089      * Returns true if the passed node is an ancestor (at any point) of this node.
7090      * @param {Node} node
7091      * @return {Boolean}
7092      */
7093     isAncestor : function(node){
7094         var p = this.parentNode;
7095         while(p){
7096             if(p == node){
7097                 return true;
7098             }
7099             p = p.parentNode;
7100         }
7101         return false;
7102     },
7103
7104     toString : function(){
7105         return "[Node"+(this.id?" "+this.id:"")+"]";
7106     }
7107 });/*
7108  * Based on:
7109  * Ext JS Library 1.1.1
7110  * Copyright(c) 2006-2007, Ext JS, LLC.
7111  *
7112  * Originally Released Under LGPL - original licence link has changed is not relivant.
7113  *
7114  * Fork - LGPL
7115  * <script type="text/javascript">
7116  */
7117  
7118
7119 /**
7120  * @class Roo.ComponentMgr
7121  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7122  * @singleton
7123  */
7124 Roo.ComponentMgr = function(){
7125     var all = new Roo.util.MixedCollection();
7126
7127     return {
7128         /**
7129          * Registers a component.
7130          * @param {Roo.Component} c The component
7131          */
7132         register : function(c){
7133             all.add(c);
7134         },
7135
7136         /**
7137          * Unregisters a component.
7138          * @param {Roo.Component} c The component
7139          */
7140         unregister : function(c){
7141             all.remove(c);
7142         },
7143
7144         /**
7145          * Returns a component by id
7146          * @param {String} id The component id
7147          */
7148         get : function(id){
7149             return all.get(id);
7150         },
7151
7152         /**
7153          * Registers a function that will be called when a specified component is added to ComponentMgr
7154          * @param {String} id The component id
7155          * @param {Funtction} fn The callback function
7156          * @param {Object} scope The scope of the callback
7157          */
7158         onAvailable : function(id, fn, scope){
7159             all.on("add", function(index, o){
7160                 if(o.id == id){
7161                     fn.call(scope || o, o);
7162                     all.un("add", fn, scope);
7163                 }
7164             });
7165         }
7166     };
7167 }();/*
7168  * Based on:
7169  * Ext JS Library 1.1.1
7170  * Copyright(c) 2006-2007, Ext JS, LLC.
7171  *
7172  * Originally Released Under LGPL - original licence link has changed is not relivant.
7173  *
7174  * Fork - LGPL
7175  * <script type="text/javascript">
7176  */
7177  
7178 /**
7179  * @class Roo.Component
7180  * @extends Roo.util.Observable
7181  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7182  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7183  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7184  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7185  * All visual components (widgets) that require rendering into a layout should subclass Component.
7186  * @constructor
7187  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7188  * 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
7189  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7190  */
7191 Roo.Component = function(config){
7192     config = config || {};
7193     if(config.tagName || config.dom || typeof config == "string"){ // element object
7194         config = {el: config, id: config.id || config};
7195     }
7196     this.initialConfig = config;
7197
7198     Roo.apply(this, config);
7199     this.addEvents({
7200         /**
7201          * @event disable
7202          * Fires after the component is disabled.
7203              * @param {Roo.Component} this
7204              */
7205         disable : true,
7206         /**
7207          * @event enable
7208          * Fires after the component is enabled.
7209              * @param {Roo.Component} this
7210              */
7211         enable : true,
7212         /**
7213          * @event beforeshow
7214          * Fires before the component is shown.  Return false to stop the show.
7215              * @param {Roo.Component} this
7216              */
7217         beforeshow : true,
7218         /**
7219          * @event show
7220          * Fires after the component is shown.
7221              * @param {Roo.Component} this
7222              */
7223         show : true,
7224         /**
7225          * @event beforehide
7226          * Fires before the component is hidden. Return false to stop the hide.
7227              * @param {Roo.Component} this
7228              */
7229         beforehide : true,
7230         /**
7231          * @event hide
7232          * Fires after the component is hidden.
7233              * @param {Roo.Component} this
7234              */
7235         hide : true,
7236         /**
7237          * @event beforerender
7238          * Fires before the component is rendered. Return false to stop the render.
7239              * @param {Roo.Component} this
7240              */
7241         beforerender : true,
7242         /**
7243          * @event render
7244          * Fires after the component is rendered.
7245              * @param {Roo.Component} this
7246              */
7247         render : true,
7248         /**
7249          * @event beforedestroy
7250          * Fires before the component is destroyed. Return false to stop the destroy.
7251              * @param {Roo.Component} this
7252              */
7253         beforedestroy : true,
7254         /**
7255          * @event destroy
7256          * Fires after the component is destroyed.
7257              * @param {Roo.Component} this
7258              */
7259         destroy : true
7260     });
7261     if(!this.id){
7262         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7263     }
7264     Roo.ComponentMgr.register(this);
7265     Roo.Component.superclass.constructor.call(this);
7266     this.initComponent();
7267     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7268         this.render(this.renderTo);
7269         delete this.renderTo;
7270     }
7271 };
7272
7273 // private
7274 Roo.Component.AUTO_ID = 1000;
7275
7276 Roo.extend(Roo.Component, Roo.util.Observable, {
7277     /**
7278      * @property {Boolean} hidden
7279      * true if this component is hidden. Read-only.
7280      */
7281     hidden : false,
7282     /**
7283      * true if this component is disabled. Read-only.
7284      */
7285     disabled : false,
7286     /**
7287      * true if this component has been rendered. Read-only.
7288      */
7289     rendered : false,
7290     
7291     /** @cfg {String} disableClass
7292      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7293      */
7294     disabledClass : "x-item-disabled",
7295         /** @cfg {Boolean} allowDomMove
7296          * Whether the component can move the Dom node when rendering (defaults to true).
7297          */
7298     allowDomMove : true,
7299     /** @cfg {String} hideMode
7300      * How this component should hidden. Supported values are
7301      * "visibility" (css visibility), "offsets" (negative offset position) and
7302      * "display" (css display) - defaults to "display".
7303      */
7304     hideMode: 'display',
7305
7306     // private
7307     ctype : "Roo.Component",
7308
7309     /** @cfg {String} actionMode 
7310      * which property holds the element that used for  hide() / show() / disable() / enable()
7311      * default is 'el' 
7312      */
7313     actionMode : "el",
7314
7315     // private
7316     getActionEl : function(){
7317         return this[this.actionMode];
7318     },
7319
7320     initComponent : Roo.emptyFn,
7321     /**
7322      * If this is a lazy rendering component, render it to its container element.
7323      * @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.
7324      */
7325     render : function(container, position){
7326         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7327             if(!container && this.el){
7328                 this.el = Roo.get(this.el);
7329                 container = this.el.dom.parentNode;
7330                 this.allowDomMove = false;
7331             }
7332             this.container = Roo.get(container);
7333             this.rendered = true;
7334             if(position !== undefined){
7335                 if(typeof position == 'number'){
7336                     position = this.container.dom.childNodes[position];
7337                 }else{
7338                     position = Roo.getDom(position);
7339                 }
7340             }
7341             this.onRender(this.container, position || null);
7342             if(this.cls){
7343                 this.el.addClass(this.cls);
7344                 delete this.cls;
7345             }
7346             if(this.style){
7347                 this.el.applyStyles(this.style);
7348                 delete this.style;
7349             }
7350             this.fireEvent("render", this);
7351             this.afterRender(this.container);
7352             if(this.hidden){
7353                 this.hide();
7354             }
7355             if(this.disabled){
7356                 this.disable();
7357             }
7358         }
7359         return this;
7360     },
7361
7362     // private
7363     // default function is not really useful
7364     onRender : function(ct, position){
7365         if(this.el){
7366             this.el = Roo.get(this.el);
7367             if(this.allowDomMove !== false){
7368                 ct.dom.insertBefore(this.el.dom, position);
7369             }
7370         }
7371     },
7372
7373     // private
7374     getAutoCreate : function(){
7375         var cfg = typeof this.autoCreate == "object" ?
7376                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7377         if(this.id && !cfg.id){
7378             cfg.id = this.id;
7379         }
7380         return cfg;
7381     },
7382
7383     // private
7384     afterRender : Roo.emptyFn,
7385
7386     /**
7387      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7388      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7389      */
7390     destroy : function(){
7391         if(this.fireEvent("beforedestroy", this) !== false){
7392             this.purgeListeners();
7393             this.beforeDestroy();
7394             if(this.rendered){
7395                 this.el.removeAllListeners();
7396                 this.el.remove();
7397                 if(this.actionMode == "container"){
7398                     this.container.remove();
7399                 }
7400             }
7401             this.onDestroy();
7402             Roo.ComponentMgr.unregister(this);
7403             this.fireEvent("destroy", this);
7404         }
7405     },
7406
7407         // private
7408     beforeDestroy : function(){
7409
7410     },
7411
7412         // private
7413         onDestroy : function(){
7414
7415     },
7416
7417     /**
7418      * Returns the underlying {@link Roo.Element}.
7419      * @return {Roo.Element} The element
7420      */
7421     getEl : function(){
7422         return this.el;
7423     },
7424
7425     /**
7426      * Returns the id of this component.
7427      * @return {String}
7428      */
7429     getId : function(){
7430         return this.id;
7431     },
7432
7433     /**
7434      * Try to focus this component.
7435      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7436      * @return {Roo.Component} this
7437      */
7438     focus : function(selectText){
7439         if(this.rendered){
7440             this.el.focus();
7441             if(selectText === true){
7442                 this.el.dom.select();
7443             }
7444         }
7445         return this;
7446     },
7447
7448     // private
7449     blur : function(){
7450         if(this.rendered){
7451             this.el.blur();
7452         }
7453         return this;
7454     },
7455
7456     /**
7457      * Disable this component.
7458      * @return {Roo.Component} this
7459      */
7460     disable : function(){
7461         if(this.rendered){
7462             this.onDisable();
7463         }
7464         this.disabled = true;
7465         this.fireEvent("disable", this);
7466         return this;
7467     },
7468
7469         // private
7470     onDisable : function(){
7471         this.getActionEl().addClass(this.disabledClass);
7472         this.el.dom.disabled = true;
7473     },
7474
7475     /**
7476      * Enable this component.
7477      * @return {Roo.Component} this
7478      */
7479     enable : function(){
7480         if(this.rendered){
7481             this.onEnable();
7482         }
7483         this.disabled = false;
7484         this.fireEvent("enable", this);
7485         return this;
7486     },
7487
7488         // private
7489     onEnable : function(){
7490         this.getActionEl().removeClass(this.disabledClass);
7491         this.el.dom.disabled = false;
7492     },
7493
7494     /**
7495      * Convenience function for setting disabled/enabled by boolean.
7496      * @param {Boolean} disabled
7497      */
7498     setDisabled : function(disabled){
7499         this[disabled ? "disable" : "enable"]();
7500     },
7501
7502     /**
7503      * Show this component.
7504      * @return {Roo.Component} this
7505      */
7506     show: function(){
7507         if(this.fireEvent("beforeshow", this) !== false){
7508             this.hidden = false;
7509             if(this.rendered){
7510                 this.onShow();
7511             }
7512             this.fireEvent("show", this);
7513         }
7514         return this;
7515     },
7516
7517     // private
7518     onShow : function(){
7519         var ae = this.getActionEl();
7520         if(this.hideMode == 'visibility'){
7521             ae.dom.style.visibility = "visible";
7522         }else if(this.hideMode == 'offsets'){
7523             ae.removeClass('x-hidden');
7524         }else{
7525             ae.dom.style.display = "";
7526         }
7527     },
7528
7529     /**
7530      * Hide this component.
7531      * @return {Roo.Component} this
7532      */
7533     hide: function(){
7534         if(this.fireEvent("beforehide", this) !== false){
7535             this.hidden = true;
7536             if(this.rendered){
7537                 this.onHide();
7538             }
7539             this.fireEvent("hide", this);
7540         }
7541         return this;
7542     },
7543
7544     // private
7545     onHide : function(){
7546         var ae = this.getActionEl();
7547         if(this.hideMode == 'visibility'){
7548             ae.dom.style.visibility = "hidden";
7549         }else if(this.hideMode == 'offsets'){
7550             ae.addClass('x-hidden');
7551         }else{
7552             ae.dom.style.display = "none";
7553         }
7554     },
7555
7556     /**
7557      * Convenience function to hide or show this component by boolean.
7558      * @param {Boolean} visible True to show, false to hide
7559      * @return {Roo.Component} this
7560      */
7561     setVisible: function(visible){
7562         if(visible) {
7563             this.show();
7564         }else{
7565             this.hide();
7566         }
7567         return this;
7568     },
7569
7570     /**
7571      * Returns true if this component is visible.
7572      */
7573     isVisible : function(){
7574         return this.getActionEl().isVisible();
7575     },
7576
7577     cloneConfig : function(overrides){
7578         overrides = overrides || {};
7579         var id = overrides.id || Roo.id();
7580         var cfg = Roo.applyIf(overrides, this.initialConfig);
7581         cfg.id = id; // prevent dup id
7582         return new this.constructor(cfg);
7583     }
7584 });/*
7585  * Based on:
7586  * Ext JS Library 1.1.1
7587  * Copyright(c) 2006-2007, Ext JS, LLC.
7588  *
7589  * Originally Released Under LGPL - original licence link has changed is not relivant.
7590  *
7591  * Fork - LGPL
7592  * <script type="text/javascript">
7593  */
7594  (function(){ 
7595 /**
7596  * @class Roo.Layer
7597  * @extends Roo.Element
7598  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7599  * automatic maintaining of shadow/shim positions.
7600  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7601  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7602  * you can pass a string with a CSS class name. False turns off the shadow.
7603  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7604  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7605  * @cfg {String} cls CSS class to add to the element
7606  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7607  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7608  * @constructor
7609  * @param {Object} config An object with config options.
7610  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7611  */
7612
7613 Roo.Layer = function(config, existingEl){
7614     config = config || {};
7615     var dh = Roo.DomHelper;
7616     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7617     if(existingEl){
7618         this.dom = Roo.getDom(existingEl);
7619     }
7620     if(!this.dom){
7621         var o = config.dh || {tag: "div", cls: "x-layer"};
7622         this.dom = dh.append(pel, o);
7623     }
7624     if(config.cls){
7625         this.addClass(config.cls);
7626     }
7627     this.constrain = config.constrain !== false;
7628     this.visibilityMode = Roo.Element.VISIBILITY;
7629     if(config.id){
7630         this.id = this.dom.id = config.id;
7631     }else{
7632         this.id = Roo.id(this.dom);
7633     }
7634     this.zindex = config.zindex || this.getZIndex();
7635     this.position("absolute", this.zindex);
7636     if(config.shadow){
7637         this.shadowOffset = config.shadowOffset || 4;
7638         this.shadow = new Roo.Shadow({
7639             offset : this.shadowOffset,
7640             mode : config.shadow
7641         });
7642     }else{
7643         this.shadowOffset = 0;
7644     }
7645     this.useShim = config.shim !== false && Roo.useShims;
7646     this.useDisplay = config.useDisplay;
7647     this.hide();
7648 };
7649
7650 var supr = Roo.Element.prototype;
7651
7652 // shims are shared among layer to keep from having 100 iframes
7653 var shims = [];
7654
7655 Roo.extend(Roo.Layer, Roo.Element, {
7656
7657     getZIndex : function(){
7658         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7659     },
7660
7661     getShim : function(){
7662         if(!this.useShim){
7663             return null;
7664         }
7665         if(this.shim){
7666             return this.shim;
7667         }
7668         var shim = shims.shift();
7669         if(!shim){
7670             shim = this.createShim();
7671             shim.enableDisplayMode('block');
7672             shim.dom.style.display = 'none';
7673             shim.dom.style.visibility = 'visible';
7674         }
7675         var pn = this.dom.parentNode;
7676         if(shim.dom.parentNode != pn){
7677             pn.insertBefore(shim.dom, this.dom);
7678         }
7679         shim.setStyle('z-index', this.getZIndex()-2);
7680         this.shim = shim;
7681         return shim;
7682     },
7683
7684     hideShim : function(){
7685         if(this.shim){
7686             this.shim.setDisplayed(false);
7687             shims.push(this.shim);
7688             delete this.shim;
7689         }
7690     },
7691
7692     disableShadow : function(){
7693         if(this.shadow){
7694             this.shadowDisabled = true;
7695             this.shadow.hide();
7696             this.lastShadowOffset = this.shadowOffset;
7697             this.shadowOffset = 0;
7698         }
7699     },
7700
7701     enableShadow : function(show){
7702         if(this.shadow){
7703             this.shadowDisabled = false;
7704             this.shadowOffset = this.lastShadowOffset;
7705             delete this.lastShadowOffset;
7706             if(show){
7707                 this.sync(true);
7708             }
7709         }
7710     },
7711
7712     // private
7713     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7714     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7715     sync : function(doShow){
7716         var sw = this.shadow;
7717         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7718             var sh = this.getShim();
7719
7720             var w = this.getWidth(),
7721                 h = this.getHeight();
7722
7723             var l = this.getLeft(true),
7724                 t = this.getTop(true);
7725
7726             if(sw && !this.shadowDisabled){
7727                 if(doShow && !sw.isVisible()){
7728                     sw.show(this);
7729                 }else{
7730                     sw.realign(l, t, w, h);
7731                 }
7732                 if(sh){
7733                     if(doShow){
7734                        sh.show();
7735                     }
7736                     // fit the shim behind the shadow, so it is shimmed too
7737                     var a = sw.adjusts, s = sh.dom.style;
7738                     s.left = (Math.min(l, l+a.l))+"px";
7739                     s.top = (Math.min(t, t+a.t))+"px";
7740                     s.width = (w+a.w)+"px";
7741                     s.height = (h+a.h)+"px";
7742                 }
7743             }else if(sh){
7744                 if(doShow){
7745                    sh.show();
7746                 }
7747                 sh.setSize(w, h);
7748                 sh.setLeftTop(l, t);
7749             }
7750             
7751         }
7752     },
7753
7754     // private
7755     destroy : function(){
7756         this.hideShim();
7757         if(this.shadow){
7758             this.shadow.hide();
7759         }
7760         this.removeAllListeners();
7761         var pn = this.dom.parentNode;
7762         if(pn){
7763             pn.removeChild(this.dom);
7764         }
7765         Roo.Element.uncache(this.id);
7766     },
7767
7768     remove : function(){
7769         this.destroy();
7770     },
7771
7772     // private
7773     beginUpdate : function(){
7774         this.updating = true;
7775     },
7776
7777     // private
7778     endUpdate : function(){
7779         this.updating = false;
7780         this.sync(true);
7781     },
7782
7783     // private
7784     hideUnders : function(negOffset){
7785         if(this.shadow){
7786             this.shadow.hide();
7787         }
7788         this.hideShim();
7789     },
7790
7791     // private
7792     constrainXY : function(){
7793         if(this.constrain){
7794             var vw = Roo.lib.Dom.getViewWidth(),
7795                 vh = Roo.lib.Dom.getViewHeight();
7796             var s = Roo.get(document).getScroll();
7797
7798             var xy = this.getXY();
7799             var x = xy[0], y = xy[1];   
7800             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7801             // only move it if it needs it
7802             var moved = false;
7803             // first validate right/bottom
7804             if((x + w) > vw+s.left){
7805                 x = vw - w - this.shadowOffset;
7806                 moved = true;
7807             }
7808             if((y + h) > vh+s.top){
7809                 y = vh - h - this.shadowOffset;
7810                 moved = true;
7811             }
7812             // then make sure top/left isn't negative
7813             if(x < s.left){
7814                 x = s.left;
7815                 moved = true;
7816             }
7817             if(y < s.top){
7818                 y = s.top;
7819                 moved = true;
7820             }
7821             if(moved){
7822                 if(this.avoidY){
7823                     var ay = this.avoidY;
7824                     if(y <= ay && (y+h) >= ay){
7825                         y = ay-h-5;   
7826                     }
7827                 }
7828                 xy = [x, y];
7829                 this.storeXY(xy);
7830                 supr.setXY.call(this, xy);
7831                 this.sync();
7832             }
7833         }
7834     },
7835
7836     isVisible : function(){
7837         return this.visible;    
7838     },
7839
7840     // private
7841     showAction : function(){
7842         this.visible = true; // track visibility to prevent getStyle calls
7843         if(this.useDisplay === true){
7844             this.setDisplayed("");
7845         }else if(this.lastXY){
7846             supr.setXY.call(this, this.lastXY);
7847         }else if(this.lastLT){
7848             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7849         }
7850     },
7851
7852     // private
7853     hideAction : function(){
7854         this.visible = false;
7855         if(this.useDisplay === true){
7856             this.setDisplayed(false);
7857         }else{
7858             this.setLeftTop(-10000,-10000);
7859         }
7860     },
7861
7862     // overridden Element method
7863     setVisible : function(v, a, d, c, e){
7864         if(v){
7865             this.showAction();
7866         }
7867         if(a && v){
7868             var cb = function(){
7869                 this.sync(true);
7870                 if(c){
7871                     c();
7872                 }
7873             }.createDelegate(this);
7874             supr.setVisible.call(this, true, true, d, cb, e);
7875         }else{
7876             if(!v){
7877                 this.hideUnders(true);
7878             }
7879             var cb = c;
7880             if(a){
7881                 cb = function(){
7882                     this.hideAction();
7883                     if(c){
7884                         c();
7885                     }
7886                 }.createDelegate(this);
7887             }
7888             supr.setVisible.call(this, v, a, d, cb, e);
7889             if(v){
7890                 this.sync(true);
7891             }else if(!a){
7892                 this.hideAction();
7893             }
7894         }
7895     },
7896
7897     storeXY : function(xy){
7898         delete this.lastLT;
7899         this.lastXY = xy;
7900     },
7901
7902     storeLeftTop : function(left, top){
7903         delete this.lastXY;
7904         this.lastLT = [left, top];
7905     },
7906
7907     // private
7908     beforeFx : function(){
7909         this.beforeAction();
7910         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7911     },
7912
7913     // private
7914     afterFx : function(){
7915         Roo.Layer.superclass.afterFx.apply(this, arguments);
7916         this.sync(this.isVisible());
7917     },
7918
7919     // private
7920     beforeAction : function(){
7921         if(!this.updating && this.shadow){
7922             this.shadow.hide();
7923         }
7924     },
7925
7926     // overridden Element method
7927     setLeft : function(left){
7928         this.storeLeftTop(left, this.getTop(true));
7929         supr.setLeft.apply(this, arguments);
7930         this.sync();
7931     },
7932
7933     setTop : function(top){
7934         this.storeLeftTop(this.getLeft(true), top);
7935         supr.setTop.apply(this, arguments);
7936         this.sync();
7937     },
7938
7939     setLeftTop : function(left, top){
7940         this.storeLeftTop(left, top);
7941         supr.setLeftTop.apply(this, arguments);
7942         this.sync();
7943     },
7944
7945     setXY : function(xy, a, d, c, e){
7946         this.fixDisplay();
7947         this.beforeAction();
7948         this.storeXY(xy);
7949         var cb = this.createCB(c);
7950         supr.setXY.call(this, xy, a, d, cb, e);
7951         if(!a){
7952             cb();
7953         }
7954     },
7955
7956     // private
7957     createCB : function(c){
7958         var el = this;
7959         return function(){
7960             el.constrainXY();
7961             el.sync(true);
7962             if(c){
7963                 c();
7964             }
7965         };
7966     },
7967
7968     // overridden Element method
7969     setX : function(x, a, d, c, e){
7970         this.setXY([x, this.getY()], a, d, c, e);
7971     },
7972
7973     // overridden Element method
7974     setY : function(y, a, d, c, e){
7975         this.setXY([this.getX(), y], a, d, c, e);
7976     },
7977
7978     // overridden Element method
7979     setSize : function(w, h, a, d, c, e){
7980         this.beforeAction();
7981         var cb = this.createCB(c);
7982         supr.setSize.call(this, w, h, a, d, cb, e);
7983         if(!a){
7984             cb();
7985         }
7986     },
7987
7988     // overridden Element method
7989     setWidth : function(w, a, d, c, e){
7990         this.beforeAction();
7991         var cb = this.createCB(c);
7992         supr.setWidth.call(this, w, a, d, cb, e);
7993         if(!a){
7994             cb();
7995         }
7996     },
7997
7998     // overridden Element method
7999     setHeight : function(h, a, d, c, e){
8000         this.beforeAction();
8001         var cb = this.createCB(c);
8002         supr.setHeight.call(this, h, a, d, cb, e);
8003         if(!a){
8004             cb();
8005         }
8006     },
8007
8008     // overridden Element method
8009     setBounds : function(x, y, w, h, a, d, c, e){
8010         this.beforeAction();
8011         var cb = this.createCB(c);
8012         if(!a){
8013             this.storeXY([x, y]);
8014             supr.setXY.call(this, [x, y]);
8015             supr.setSize.call(this, w, h, a, d, cb, e);
8016             cb();
8017         }else{
8018             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8019         }
8020         return this;
8021     },
8022     
8023     /**
8024      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8025      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8026      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8027      * @param {Number} zindex The new z-index to set
8028      * @return {this} The Layer
8029      */
8030     setZIndex : function(zindex){
8031         this.zindex = zindex;
8032         this.setStyle("z-index", zindex + 2);
8033         if(this.shadow){
8034             this.shadow.setZIndex(zindex + 1);
8035         }
8036         if(this.shim){
8037             this.shim.setStyle("z-index", zindex);
8038         }
8039     }
8040 });
8041 })();/*
8042  * Based on:
8043  * Ext JS Library 1.1.1
8044  * Copyright(c) 2006-2007, Ext JS, LLC.
8045  *
8046  * Originally Released Under LGPL - original licence link has changed is not relivant.
8047  *
8048  * Fork - LGPL
8049  * <script type="text/javascript">
8050  */
8051
8052
8053 /**
8054  * @class Roo.Shadow
8055  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8056  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8057  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8058  * @constructor
8059  * Create a new Shadow
8060  * @param {Object} config The config object
8061  */
8062 Roo.Shadow = function(config){
8063     Roo.apply(this, config);
8064     if(typeof this.mode != "string"){
8065         this.mode = this.defaultMode;
8066     }
8067     var o = this.offset, a = {h: 0};
8068     var rad = Math.floor(this.offset/2);
8069     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8070         case "drop":
8071             a.w = 0;
8072             a.l = a.t = o;
8073             a.t -= 1;
8074             if(Roo.isIE){
8075                 a.l -= this.offset + rad;
8076                 a.t -= this.offset + rad;
8077                 a.w -= rad;
8078                 a.h -= rad;
8079                 a.t += 1;
8080             }
8081         break;
8082         case "sides":
8083             a.w = (o*2);
8084             a.l = -o;
8085             a.t = o-1;
8086             if(Roo.isIE){
8087                 a.l -= (this.offset - rad);
8088                 a.t -= this.offset + rad;
8089                 a.l += 1;
8090                 a.w -= (this.offset - rad)*2;
8091                 a.w -= rad + 1;
8092                 a.h -= 1;
8093             }
8094         break;
8095         case "frame":
8096             a.w = a.h = (o*2);
8097             a.l = a.t = -o;
8098             a.t += 1;
8099             a.h -= 2;
8100             if(Roo.isIE){
8101                 a.l -= (this.offset - rad);
8102                 a.t -= (this.offset - rad);
8103                 a.l += 1;
8104                 a.w -= (this.offset + rad + 1);
8105                 a.h -= (this.offset + rad);
8106                 a.h += 1;
8107             }
8108         break;
8109     };
8110
8111     this.adjusts = a;
8112 };
8113
8114 Roo.Shadow.prototype = {
8115     /**
8116      * @cfg {String} mode
8117      * The shadow display mode.  Supports the following options:<br />
8118      * sides: Shadow displays on both sides and bottom only<br />
8119      * frame: Shadow displays equally on all four sides<br />
8120      * drop: Traditional bottom-right drop shadow (default)
8121      */
8122     /**
8123      * @cfg {String} offset
8124      * The number of pixels to offset the shadow from the element (defaults to 4)
8125      */
8126     offset: 4,
8127
8128     // private
8129     defaultMode: "drop",
8130
8131     /**
8132      * Displays the shadow under the target element
8133      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8134      */
8135     show : function(target){
8136         target = Roo.get(target);
8137         if(!this.el){
8138             this.el = Roo.Shadow.Pool.pull();
8139             if(this.el.dom.nextSibling != target.dom){
8140                 this.el.insertBefore(target);
8141             }
8142         }
8143         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8144         if(Roo.isIE){
8145             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8146         }
8147         this.realign(
8148             target.getLeft(true),
8149             target.getTop(true),
8150             target.getWidth(),
8151             target.getHeight()
8152         );
8153         this.el.dom.style.display = "block";
8154     },
8155
8156     /**
8157      * Returns true if the shadow is visible, else false
8158      */
8159     isVisible : function(){
8160         return this.el ? true : false;  
8161     },
8162
8163     /**
8164      * Direct alignment when values are already available. Show must be called at least once before
8165      * calling this method to ensure it is initialized.
8166      * @param {Number} left The target element left position
8167      * @param {Number} top The target element top position
8168      * @param {Number} width The target element width
8169      * @param {Number} height The target element height
8170      */
8171     realign : function(l, t, w, h){
8172         if(!this.el){
8173             return;
8174         }
8175         var a = this.adjusts, d = this.el.dom, s = d.style;
8176         var iea = 0;
8177         s.left = (l+a.l)+"px";
8178         s.top = (t+a.t)+"px";
8179         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8180         if(s.width != sws || s.height != shs){
8181             s.width = sws;
8182             s.height = shs;
8183             if(!Roo.isIE){
8184                 var cn = d.childNodes;
8185                 var sww = Math.max(0, (sw-12))+"px";
8186                 cn[0].childNodes[1].style.width = sww;
8187                 cn[1].childNodes[1].style.width = sww;
8188                 cn[2].childNodes[1].style.width = sww;
8189                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8190             }
8191         }
8192     },
8193
8194     /**
8195      * Hides this shadow
8196      */
8197     hide : function(){
8198         if(this.el){
8199             this.el.dom.style.display = "none";
8200             Roo.Shadow.Pool.push(this.el);
8201             delete this.el;
8202         }
8203     },
8204
8205     /**
8206      * Adjust the z-index of this shadow
8207      * @param {Number} zindex The new z-index
8208      */
8209     setZIndex : function(z){
8210         this.zIndex = z;
8211         if(this.el){
8212             this.el.setStyle("z-index", z);
8213         }
8214     }
8215 };
8216
8217 // Private utility class that manages the internal Shadow cache
8218 Roo.Shadow.Pool = function(){
8219     var p = [];
8220     var markup = Roo.isIE ?
8221                  '<div class="x-ie-shadow"></div>' :
8222                  '<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>';
8223     return {
8224         pull : function(){
8225             var sh = p.shift();
8226             if(!sh){
8227                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8228                 sh.autoBoxAdjust = false;
8229             }
8230             return sh;
8231         },
8232
8233         push : function(sh){
8234             p.push(sh);
8235         }
8236     };
8237 }();/*
8238  * Based on:
8239  * Ext JS Library 1.1.1
8240  * Copyright(c) 2006-2007, Ext JS, LLC.
8241  *
8242  * Originally Released Under LGPL - original licence link has changed is not relivant.
8243  *
8244  * Fork - LGPL
8245  * <script type="text/javascript">
8246  */
8247
8248 /**
8249  * @class Roo.BoxComponent
8250  * @extends Roo.Component
8251  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8252  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8253  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8254  * layout containers.
8255  * @constructor
8256  * @param {Roo.Element/String/Object} config The configuration options.
8257  */
8258 Roo.BoxComponent = function(config){
8259     Roo.Component.call(this, config);
8260     this.addEvents({
8261         /**
8262          * @event resize
8263          * Fires after the component is resized.
8264              * @param {Roo.Component} this
8265              * @param {Number} adjWidth The box-adjusted width that was set
8266              * @param {Number} adjHeight The box-adjusted height that was set
8267              * @param {Number} rawWidth The width that was originally specified
8268              * @param {Number} rawHeight The height that was originally specified
8269              */
8270         resize : true,
8271         /**
8272          * @event move
8273          * Fires after the component is moved.
8274              * @param {Roo.Component} this
8275              * @param {Number} x The new x position
8276              * @param {Number} y The new y position
8277              */
8278         move : true
8279     });
8280 };
8281
8282 Roo.extend(Roo.BoxComponent, Roo.Component, {
8283     // private, set in afterRender to signify that the component has been rendered
8284     boxReady : false,
8285     // private, used to defer height settings to subclasses
8286     deferHeight: false,
8287     /** @cfg {Number} width
8288      * width (optional) size of component
8289      */
8290      /** @cfg {Number} height
8291      * height (optional) size of component
8292      */
8293      
8294     /**
8295      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8296      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8297      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8298      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8299      * @return {Roo.BoxComponent} this
8300      */
8301     setSize : function(w, h){
8302         // support for standard size objects
8303         if(typeof w == 'object'){
8304             h = w.height;
8305             w = w.width;
8306         }
8307         // not rendered
8308         if(!this.boxReady){
8309             this.width = w;
8310             this.height = h;
8311             return this;
8312         }
8313
8314         // prevent recalcs when not needed
8315         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8316             return this;
8317         }
8318         this.lastSize = {width: w, height: h};
8319
8320         var adj = this.adjustSize(w, h);
8321         var aw = adj.width, ah = adj.height;
8322         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8323             var rz = this.getResizeEl();
8324             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8325                 rz.setSize(aw, ah);
8326             }else if(!this.deferHeight && ah !== undefined){
8327                 rz.setHeight(ah);
8328             }else if(aw !== undefined){
8329                 rz.setWidth(aw);
8330             }
8331             this.onResize(aw, ah, w, h);
8332             this.fireEvent('resize', this, aw, ah, w, h);
8333         }
8334         return this;
8335     },
8336
8337     /**
8338      * Gets the current size of the component's underlying element.
8339      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8340      */
8341     getSize : function(){
8342         return this.el.getSize();
8343     },
8344
8345     /**
8346      * Gets the current XY position of the component's underlying element.
8347      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8348      * @return {Array} The XY position of the element (e.g., [100, 200])
8349      */
8350     getPosition : function(local){
8351         if(local === true){
8352             return [this.el.getLeft(true), this.el.getTop(true)];
8353         }
8354         return this.xy || this.el.getXY();
8355     },
8356
8357     /**
8358      * Gets the current box measurements of the component's underlying element.
8359      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8360      * @returns {Object} box An object in the format {x, y, width, height}
8361      */
8362     getBox : function(local){
8363         var s = this.el.getSize();
8364         if(local){
8365             s.x = this.el.getLeft(true);
8366             s.y = this.el.getTop(true);
8367         }else{
8368             var xy = this.xy || this.el.getXY();
8369             s.x = xy[0];
8370             s.y = xy[1];
8371         }
8372         return s;
8373     },
8374
8375     /**
8376      * Sets the current box measurements of the component's underlying element.
8377      * @param {Object} box An object in the format {x, y, width, height}
8378      * @returns {Roo.BoxComponent} this
8379      */
8380     updateBox : function(box){
8381         this.setSize(box.width, box.height);
8382         this.setPagePosition(box.x, box.y);
8383         return this;
8384     },
8385
8386     // protected
8387     getResizeEl : function(){
8388         return this.resizeEl || this.el;
8389     },
8390
8391     // protected
8392     getPositionEl : function(){
8393         return this.positionEl || this.el;
8394     },
8395
8396     /**
8397      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8398      * This method fires the move event.
8399      * @param {Number} left The new left
8400      * @param {Number} top The new top
8401      * @returns {Roo.BoxComponent} this
8402      */
8403     setPosition : function(x, y){
8404         this.x = x;
8405         this.y = y;
8406         if(!this.boxReady){
8407             return this;
8408         }
8409         var adj = this.adjustPosition(x, y);
8410         var ax = adj.x, ay = adj.y;
8411
8412         var el = this.getPositionEl();
8413         if(ax !== undefined || ay !== undefined){
8414             if(ax !== undefined && ay !== undefined){
8415                 el.setLeftTop(ax, ay);
8416             }else if(ax !== undefined){
8417                 el.setLeft(ax);
8418             }else if(ay !== undefined){
8419                 el.setTop(ay);
8420             }
8421             this.onPosition(ax, ay);
8422             this.fireEvent('move', this, ax, ay);
8423         }
8424         return this;
8425     },
8426
8427     /**
8428      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8429      * This method fires the move event.
8430      * @param {Number} x The new x position
8431      * @param {Number} y The new y position
8432      * @returns {Roo.BoxComponent} this
8433      */
8434     setPagePosition : function(x, y){
8435         this.pageX = x;
8436         this.pageY = y;
8437         if(!this.boxReady){
8438             return;
8439         }
8440         if(x === undefined || y === undefined){ // cannot translate undefined points
8441             return;
8442         }
8443         var p = this.el.translatePoints(x, y);
8444         this.setPosition(p.left, p.top);
8445         return this;
8446     },
8447
8448     // private
8449     onRender : function(ct, position){
8450         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8451         if(this.resizeEl){
8452             this.resizeEl = Roo.get(this.resizeEl);
8453         }
8454         if(this.positionEl){
8455             this.positionEl = Roo.get(this.positionEl);
8456         }
8457     },
8458
8459     // private
8460     afterRender : function(){
8461         Roo.BoxComponent.superclass.afterRender.call(this);
8462         this.boxReady = true;
8463         this.setSize(this.width, this.height);
8464         if(this.x || this.y){
8465             this.setPosition(this.x, this.y);
8466         }
8467         if(this.pageX || this.pageY){
8468             this.setPagePosition(this.pageX, this.pageY);
8469         }
8470     },
8471
8472     /**
8473      * Force the component's size to recalculate based on the underlying element's current height and width.
8474      * @returns {Roo.BoxComponent} this
8475      */
8476     syncSize : function(){
8477         delete this.lastSize;
8478         this.setSize(this.el.getWidth(), this.el.getHeight());
8479         return this;
8480     },
8481
8482     /**
8483      * Called after the component is resized, this method is empty by default but can be implemented by any
8484      * subclass that needs to perform custom logic after a resize occurs.
8485      * @param {Number} adjWidth The box-adjusted width that was set
8486      * @param {Number} adjHeight The box-adjusted height that was set
8487      * @param {Number} rawWidth The width that was originally specified
8488      * @param {Number} rawHeight The height that was originally specified
8489      */
8490     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8491
8492     },
8493
8494     /**
8495      * Called after the component is moved, this method is empty by default but can be implemented by any
8496      * subclass that needs to perform custom logic after a move occurs.
8497      * @param {Number} x The new x position
8498      * @param {Number} y The new y position
8499      */
8500     onPosition : function(x, y){
8501
8502     },
8503
8504     // private
8505     adjustSize : function(w, h){
8506         if(this.autoWidth){
8507             w = 'auto';
8508         }
8509         if(this.autoHeight){
8510             h = 'auto';
8511         }
8512         return {width : w, height: h};
8513     },
8514
8515     // private
8516     adjustPosition : function(x, y){
8517         return {x : x, y: y};
8518     }
8519 });/*
8520  * Based on:
8521  * Ext JS Library 1.1.1
8522  * Copyright(c) 2006-2007, Ext JS, LLC.
8523  *
8524  * Originally Released Under LGPL - original licence link has changed is not relivant.
8525  *
8526  * Fork - LGPL
8527  * <script type="text/javascript">
8528  */
8529
8530
8531 /**
8532  * @class Roo.SplitBar
8533  * @extends Roo.util.Observable
8534  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8535  * <br><br>
8536  * Usage:
8537  * <pre><code>
8538 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8539                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8540 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8541 split.minSize = 100;
8542 split.maxSize = 600;
8543 split.animate = true;
8544 split.on('moved', splitterMoved);
8545 </code></pre>
8546  * @constructor
8547  * Create a new SplitBar
8548  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8549  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8550  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8551  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8552                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8553                         position of the SplitBar).
8554  */
8555 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8556     
8557     /** @private */
8558     this.el = Roo.get(dragElement, true);
8559     this.el.dom.unselectable = "on";
8560     /** @private */
8561     this.resizingEl = Roo.get(resizingElement, true);
8562
8563     /**
8564      * @private
8565      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8566      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8567      * @type Number
8568      */
8569     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8570     
8571     /**
8572      * The minimum size of the resizing element. (Defaults to 0)
8573      * @type Number
8574      */
8575     this.minSize = 0;
8576     
8577     /**
8578      * The maximum size of the resizing element. (Defaults to 2000)
8579      * @type Number
8580      */
8581     this.maxSize = 2000;
8582     
8583     /**
8584      * Whether to animate the transition to the new size
8585      * @type Boolean
8586      */
8587     this.animate = false;
8588     
8589     /**
8590      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8591      * @type Boolean
8592      */
8593     this.useShim = false;
8594     
8595     /** @private */
8596     this.shim = null;
8597     
8598     if(!existingProxy){
8599         /** @private */
8600         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8601     }else{
8602         this.proxy = Roo.get(existingProxy).dom;
8603     }
8604     /** @private */
8605     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8606     
8607     /** @private */
8608     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8609     
8610     /** @private */
8611     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8612     
8613     /** @private */
8614     this.dragSpecs = {};
8615     
8616     /**
8617      * @private The adapter to use to positon and resize elements
8618      */
8619     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8620     this.adapter.init(this);
8621     
8622     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8623         /** @private */
8624         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8625         this.el.addClass("x-splitbar-h");
8626     }else{
8627         /** @private */
8628         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8629         this.el.addClass("x-splitbar-v");
8630     }
8631     
8632     this.addEvents({
8633         /**
8634          * @event resize
8635          * Fires when the splitter is moved (alias for {@link #event-moved})
8636          * @param {Roo.SplitBar} this
8637          * @param {Number} newSize the new width or height
8638          */
8639         "resize" : true,
8640         /**
8641          * @event moved
8642          * Fires when the splitter is moved
8643          * @param {Roo.SplitBar} this
8644          * @param {Number} newSize the new width or height
8645          */
8646         "moved" : true,
8647         /**
8648          * @event beforeresize
8649          * Fires before the splitter is dragged
8650          * @param {Roo.SplitBar} this
8651          */
8652         "beforeresize" : true,
8653
8654         "beforeapply" : true
8655     });
8656
8657     Roo.util.Observable.call(this);
8658 };
8659
8660 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8661     onStartProxyDrag : function(x, y){
8662         this.fireEvent("beforeresize", this);
8663         if(!this.overlay){
8664             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8665             o.unselectable();
8666             o.enableDisplayMode("block");
8667             // all splitbars share the same overlay
8668             Roo.SplitBar.prototype.overlay = o;
8669         }
8670         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8671         this.overlay.show();
8672         Roo.get(this.proxy).setDisplayed("block");
8673         var size = this.adapter.getElementSize(this);
8674         this.activeMinSize = this.getMinimumSize();;
8675         this.activeMaxSize = this.getMaximumSize();;
8676         var c1 = size - this.activeMinSize;
8677         var c2 = Math.max(this.activeMaxSize - size, 0);
8678         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8679             this.dd.resetConstraints();
8680             this.dd.setXConstraint(
8681                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8682                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8683             );
8684             this.dd.setYConstraint(0, 0);
8685         }else{
8686             this.dd.resetConstraints();
8687             this.dd.setXConstraint(0, 0);
8688             this.dd.setYConstraint(
8689                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8690                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8691             );
8692          }
8693         this.dragSpecs.startSize = size;
8694         this.dragSpecs.startPoint = [x, y];
8695         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8696     },
8697     
8698     /** 
8699      * @private Called after the drag operation by the DDProxy
8700      */
8701     onEndProxyDrag : function(e){
8702         Roo.get(this.proxy).setDisplayed(false);
8703         var endPoint = Roo.lib.Event.getXY(e);
8704         if(this.overlay){
8705             this.overlay.hide();
8706         }
8707         var newSize;
8708         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8709             newSize = this.dragSpecs.startSize + 
8710                 (this.placement == Roo.SplitBar.LEFT ?
8711                     endPoint[0] - this.dragSpecs.startPoint[0] :
8712                     this.dragSpecs.startPoint[0] - endPoint[0]
8713                 );
8714         }else{
8715             newSize = this.dragSpecs.startSize + 
8716                 (this.placement == Roo.SplitBar.TOP ?
8717                     endPoint[1] - this.dragSpecs.startPoint[1] :
8718                     this.dragSpecs.startPoint[1] - endPoint[1]
8719                 );
8720         }
8721         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8722         if(newSize != this.dragSpecs.startSize){
8723             if(this.fireEvent('beforeapply', this, newSize) !== false){
8724                 this.adapter.setElementSize(this, newSize);
8725                 this.fireEvent("moved", this, newSize);
8726                 this.fireEvent("resize", this, newSize);
8727             }
8728         }
8729     },
8730     
8731     /**
8732      * Get the adapter this SplitBar uses
8733      * @return The adapter object
8734      */
8735     getAdapter : function(){
8736         return this.adapter;
8737     },
8738     
8739     /**
8740      * Set the adapter this SplitBar uses
8741      * @param {Object} adapter A SplitBar adapter object
8742      */
8743     setAdapter : function(adapter){
8744         this.adapter = adapter;
8745         this.adapter.init(this);
8746     },
8747     
8748     /**
8749      * Gets the minimum size for the resizing element
8750      * @return {Number} The minimum size
8751      */
8752     getMinimumSize : function(){
8753         return this.minSize;
8754     },
8755     
8756     /**
8757      * Sets the minimum size for the resizing element
8758      * @param {Number} minSize The minimum size
8759      */
8760     setMinimumSize : function(minSize){
8761         this.minSize = minSize;
8762     },
8763     
8764     /**
8765      * Gets the maximum size for the resizing element
8766      * @return {Number} The maximum size
8767      */
8768     getMaximumSize : function(){
8769         return this.maxSize;
8770     },
8771     
8772     /**
8773      * Sets the maximum size for the resizing element
8774      * @param {Number} maxSize The maximum size
8775      */
8776     setMaximumSize : function(maxSize){
8777         this.maxSize = maxSize;
8778     },
8779     
8780     /**
8781      * Sets the initialize size for the resizing element
8782      * @param {Number} size The initial size
8783      */
8784     setCurrentSize : function(size){
8785         var oldAnimate = this.animate;
8786         this.animate = false;
8787         this.adapter.setElementSize(this, size);
8788         this.animate = oldAnimate;
8789     },
8790     
8791     /**
8792      * Destroy this splitbar. 
8793      * @param {Boolean} removeEl True to remove the element
8794      */
8795     destroy : function(removeEl){
8796         if(this.shim){
8797             this.shim.remove();
8798         }
8799         this.dd.unreg();
8800         this.proxy.parentNode.removeChild(this.proxy);
8801         if(removeEl){
8802             this.el.remove();
8803         }
8804     }
8805 });
8806
8807 /**
8808  * @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.
8809  */
8810 Roo.SplitBar.createProxy = function(dir){
8811     var proxy = new Roo.Element(document.createElement("div"));
8812     proxy.unselectable();
8813     var cls = 'x-splitbar-proxy';
8814     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8815     document.body.appendChild(proxy.dom);
8816     return proxy.dom;
8817 };
8818
8819 /** 
8820  * @class Roo.SplitBar.BasicLayoutAdapter
8821  * Default Adapter. It assumes the splitter and resizing element are not positioned
8822  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8823  */
8824 Roo.SplitBar.BasicLayoutAdapter = function(){
8825 };
8826
8827 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8828     // do nothing for now
8829     init : function(s){
8830     
8831     },
8832     /**
8833      * Called before drag operations to get the current size of the resizing element. 
8834      * @param {Roo.SplitBar} s The SplitBar using this adapter
8835      */
8836      getElementSize : function(s){
8837         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8838             return s.resizingEl.getWidth();
8839         }else{
8840             return s.resizingEl.getHeight();
8841         }
8842     },
8843     
8844     /**
8845      * Called after drag operations to set the size of the resizing element.
8846      * @param {Roo.SplitBar} s The SplitBar using this adapter
8847      * @param {Number} newSize The new size to set
8848      * @param {Function} onComplete A function to be invoked when resizing is complete
8849      */
8850     setElementSize : function(s, newSize, onComplete){
8851         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8852             if(!s.animate){
8853                 s.resizingEl.setWidth(newSize);
8854                 if(onComplete){
8855                     onComplete(s, newSize);
8856                 }
8857             }else{
8858                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8859             }
8860         }else{
8861             
8862             if(!s.animate){
8863                 s.resizingEl.setHeight(newSize);
8864                 if(onComplete){
8865                     onComplete(s, newSize);
8866                 }
8867             }else{
8868                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8869             }
8870         }
8871     }
8872 };
8873
8874 /** 
8875  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8876  * @extends Roo.SplitBar.BasicLayoutAdapter
8877  * Adapter that  moves the splitter element to align with the resized sizing element. 
8878  * Used with an absolute positioned SplitBar.
8879  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8880  * document.body, make sure you assign an id to the body element.
8881  */
8882 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8883     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8884     this.container = Roo.get(container);
8885 };
8886
8887 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8888     init : function(s){
8889         this.basic.init(s);
8890     },
8891     
8892     getElementSize : function(s){
8893         return this.basic.getElementSize(s);
8894     },
8895     
8896     setElementSize : function(s, newSize, onComplete){
8897         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8898     },
8899     
8900     moveSplitter : function(s){
8901         var yes = Roo.SplitBar;
8902         switch(s.placement){
8903             case yes.LEFT:
8904                 s.el.setX(s.resizingEl.getRight());
8905                 break;
8906             case yes.RIGHT:
8907                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8908                 break;
8909             case yes.TOP:
8910                 s.el.setY(s.resizingEl.getBottom());
8911                 break;
8912             case yes.BOTTOM:
8913                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8914                 break;
8915         }
8916     }
8917 };
8918
8919 /**
8920  * Orientation constant - Create a vertical SplitBar
8921  * @static
8922  * @type Number
8923  */
8924 Roo.SplitBar.VERTICAL = 1;
8925
8926 /**
8927  * Orientation constant - Create a horizontal SplitBar
8928  * @static
8929  * @type Number
8930  */
8931 Roo.SplitBar.HORIZONTAL = 2;
8932
8933 /**
8934  * Placement constant - The resizing element is to the left of the splitter element
8935  * @static
8936  * @type Number
8937  */
8938 Roo.SplitBar.LEFT = 1;
8939
8940 /**
8941  * Placement constant - The resizing element is to the right of the splitter element
8942  * @static
8943  * @type Number
8944  */
8945 Roo.SplitBar.RIGHT = 2;
8946
8947 /**
8948  * Placement constant - The resizing element is positioned above the splitter element
8949  * @static
8950  * @type Number
8951  */
8952 Roo.SplitBar.TOP = 3;
8953
8954 /**
8955  * Placement constant - The resizing element is positioned under splitter element
8956  * @static
8957  * @type Number
8958  */
8959 Roo.SplitBar.BOTTOM = 4;
8960 /*
8961  * Based on:
8962  * Ext JS Library 1.1.1
8963  * Copyright(c) 2006-2007, Ext JS, LLC.
8964  *
8965  * Originally Released Under LGPL - original licence link has changed is not relivant.
8966  *
8967  * Fork - LGPL
8968  * <script type="text/javascript">
8969  */
8970
8971 /**
8972  * @class Roo.View
8973  * @extends Roo.util.Observable
8974  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8975  * This class also supports single and multi selection modes. <br>
8976  * Create a data model bound view:
8977  <pre><code>
8978  var store = new Roo.data.Store(...);
8979
8980  var view = new Roo.View({
8981     el : "my-element",
8982     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
8983  
8984     singleSelect: true,
8985     selectedClass: "ydataview-selected",
8986     store: store
8987  });
8988
8989  // listen for node click?
8990  view.on("click", function(vw, index, node, e){
8991  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8992  });
8993
8994  // load XML data
8995  dataModel.load("foobar.xml");
8996  </code></pre>
8997  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8998  * <br><br>
8999  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9000  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9001  * 
9002  * Note: old style constructor is still suported (container, template, config)
9003  * 
9004  * @constructor
9005  * Create a new View
9006  * @param {Object} config The config object
9007  * 
9008  */
9009 Roo.View = function(config, depreciated_tpl, depreciated_config){
9010     
9011     if (typeof(depreciated_tpl) == 'undefined') {
9012         // new way.. - universal constructor.
9013         Roo.apply(this, config);
9014         this.el  = Roo.get(this.el);
9015     } else {
9016         // old format..
9017         this.el  = Roo.get(config);
9018         this.tpl = depreciated_tpl;
9019         Roo.apply(this, depreciated_config);
9020     }
9021      
9022     
9023     if(typeof(this.tpl) == "string"){
9024         this.tpl = new Roo.Template(this.tpl);
9025     } else {
9026         // support xtype ctors..
9027         this.tpl = new Roo.factory(this.tpl, Roo);
9028     }
9029     
9030     
9031     this.tpl.compile();
9032    
9033
9034      
9035     /** @private */
9036     this.addEvents({
9037     /**
9038      * @event beforeclick
9039      * Fires before a click is processed. Returns false to cancel the default action.
9040      * @param {Roo.View} this
9041      * @param {Number} index The index of the target node
9042      * @param {HTMLElement} node The target node
9043      * @param {Roo.EventObject} e The raw event object
9044      */
9045         "beforeclick" : true,
9046     /**
9047      * @event click
9048      * Fires when a template node is clicked.
9049      * @param {Roo.View} this
9050      * @param {Number} index The index of the target node
9051      * @param {HTMLElement} node The target node
9052      * @param {Roo.EventObject} e The raw event object
9053      */
9054         "click" : true,
9055     /**
9056      * @event dblclick
9057      * Fires when a template node is double clicked.
9058      * @param {Roo.View} this
9059      * @param {Number} index The index of the target node
9060      * @param {HTMLElement} node The target node
9061      * @param {Roo.EventObject} e The raw event object
9062      */
9063         "dblclick" : true,
9064     /**
9065      * @event contextmenu
9066      * Fires when a template node is right clicked.
9067      * @param {Roo.View} this
9068      * @param {Number} index The index of the target node
9069      * @param {HTMLElement} node The target node
9070      * @param {Roo.EventObject} e The raw event object
9071      */
9072         "contextmenu" : true,
9073     /**
9074      * @event selectionchange
9075      * Fires when the selected nodes change.
9076      * @param {Roo.View} this
9077      * @param {Array} selections Array of the selected nodes
9078      */
9079         "selectionchange" : true,
9080
9081     /**
9082      * @event beforeselect
9083      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9084      * @param {Roo.View} this
9085      * @param {HTMLElement} node The node to be selected
9086      * @param {Array} selections Array of currently selected nodes
9087      */
9088         "beforeselect" : true
9089     });
9090
9091     this.el.on({
9092         "click": this.onClick,
9093         "dblclick": this.onDblClick,
9094         "contextmenu": this.onContextMenu,
9095         scope:this
9096     });
9097
9098     this.selections = [];
9099     this.nodes = [];
9100     this.cmp = new Roo.CompositeElementLite([]);
9101     if(this.store){
9102         this.store = Roo.factory(this.store, Roo.data);
9103         this.setStore(this.store, true);
9104     }
9105     Roo.View.superclass.constructor.call(this);
9106 };
9107
9108 Roo.extend(Roo.View, Roo.util.Observable, {
9109     
9110      /**
9111      * @cfg {Roo.data.Store} store Data store to load data from.
9112      */
9113     store : false,
9114     
9115     /**
9116      * @cfg {String|Roo.Element} el The container element.
9117      */
9118     el : '',
9119     
9120     /**
9121      * @cfg {String|Roo.Template} tpl The template used by this View 
9122      */
9123     tpl : false,
9124     
9125     /**
9126      * @cfg {String} selectedClass The css class to add to selected nodes
9127      */
9128     selectedClass : "x-view-selected",
9129      /**
9130      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9131      */
9132     emptyText : "",
9133     /**
9134      * @cfg {Boolean} multiSelect Allow multiple selection
9135      */
9136     
9137     multiSelect : false,
9138     /**
9139      * @cfg {Boolean} singleSelect Allow single selection
9140      */
9141     singleSelect:  false,
9142     
9143     /**
9144      * Returns the element this view is bound to.
9145      * @return {Roo.Element}
9146      */
9147     getEl : function(){
9148         return this.el;
9149     },
9150
9151     /**
9152      * Refreshes the view.
9153      */
9154     refresh : function(){
9155         var t = this.tpl;
9156         this.clearSelections();
9157         this.el.update("");
9158         var html = [];
9159         var records = this.store.getRange();
9160         if(records.length < 1){
9161             this.el.update(this.emptyText);
9162             return;
9163         }
9164         for(var i = 0, len = records.length; i < len; i++){
9165             var data = this.prepareData(records[i].data, i, records[i]);
9166             html[html.length] = t.apply(data);
9167         }
9168         this.el.update(html.join(""));
9169         this.nodes = this.el.dom.childNodes;
9170         this.updateIndexes(0);
9171     },
9172
9173     /**
9174      * Function to override to reformat the data that is sent to
9175      * the template for each node.
9176      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9177      * a JSON object for an UpdateManager bound view).
9178      */
9179     prepareData : function(data){
9180         return data;
9181     },
9182
9183     onUpdate : function(ds, record){
9184         this.clearSelections();
9185         var index = this.store.indexOf(record);
9186         var n = this.nodes[index];
9187         this.tpl.insertBefore(n, this.prepareData(record.data));
9188         n.parentNode.removeChild(n);
9189         this.updateIndexes(index, index);
9190     },
9191
9192     onAdd : function(ds, records, index){
9193         this.clearSelections();
9194         if(this.nodes.length == 0){
9195             this.refresh();
9196             return;
9197         }
9198         var n = this.nodes[index];
9199         for(var i = 0, len = records.length; i < len; i++){
9200             var d = this.prepareData(records[i].data);
9201             if(n){
9202                 this.tpl.insertBefore(n, d);
9203             }else{
9204                 this.tpl.append(this.el, d);
9205             }
9206         }
9207         this.updateIndexes(index);
9208     },
9209
9210     onRemove : function(ds, record, index){
9211         this.clearSelections();
9212         this.el.dom.removeChild(this.nodes[index]);
9213         this.updateIndexes(index);
9214     },
9215
9216     /**
9217      * Refresh an individual node.
9218      * @param {Number} index
9219      */
9220     refreshNode : function(index){
9221         this.onUpdate(this.store, this.store.getAt(index));
9222     },
9223
9224     updateIndexes : function(startIndex, endIndex){
9225         var ns = this.nodes;
9226         startIndex = startIndex || 0;
9227         endIndex = endIndex || ns.length - 1;
9228         for(var i = startIndex; i <= endIndex; i++){
9229             ns[i].nodeIndex = i;
9230         }
9231     },
9232
9233     /**
9234      * Changes the data store this view uses and refresh the view.
9235      * @param {Store} store
9236      */
9237     setStore : function(store, initial){
9238         if(!initial && this.store){
9239             this.store.un("datachanged", this.refresh);
9240             this.store.un("add", this.onAdd);
9241             this.store.un("remove", this.onRemove);
9242             this.store.un("update", this.onUpdate);
9243             this.store.un("clear", this.refresh);
9244         }
9245         if(store){
9246           
9247             store.on("datachanged", this.refresh, this);
9248             store.on("add", this.onAdd, this);
9249             store.on("remove", this.onRemove, this);
9250             store.on("update", this.onUpdate, this);
9251             store.on("clear", this.refresh, this);
9252         }
9253         
9254         if(store){
9255             this.refresh();
9256         }
9257     },
9258
9259     /**
9260      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9261      * @param {HTMLElement} node
9262      * @return {HTMLElement} The template node
9263      */
9264     findItemFromChild : function(node){
9265         var el = this.el.dom;
9266         if(!node || node.parentNode == el){
9267                     return node;
9268             }
9269             var p = node.parentNode;
9270             while(p && p != el){
9271             if(p.parentNode == el){
9272                 return p;
9273             }
9274             p = p.parentNode;
9275         }
9276             return null;
9277     },
9278
9279     /** @ignore */
9280     onClick : function(e){
9281         var item = this.findItemFromChild(e.getTarget());
9282         if(item){
9283             var index = this.indexOf(item);
9284             if(this.onItemClick(item, index, e) !== false){
9285                 this.fireEvent("click", this, index, item, e);
9286             }
9287         }else{
9288             this.clearSelections();
9289         }
9290     },
9291
9292     /** @ignore */
9293     onContextMenu : function(e){
9294         var item = this.findItemFromChild(e.getTarget());
9295         if(item){
9296             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9297         }
9298     },
9299
9300     /** @ignore */
9301     onDblClick : function(e){
9302         var item = this.findItemFromChild(e.getTarget());
9303         if(item){
9304             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9305         }
9306     },
9307
9308     onItemClick : function(item, index, e){
9309         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9310             return false;
9311         }
9312         if(this.multiSelect || this.singleSelect){
9313             if(this.multiSelect && e.shiftKey && this.lastSelection){
9314                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9315             }else{
9316                 this.select(item, this.multiSelect && e.ctrlKey);
9317                 this.lastSelection = item;
9318             }
9319             e.preventDefault();
9320         }
9321         return true;
9322     },
9323
9324     /**
9325      * Get the number of selected nodes.
9326      * @return {Number}
9327      */
9328     getSelectionCount : function(){
9329         return this.selections.length;
9330     },
9331
9332     /**
9333      * Get the currently selected nodes.
9334      * @return {Array} An array of HTMLElements
9335      */
9336     getSelectedNodes : function(){
9337         return this.selections;
9338     },
9339
9340     /**
9341      * Get the indexes of the selected nodes.
9342      * @return {Array}
9343      */
9344     getSelectedIndexes : function(){
9345         var indexes = [], s = this.selections;
9346         for(var i = 0, len = s.length; i < len; i++){
9347             indexes.push(s[i].nodeIndex);
9348         }
9349         return indexes;
9350     },
9351
9352     /**
9353      * Clear all selections
9354      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9355      */
9356     clearSelections : function(suppressEvent){
9357         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9358             this.cmp.elements = this.selections;
9359             this.cmp.removeClass(this.selectedClass);
9360             this.selections = [];
9361             if(!suppressEvent){
9362                 this.fireEvent("selectionchange", this, this.selections);
9363             }
9364         }
9365     },
9366
9367     /**
9368      * Returns true if the passed node is selected
9369      * @param {HTMLElement/Number} node The node or node index
9370      * @return {Boolean}
9371      */
9372     isSelected : function(node){
9373         var s = this.selections;
9374         if(s.length < 1){
9375             return false;
9376         }
9377         node = this.getNode(node);
9378         return s.indexOf(node) !== -1;
9379     },
9380
9381     /**
9382      * Selects nodes.
9383      * @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
9384      * @param {Boolean} keepExisting (optional) true to keep existing selections
9385      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9386      */
9387     select : function(nodeInfo, keepExisting, suppressEvent){
9388         if(nodeInfo instanceof Array){
9389             if(!keepExisting){
9390                 this.clearSelections(true);
9391             }
9392             for(var i = 0, len = nodeInfo.length; i < len; i++){
9393                 this.select(nodeInfo[i], true, true);
9394             }
9395         } else{
9396             var node = this.getNode(nodeInfo);
9397             if(node && !this.isSelected(node)){
9398                 if(!keepExisting){
9399                     this.clearSelections(true);
9400                 }
9401                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9402                     Roo.fly(node).addClass(this.selectedClass);
9403                     this.selections.push(node);
9404                     if(!suppressEvent){
9405                         this.fireEvent("selectionchange", this, this.selections);
9406                     }
9407                 }
9408             }
9409         }
9410     },
9411
9412     /**
9413      * Gets a template node.
9414      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9415      * @return {HTMLElement} The node or null if it wasn't found
9416      */
9417     getNode : function(nodeInfo){
9418         if(typeof nodeInfo == "string"){
9419             return document.getElementById(nodeInfo);
9420         }else if(typeof nodeInfo == "number"){
9421             return this.nodes[nodeInfo];
9422         }
9423         return nodeInfo;
9424     },
9425
9426     /**
9427      * Gets a range template nodes.
9428      * @param {Number} startIndex
9429      * @param {Number} endIndex
9430      * @return {Array} An array of nodes
9431      */
9432     getNodes : function(start, end){
9433         var ns = this.nodes;
9434         start = start || 0;
9435         end = typeof end == "undefined" ? ns.length - 1 : end;
9436         var nodes = [];
9437         if(start <= end){
9438             for(var i = start; i <= end; i++){
9439                 nodes.push(ns[i]);
9440             }
9441         } else{
9442             for(var i = start; i >= end; i--){
9443                 nodes.push(ns[i]);
9444             }
9445         }
9446         return nodes;
9447     },
9448
9449     /**
9450      * Finds the index of the passed node
9451      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9452      * @return {Number} The index of the node or -1
9453      */
9454     indexOf : function(node){
9455         node = this.getNode(node);
9456         if(typeof node.nodeIndex == "number"){
9457             return node.nodeIndex;
9458         }
9459         var ns = this.nodes;
9460         for(var i = 0, len = ns.length; i < len; i++){
9461             if(ns[i] == node){
9462                 return i;
9463             }
9464         }
9465         return -1;
9466     }
9467 });
9468 /*
9469  * Based on:
9470  * Ext JS Library 1.1.1
9471  * Copyright(c) 2006-2007, Ext JS, LLC.
9472  *
9473  * Originally Released Under LGPL - original licence link has changed is not relivant.
9474  *
9475  * Fork - LGPL
9476  * <script type="text/javascript">
9477  */
9478
9479 /**
9480  * @class Roo.JsonView
9481  * @extends Roo.View
9482  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9483 <pre><code>
9484 var view = new Roo.JsonView({
9485     container: "my-element",
9486     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9487     multiSelect: true, 
9488     jsonRoot: "data" 
9489 });
9490
9491 // listen for node click?
9492 view.on("click", function(vw, index, node, e){
9493     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9494 });
9495
9496 // direct load of JSON data
9497 view.load("foobar.php");
9498
9499 // Example from my blog list
9500 var tpl = new Roo.Template(
9501     '&lt;div class="entry"&gt;' +
9502     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9503     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9504     "&lt;/div&gt;&lt;hr /&gt;"
9505 );
9506
9507 var moreView = new Roo.JsonView({
9508     container :  "entry-list", 
9509     template : tpl,
9510     jsonRoot: "posts"
9511 });
9512 moreView.on("beforerender", this.sortEntries, this);
9513 moreView.load({
9514     url: "/blog/get-posts.php",
9515     params: "allposts=true",
9516     text: "Loading Blog Entries..."
9517 });
9518 </code></pre>
9519
9520 * Note: old code is supported with arguments : (container, template, config)
9521
9522
9523  * @constructor
9524  * Create a new JsonView
9525  * 
9526  * @param {Object} config The config object
9527  * 
9528  */
9529 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9530     
9531     
9532     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9533
9534     var um = this.el.getUpdateManager();
9535     um.setRenderer(this);
9536     um.on("update", this.onLoad, this);
9537     um.on("failure", this.onLoadException, this);
9538
9539     /**
9540      * @event beforerender
9541      * Fires before rendering of the downloaded JSON data.
9542      * @param {Roo.JsonView} this
9543      * @param {Object} data The JSON data loaded
9544      */
9545     /**
9546      * @event load
9547      * Fires when data is loaded.
9548      * @param {Roo.JsonView} this
9549      * @param {Object} data The JSON data loaded
9550      * @param {Object} response The raw Connect response object
9551      */
9552     /**
9553      * @event loadexception
9554      * Fires when loading fails.
9555      * @param {Roo.JsonView} this
9556      * @param {Object} response The raw Connect response object
9557      */
9558     this.addEvents({
9559         'beforerender' : true,
9560         'load' : true,
9561         'loadexception' : true
9562     });
9563 };
9564 Roo.extend(Roo.JsonView, Roo.View, {
9565     /**
9566      * @type {String} The root property in the loaded JSON object that contains the data
9567      */
9568     jsonRoot : "",
9569
9570     /**
9571      * Refreshes the view.
9572      */
9573     refresh : function(){
9574         this.clearSelections();
9575         this.el.update("");
9576         var html = [];
9577         var o = this.jsonData;
9578         if(o && o.length > 0){
9579             for(var i = 0, len = o.length; i < len; i++){
9580                 var data = this.prepareData(o[i], i, o);
9581                 html[html.length] = this.tpl.apply(data);
9582             }
9583         }else{
9584             html.push(this.emptyText);
9585         }
9586         this.el.update(html.join(""));
9587         this.nodes = this.el.dom.childNodes;
9588         this.updateIndexes(0);
9589     },
9590
9591     /**
9592      * 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.
9593      * @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:
9594      <pre><code>
9595      view.load({
9596          url: "your-url.php",
9597          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9598          callback: yourFunction,
9599          scope: yourObject, //(optional scope)
9600          discardUrl: false,
9601          nocache: false,
9602          text: "Loading...",
9603          timeout: 30,
9604          scripts: false
9605      });
9606      </code></pre>
9607      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9608      * 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.
9609      * @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}
9610      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9611      * @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.
9612      */
9613     load : function(){
9614         var um = this.el.getUpdateManager();
9615         um.update.apply(um, arguments);
9616     },
9617
9618     render : function(el, response){
9619         this.clearSelections();
9620         this.el.update("");
9621         var o;
9622         try{
9623             o = Roo.util.JSON.decode(response.responseText);
9624             if(this.jsonRoot){
9625                 
9626                 o = o[this.jsonRoot];
9627             }
9628         } catch(e){
9629         }
9630         /**
9631          * The current JSON data or null
9632          */
9633         this.jsonData = o;
9634         this.beforeRender();
9635         this.refresh();
9636     },
9637
9638 /**
9639  * Get the number of records in the current JSON dataset
9640  * @return {Number}
9641  */
9642     getCount : function(){
9643         return this.jsonData ? this.jsonData.length : 0;
9644     },
9645
9646 /**
9647  * Returns the JSON object for the specified node(s)
9648  * @param {HTMLElement/Array} node The node or an array of nodes
9649  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9650  * you get the JSON object for the node
9651  */
9652     getNodeData : function(node){
9653         if(node instanceof Array){
9654             var data = [];
9655             for(var i = 0, len = node.length; i < len; i++){
9656                 data.push(this.getNodeData(node[i]));
9657             }
9658             return data;
9659         }
9660         return this.jsonData[this.indexOf(node)] || null;
9661     },
9662
9663     beforeRender : function(){
9664         this.snapshot = this.jsonData;
9665         if(this.sortInfo){
9666             this.sort.apply(this, this.sortInfo);
9667         }
9668         this.fireEvent("beforerender", this, this.jsonData);
9669     },
9670
9671     onLoad : function(el, o){
9672         this.fireEvent("load", this, this.jsonData, o);
9673     },
9674
9675     onLoadException : function(el, o){
9676         this.fireEvent("loadexception", this, o);
9677     },
9678
9679 /**
9680  * Filter the data by a specific property.
9681  * @param {String} property A property on your JSON objects
9682  * @param {String/RegExp} value Either string that the property values
9683  * should start with, or a RegExp to test against the property
9684  */
9685     filter : function(property, value){
9686         if(this.jsonData){
9687             var data = [];
9688             var ss = this.snapshot;
9689             if(typeof value == "string"){
9690                 var vlen = value.length;
9691                 if(vlen == 0){
9692                     this.clearFilter();
9693                     return;
9694                 }
9695                 value = value.toLowerCase();
9696                 for(var i = 0, len = ss.length; i < len; i++){
9697                     var o = ss[i];
9698                     if(o[property].substr(0, vlen).toLowerCase() == value){
9699                         data.push(o);
9700                     }
9701                 }
9702             } else if(value.exec){ // regex?
9703                 for(var i = 0, len = ss.length; i < len; i++){
9704                     var o = ss[i];
9705                     if(value.test(o[property])){
9706                         data.push(o);
9707                     }
9708                 }
9709             } else{
9710                 return;
9711             }
9712             this.jsonData = data;
9713             this.refresh();
9714         }
9715     },
9716
9717 /**
9718  * Filter by a function. The passed function will be called with each
9719  * object in the current dataset. If the function returns true the value is kept,
9720  * otherwise it is filtered.
9721  * @param {Function} fn
9722  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9723  */
9724     filterBy : function(fn, scope){
9725         if(this.jsonData){
9726             var data = [];
9727             var ss = this.snapshot;
9728             for(var i = 0, len = ss.length; i < len; i++){
9729                 var o = ss[i];
9730                 if(fn.call(scope || this, o)){
9731                     data.push(o);
9732                 }
9733             }
9734             this.jsonData = data;
9735             this.refresh();
9736         }
9737     },
9738
9739 /**
9740  * Clears the current filter.
9741  */
9742     clearFilter : function(){
9743         if(this.snapshot && this.jsonData != this.snapshot){
9744             this.jsonData = this.snapshot;
9745             this.refresh();
9746         }
9747     },
9748
9749
9750 /**
9751  * Sorts the data for this view and refreshes it.
9752  * @param {String} property A property on your JSON objects to sort on
9753  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9754  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9755  */
9756     sort : function(property, dir, sortType){
9757         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9758         if(this.jsonData){
9759             var p = property;
9760             var dsc = dir && dir.toLowerCase() == "desc";
9761             var f = function(o1, o2){
9762                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9763                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9764                 ;
9765                 if(v1 < v2){
9766                     return dsc ? +1 : -1;
9767                 } else if(v1 > v2){
9768                     return dsc ? -1 : +1;
9769                 } else{
9770                     return 0;
9771                 }
9772             };
9773             this.jsonData.sort(f);
9774             this.refresh();
9775             if(this.jsonData != this.snapshot){
9776                 this.snapshot.sort(f);
9777             }
9778         }
9779     }
9780 });/*
9781  * Based on:
9782  * Ext JS Library 1.1.1
9783  * Copyright(c) 2006-2007, Ext JS, LLC.
9784  *
9785  * Originally Released Under LGPL - original licence link has changed is not relivant.
9786  *
9787  * Fork - LGPL
9788  * <script type="text/javascript">
9789  */
9790  
9791
9792 /**
9793  * @class Roo.ColorPalette
9794  * @extends Roo.Component
9795  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9796  * Here's an example of typical usage:
9797  * <pre><code>
9798 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9799 cp.render('my-div');
9800
9801 cp.on('select', function(palette, selColor){
9802     // do something with selColor
9803 });
9804 </code></pre>
9805  * @constructor
9806  * Create a new ColorPalette
9807  * @param {Object} config The config object
9808  */
9809 Roo.ColorPalette = function(config){
9810     Roo.ColorPalette.superclass.constructor.call(this, config);
9811     this.addEvents({
9812         /**
9813              * @event select
9814              * Fires when a color is selected
9815              * @param {ColorPalette} this
9816              * @param {String} color The 6-digit color hex code (without the # symbol)
9817              */
9818         select: true
9819     });
9820
9821     if(this.handler){
9822         this.on("select", this.handler, this.scope, true);
9823     }
9824 };
9825 Roo.extend(Roo.ColorPalette, Roo.Component, {
9826     /**
9827      * @cfg {String} itemCls
9828      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9829      */
9830     itemCls : "x-color-palette",
9831     /**
9832      * @cfg {String} value
9833      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9834      * the hex codes are case-sensitive.
9835      */
9836     value : null,
9837     clickEvent:'click',
9838     // private
9839     ctype: "Roo.ColorPalette",
9840
9841     /**
9842      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9843      */
9844     allowReselect : false,
9845
9846     /**
9847      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9848      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9849      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9850      * of colors with the width setting until the box is symmetrical.</p>
9851      * <p>You can override individual colors if needed:</p>
9852      * <pre><code>
9853 var cp = new Roo.ColorPalette();
9854 cp.colors[0] = "FF0000";  // change the first box to red
9855 </code></pre>
9856
9857 Or you can provide a custom array of your own for complete control:
9858 <pre><code>
9859 var cp = new Roo.ColorPalette();
9860 cp.colors = ["000000", "993300", "333300"];
9861 </code></pre>
9862      * @type Array
9863      */
9864     colors : [
9865         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9866         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9867         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9868         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9869         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9870     ],
9871
9872     // private
9873     onRender : function(container, position){
9874         var t = new Roo.MasterTemplate(
9875             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9876         );
9877         var c = this.colors;
9878         for(var i = 0, len = c.length; i < len; i++){
9879             t.add([c[i]]);
9880         }
9881         var el = document.createElement("div");
9882         el.className = this.itemCls;
9883         t.overwrite(el);
9884         container.dom.insertBefore(el, position);
9885         this.el = Roo.get(el);
9886         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9887         if(this.clickEvent != 'click'){
9888             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9889         }
9890     },
9891
9892     // private
9893     afterRender : function(){
9894         Roo.ColorPalette.superclass.afterRender.call(this);
9895         if(this.value){
9896             var s = this.value;
9897             this.value = null;
9898             this.select(s);
9899         }
9900     },
9901
9902     // private
9903     handleClick : function(e, t){
9904         e.preventDefault();
9905         if(!this.disabled){
9906             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9907             this.select(c.toUpperCase());
9908         }
9909     },
9910
9911     /**
9912      * Selects the specified color in the palette (fires the select event)
9913      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9914      */
9915     select : function(color){
9916         color = color.replace("#", "");
9917         if(color != this.value || this.allowReselect){
9918             var el = this.el;
9919             if(this.value){
9920                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9921             }
9922             el.child("a.color-"+color).addClass("x-color-palette-sel");
9923             this.value = color;
9924             this.fireEvent("select", this, color);
9925         }
9926     }
9927 });/*
9928  * Based on:
9929  * Ext JS Library 1.1.1
9930  * Copyright(c) 2006-2007, Ext JS, LLC.
9931  *
9932  * Originally Released Under LGPL - original licence link has changed is not relivant.
9933  *
9934  * Fork - LGPL
9935  * <script type="text/javascript">
9936  */
9937  
9938 /**
9939  * @class Roo.DatePicker
9940  * @extends Roo.Component
9941  * Simple date picker class.
9942  * @constructor
9943  * Create a new DatePicker
9944  * @param {Object} config The config object
9945  */
9946 Roo.DatePicker = function(config){
9947     Roo.DatePicker.superclass.constructor.call(this, config);
9948
9949     this.value = config && config.value ?
9950                  config.value.clearTime() : new Date().clearTime();
9951
9952     this.addEvents({
9953         /**
9954              * @event select
9955              * Fires when a date is selected
9956              * @param {DatePicker} this
9957              * @param {Date} date The selected date
9958              */
9959         select: true
9960     });
9961
9962     if(this.handler){
9963         this.on("select", this.handler,  this.scope || this);
9964     }
9965     // build the disabledDatesRE
9966     if(!this.disabledDatesRE && this.disabledDates){
9967         var dd = this.disabledDates;
9968         var re = "(?:";
9969         for(var i = 0; i < dd.length; i++){
9970             re += dd[i];
9971             if(i != dd.length-1) re += "|";
9972         }
9973         this.disabledDatesRE = new RegExp(re + ")");
9974     }
9975 };
9976
9977 Roo.extend(Roo.DatePicker, Roo.Component, {
9978     /**
9979      * @cfg {String} todayText
9980      * The text to display on the button that selects the current date (defaults to "Today")
9981      */
9982     todayText : "Today",
9983     /**
9984      * @cfg {String} okText
9985      * The text to display on the ok button
9986      */
9987     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
9988     /**
9989      * @cfg {String} cancelText
9990      * The text to display on the cancel button
9991      */
9992     cancelText : "Cancel",
9993     /**
9994      * @cfg {String} todayTip
9995      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
9996      */
9997     todayTip : "{0} (Spacebar)",
9998     /**
9999      * @cfg {Date} minDate
10000      * Minimum allowable date (JavaScript date object, defaults to null)
10001      */
10002     minDate : null,
10003     /**
10004      * @cfg {Date} maxDate
10005      * Maximum allowable date (JavaScript date object, defaults to null)
10006      */
10007     maxDate : null,
10008     /**
10009      * @cfg {String} minText
10010      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10011      */
10012     minText : "This date is before the minimum date",
10013     /**
10014      * @cfg {String} maxText
10015      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10016      */
10017     maxText : "This date is after the maximum date",
10018     /**
10019      * @cfg {String} format
10020      * The default date format string which can be overriden for localization support.  The format must be
10021      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10022      */
10023     format : "m/d/y",
10024     /**
10025      * @cfg {Array} disabledDays
10026      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10027      */
10028     disabledDays : null,
10029     /**
10030      * @cfg {String} disabledDaysText
10031      * The tooltip to display when the date falls on a disabled day (defaults to "")
10032      */
10033     disabledDaysText : "",
10034     /**
10035      * @cfg {RegExp} disabledDatesRE
10036      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10037      */
10038     disabledDatesRE : null,
10039     /**
10040      * @cfg {String} disabledDatesText
10041      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10042      */
10043     disabledDatesText : "",
10044     /**
10045      * @cfg {Boolean} constrainToViewport
10046      * True to constrain the date picker to the viewport (defaults to true)
10047      */
10048     constrainToViewport : true,
10049     /**
10050      * @cfg {Array} monthNames
10051      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10052      */
10053     monthNames : Date.monthNames,
10054     /**
10055      * @cfg {Array} dayNames
10056      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10057      */
10058     dayNames : Date.dayNames,
10059     /**
10060      * @cfg {String} nextText
10061      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10062      */
10063     nextText: 'Next Month (Control+Right)',
10064     /**
10065      * @cfg {String} prevText
10066      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10067      */
10068     prevText: 'Previous Month (Control+Left)',
10069     /**
10070      * @cfg {String} monthYearText
10071      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10072      */
10073     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10074     /**
10075      * @cfg {Number} startDay
10076      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10077      */
10078     startDay : 0,
10079     /**
10080      * @cfg {Bool} showClear
10081      * Show a clear button (usefull for date form elements that can be blank.)
10082      */
10083     
10084     showClear: false,
10085     
10086     /**
10087      * Sets the value of the date field
10088      * @param {Date} value The date to set
10089      */
10090     setValue : function(value){
10091         var old = this.value;
10092         this.value = value.clearTime(true);
10093         if(this.el){
10094             this.update(this.value);
10095         }
10096     },
10097
10098     /**
10099      * Gets the current selected value of the date field
10100      * @return {Date} The selected date
10101      */
10102     getValue : function(){
10103         return this.value;
10104     },
10105
10106     // private
10107     focus : function(){
10108         if(this.el){
10109             this.update(this.activeDate);
10110         }
10111     },
10112
10113     // private
10114     onRender : function(container, position){
10115         var m = [
10116              '<table cellspacing="0">',
10117                 '<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>',
10118                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10119         var dn = this.dayNames;
10120         for(var i = 0; i < 7; i++){
10121             var d = this.startDay+i;
10122             if(d > 6){
10123                 d = d-7;
10124             }
10125             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10126         }
10127         m[m.length] = "</tr></thead><tbody><tr>";
10128         for(var i = 0; i < 42; i++) {
10129             if(i % 7 == 0 && i != 0){
10130                 m[m.length] = "</tr><tr>";
10131             }
10132             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10133         }
10134         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10135             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10136
10137         var el = document.createElement("div");
10138         el.className = "x-date-picker";
10139         el.innerHTML = m.join("");
10140
10141         container.dom.insertBefore(el, position);
10142
10143         this.el = Roo.get(el);
10144         this.eventEl = Roo.get(el.firstChild);
10145
10146         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10147             handler: this.showPrevMonth,
10148             scope: this,
10149             preventDefault:true,
10150             stopDefault:true
10151         });
10152
10153         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10154             handler: this.showNextMonth,
10155             scope: this,
10156             preventDefault:true,
10157             stopDefault:true
10158         });
10159
10160         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10161
10162         this.monthPicker = this.el.down('div.x-date-mp');
10163         this.monthPicker.enableDisplayMode('block');
10164         
10165         var kn = new Roo.KeyNav(this.eventEl, {
10166             "left" : function(e){
10167                 e.ctrlKey ?
10168                     this.showPrevMonth() :
10169                     this.update(this.activeDate.add("d", -1));
10170             },
10171
10172             "right" : function(e){
10173                 e.ctrlKey ?
10174                     this.showNextMonth() :
10175                     this.update(this.activeDate.add("d", 1));
10176             },
10177
10178             "up" : function(e){
10179                 e.ctrlKey ?
10180                     this.showNextYear() :
10181                     this.update(this.activeDate.add("d", -7));
10182             },
10183
10184             "down" : function(e){
10185                 e.ctrlKey ?
10186                     this.showPrevYear() :
10187                     this.update(this.activeDate.add("d", 7));
10188             },
10189
10190             "pageUp" : function(e){
10191                 this.showNextMonth();
10192             },
10193
10194             "pageDown" : function(e){
10195                 this.showPrevMonth();
10196             },
10197
10198             "enter" : function(e){
10199                 e.stopPropagation();
10200                 return true;
10201             },
10202
10203             scope : this
10204         });
10205
10206         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10207
10208         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10209
10210         this.el.unselectable();
10211         
10212         this.cells = this.el.select("table.x-date-inner tbody td");
10213         this.textNodes = this.el.query("table.x-date-inner tbody span");
10214
10215         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10216             text: "&#160;",
10217             tooltip: this.monthYearText
10218         });
10219
10220         this.mbtn.on('click', this.showMonthPicker, this);
10221         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10222
10223
10224         var today = (new Date()).dateFormat(this.format);
10225         
10226         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10227         baseTb.add({
10228             text: String.format(this.todayText, today),
10229             tooltip: String.format(this.todayTip, today),
10230             handler: this.selectToday,
10231             scope: this
10232         });
10233         
10234         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10235             
10236         //});
10237         if (this.showClear) {
10238             
10239             baseTb.add( new Roo.Toolbar.Fill());
10240             baseTb.add({
10241                 text: '&#160;',
10242                 cls: 'x-btn-icon x-btn-clear',
10243                 handler: function() {
10244                     //this.value = '';
10245                     this.fireEvent("select", this, '');
10246                 },
10247                 scope: this
10248             });
10249         }
10250         
10251         
10252         if(Roo.isIE){
10253             this.el.repaint();
10254         }
10255         this.update(this.value);
10256     },
10257
10258     createMonthPicker : function(){
10259         if(!this.monthPicker.dom.firstChild){
10260             var buf = ['<table border="0" cellspacing="0">'];
10261             for(var i = 0; i < 6; i++){
10262                 buf.push(
10263                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10264                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10265                     i == 0 ?
10266                     '<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>' :
10267                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10268                 );
10269             }
10270             buf.push(
10271                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10272                     this.okText,
10273                     '</button><button type="button" class="x-date-mp-cancel">',
10274                     this.cancelText,
10275                     '</button></td></tr>',
10276                 '</table>'
10277             );
10278             this.monthPicker.update(buf.join(''));
10279             this.monthPicker.on('click', this.onMonthClick, this);
10280             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10281
10282             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10283             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10284
10285             this.mpMonths.each(function(m, a, i){
10286                 i += 1;
10287                 if((i%2) == 0){
10288                     m.dom.xmonth = 5 + Math.round(i * .5);
10289                 }else{
10290                     m.dom.xmonth = Math.round((i-1) * .5);
10291                 }
10292             });
10293         }
10294     },
10295
10296     showMonthPicker : function(){
10297         this.createMonthPicker();
10298         var size = this.el.getSize();
10299         this.monthPicker.setSize(size);
10300         this.monthPicker.child('table').setSize(size);
10301
10302         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10303         this.updateMPMonth(this.mpSelMonth);
10304         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10305         this.updateMPYear(this.mpSelYear);
10306
10307         this.monthPicker.slideIn('t', {duration:.2});
10308     },
10309
10310     updateMPYear : function(y){
10311         this.mpyear = y;
10312         var ys = this.mpYears.elements;
10313         for(var i = 1; i <= 10; i++){
10314             var td = ys[i-1], y2;
10315             if((i%2) == 0){
10316                 y2 = y + Math.round(i * .5);
10317                 td.firstChild.innerHTML = y2;
10318                 td.xyear = y2;
10319             }else{
10320                 y2 = y - (5-Math.round(i * .5));
10321                 td.firstChild.innerHTML = y2;
10322                 td.xyear = y2;
10323             }
10324             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10325         }
10326     },
10327
10328     updateMPMonth : function(sm){
10329         this.mpMonths.each(function(m, a, i){
10330             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10331         });
10332     },
10333
10334     selectMPMonth: function(m){
10335         
10336     },
10337
10338     onMonthClick : function(e, t){
10339         e.stopEvent();
10340         var el = new Roo.Element(t), pn;
10341         if(el.is('button.x-date-mp-cancel')){
10342             this.hideMonthPicker();
10343         }
10344         else if(el.is('button.x-date-mp-ok')){
10345             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10346             this.hideMonthPicker();
10347         }
10348         else if(pn = el.up('td.x-date-mp-month', 2)){
10349             this.mpMonths.removeClass('x-date-mp-sel');
10350             pn.addClass('x-date-mp-sel');
10351             this.mpSelMonth = pn.dom.xmonth;
10352         }
10353         else if(pn = el.up('td.x-date-mp-year', 2)){
10354             this.mpYears.removeClass('x-date-mp-sel');
10355             pn.addClass('x-date-mp-sel');
10356             this.mpSelYear = pn.dom.xyear;
10357         }
10358         else if(el.is('a.x-date-mp-prev')){
10359             this.updateMPYear(this.mpyear-10);
10360         }
10361         else if(el.is('a.x-date-mp-next')){
10362             this.updateMPYear(this.mpyear+10);
10363         }
10364     },
10365
10366     onMonthDblClick : function(e, t){
10367         e.stopEvent();
10368         var el = new Roo.Element(t), pn;
10369         if(pn = el.up('td.x-date-mp-month', 2)){
10370             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10371             this.hideMonthPicker();
10372         }
10373         else if(pn = el.up('td.x-date-mp-year', 2)){
10374             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10375             this.hideMonthPicker();
10376         }
10377     },
10378
10379     hideMonthPicker : function(disableAnim){
10380         if(this.monthPicker){
10381             if(disableAnim === true){
10382                 this.monthPicker.hide();
10383             }else{
10384                 this.monthPicker.slideOut('t', {duration:.2});
10385             }
10386         }
10387     },
10388
10389     // private
10390     showPrevMonth : function(e){
10391         this.update(this.activeDate.add("mo", -1));
10392     },
10393
10394     // private
10395     showNextMonth : function(e){
10396         this.update(this.activeDate.add("mo", 1));
10397     },
10398
10399     // private
10400     showPrevYear : function(){
10401         this.update(this.activeDate.add("y", -1));
10402     },
10403
10404     // private
10405     showNextYear : function(){
10406         this.update(this.activeDate.add("y", 1));
10407     },
10408
10409     // private
10410     handleMouseWheel : function(e){
10411         var delta = e.getWheelDelta();
10412         if(delta > 0){
10413             this.showPrevMonth();
10414             e.stopEvent();
10415         } else if(delta < 0){
10416             this.showNextMonth();
10417             e.stopEvent();
10418         }
10419     },
10420
10421     // private
10422     handleDateClick : function(e, t){
10423         e.stopEvent();
10424         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10425             this.setValue(new Date(t.dateValue));
10426             this.fireEvent("select", this, this.value);
10427         }
10428     },
10429
10430     // private
10431     selectToday : function(){
10432         this.setValue(new Date().clearTime());
10433         this.fireEvent("select", this, this.value);
10434     },
10435
10436     // private
10437     update : function(date){
10438         var vd = this.activeDate;
10439         this.activeDate = date;
10440         if(vd && this.el){
10441             var t = date.getTime();
10442             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10443                 this.cells.removeClass("x-date-selected");
10444                 this.cells.each(function(c){
10445                    if(c.dom.firstChild.dateValue == t){
10446                        c.addClass("x-date-selected");
10447                        setTimeout(function(){
10448                             try{c.dom.firstChild.focus();}catch(e){}
10449                        }, 50);
10450                        return false;
10451                    }
10452                 });
10453                 return;
10454             }
10455         }
10456         var days = date.getDaysInMonth();
10457         var firstOfMonth = date.getFirstDateOfMonth();
10458         var startingPos = firstOfMonth.getDay()-this.startDay;
10459
10460         if(startingPos <= this.startDay){
10461             startingPos += 7;
10462         }
10463
10464         var pm = date.add("mo", -1);
10465         var prevStart = pm.getDaysInMonth()-startingPos;
10466
10467         var cells = this.cells.elements;
10468         var textEls = this.textNodes;
10469         days += startingPos;
10470
10471         // convert everything to numbers so it's fast
10472         var day = 86400000;
10473         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10474         var today = new Date().clearTime().getTime();
10475         var sel = date.clearTime().getTime();
10476         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10477         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10478         var ddMatch = this.disabledDatesRE;
10479         var ddText = this.disabledDatesText;
10480         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10481         var ddaysText = this.disabledDaysText;
10482         var format = this.format;
10483
10484         var setCellClass = function(cal, cell){
10485             cell.title = "";
10486             var t = d.getTime();
10487             cell.firstChild.dateValue = t;
10488             if(t == today){
10489                 cell.className += " x-date-today";
10490                 cell.title = cal.todayText;
10491             }
10492             if(t == sel){
10493                 cell.className += " x-date-selected";
10494                 setTimeout(function(){
10495                     try{cell.firstChild.focus();}catch(e){}
10496                 }, 50);
10497             }
10498             // disabling
10499             if(t < min) {
10500                 cell.className = " x-date-disabled";
10501                 cell.title = cal.minText;
10502                 return;
10503             }
10504             if(t > max) {
10505                 cell.className = " x-date-disabled";
10506                 cell.title = cal.maxText;
10507                 return;
10508             }
10509             if(ddays){
10510                 if(ddays.indexOf(d.getDay()) != -1){
10511                     cell.title = ddaysText;
10512                     cell.className = " x-date-disabled";
10513                 }
10514             }
10515             if(ddMatch && format){
10516                 var fvalue = d.dateFormat(format);
10517                 if(ddMatch.test(fvalue)){
10518                     cell.title = ddText.replace("%0", fvalue);
10519                     cell.className = " x-date-disabled";
10520                 }
10521             }
10522         };
10523
10524         var i = 0;
10525         for(; i < startingPos; i++) {
10526             textEls[i].innerHTML = (++prevStart);
10527             d.setDate(d.getDate()+1);
10528             cells[i].className = "x-date-prevday";
10529             setCellClass(this, cells[i]);
10530         }
10531         for(; i < days; i++){
10532             intDay = i - startingPos + 1;
10533             textEls[i].innerHTML = (intDay);
10534             d.setDate(d.getDate()+1);
10535             cells[i].className = "x-date-active";
10536             setCellClass(this, cells[i]);
10537         }
10538         var extraDays = 0;
10539         for(; i < 42; i++) {
10540              textEls[i].innerHTML = (++extraDays);
10541              d.setDate(d.getDate()+1);
10542              cells[i].className = "x-date-nextday";
10543              setCellClass(this, cells[i]);
10544         }
10545
10546         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10547
10548         if(!this.internalRender){
10549             var main = this.el.dom.firstChild;
10550             var w = main.offsetWidth;
10551             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10552             Roo.fly(main).setWidth(w);
10553             this.internalRender = true;
10554             // opera does not respect the auto grow header center column
10555             // then, after it gets a width opera refuses to recalculate
10556             // without a second pass
10557             if(Roo.isOpera && !this.secondPass){
10558                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10559                 this.secondPass = true;
10560                 this.update.defer(10, this, [date]);
10561             }
10562         }
10563     }
10564 });/*
10565  * Based on:
10566  * Ext JS Library 1.1.1
10567  * Copyright(c) 2006-2007, Ext JS, LLC.
10568  *
10569  * Originally Released Under LGPL - original licence link has changed is not relivant.
10570  *
10571  * Fork - LGPL
10572  * <script type="text/javascript">
10573  */
10574 /**
10575  * @class Roo.TabPanel
10576  * @extends Roo.util.Observable
10577  * A lightweight tab container.
10578  * <br><br>
10579  * Usage:
10580  * <pre><code>
10581 // basic tabs 1, built from existing content
10582 var tabs = new Roo.TabPanel("tabs1");
10583 tabs.addTab("script", "View Script");
10584 tabs.addTab("markup", "View Markup");
10585 tabs.activate("script");
10586
10587 // more advanced tabs, built from javascript
10588 var jtabs = new Roo.TabPanel("jtabs");
10589 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10590
10591 // set up the UpdateManager
10592 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10593 var updater = tab2.getUpdateManager();
10594 updater.setDefaultUrl("ajax1.htm");
10595 tab2.on('activate', updater.refresh, updater, true);
10596
10597 // Use setUrl for Ajax loading
10598 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10599 tab3.setUrl("ajax2.htm", null, true);
10600
10601 // Disabled tab
10602 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10603 tab4.disable();
10604
10605 jtabs.activate("jtabs-1");
10606  * </code></pre>
10607  * @constructor
10608  * Create a new TabPanel.
10609  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10610  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10611  */
10612 Roo.TabPanel = function(container, config){
10613     /**
10614     * The container element for this TabPanel.
10615     * @type Roo.Element
10616     */
10617     this.el = Roo.get(container, true);
10618     if(config){
10619         if(typeof config == "boolean"){
10620             this.tabPosition = config ? "bottom" : "top";
10621         }else{
10622             Roo.apply(this, config);
10623         }
10624     }
10625     if(this.tabPosition == "bottom"){
10626         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10627         this.el.addClass("x-tabs-bottom");
10628     }
10629     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10630     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10631     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10632     if(Roo.isIE){
10633         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10634     }
10635     if(this.tabPosition != "bottom"){
10636     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10637      * @type Roo.Element
10638      */
10639       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10640       this.el.addClass("x-tabs-top");
10641     }
10642     this.items = [];
10643
10644     this.bodyEl.setStyle("position", "relative");
10645
10646     this.active = null;
10647     this.activateDelegate = this.activate.createDelegate(this);
10648
10649     this.addEvents({
10650         /**
10651          * @event tabchange
10652          * Fires when the active tab changes
10653          * @param {Roo.TabPanel} this
10654          * @param {Roo.TabPanelItem} activePanel The new active tab
10655          */
10656         "tabchange": true,
10657         /**
10658          * @event beforetabchange
10659          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10660          * @param {Roo.TabPanel} this
10661          * @param {Object} e Set cancel to true on this object to cancel the tab change
10662          * @param {Roo.TabPanelItem} tab The tab being changed to
10663          */
10664         "beforetabchange" : true
10665     });
10666
10667     Roo.EventManager.onWindowResize(this.onResize, this);
10668     this.cpad = this.el.getPadding("lr");
10669     this.hiddenCount = 0;
10670
10671     Roo.TabPanel.superclass.constructor.call(this);
10672 };
10673
10674 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10675         /*
10676          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10677          */
10678     tabPosition : "top",
10679         /*
10680          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10681          */
10682     currentTabWidth : 0,
10683         /*
10684          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10685          */
10686     minTabWidth : 40,
10687         /*
10688          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10689          */
10690     maxTabWidth : 250,
10691         /*
10692          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10693          */
10694     preferredTabWidth : 175,
10695         /*
10696          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10697          */
10698     resizeTabs : false,
10699         /*
10700          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10701          */
10702     monitorResize : true,
10703
10704     /**
10705      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10706      * @param {String} id The id of the div to use <b>or create</b>
10707      * @param {String} text The text for the tab
10708      * @param {String} content (optional) Content to put in the TabPanelItem body
10709      * @param {Boolean} closable (optional) True to create a close icon on the tab
10710      * @return {Roo.TabPanelItem} The created TabPanelItem
10711      */
10712     addTab : function(id, text, content, closable){
10713         var item = new Roo.TabPanelItem(this, id, text, closable);
10714         this.addTabItem(item);
10715         if(content){
10716             item.setContent(content);
10717         }
10718         return item;
10719     },
10720
10721     /**
10722      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10723      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10724      * @return {Roo.TabPanelItem}
10725      */
10726     getTab : function(id){
10727         return this.items[id];
10728     },
10729
10730     /**
10731      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10732      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10733      */
10734     hideTab : function(id){
10735         var t = this.items[id];
10736         if(!t.isHidden()){
10737            t.setHidden(true);
10738            this.hiddenCount++;
10739            this.autoSizeTabs();
10740         }
10741     },
10742
10743     /**
10744      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10745      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10746      */
10747     unhideTab : function(id){
10748         var t = this.items[id];
10749         if(t.isHidden()){
10750            t.setHidden(false);
10751            this.hiddenCount--;
10752            this.autoSizeTabs();
10753         }
10754     },
10755
10756     /**
10757      * Adds an existing {@link Roo.TabPanelItem}.
10758      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10759      */
10760     addTabItem : function(item){
10761         this.items[item.id] = item;
10762         this.items.push(item);
10763         if(this.resizeTabs){
10764            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10765            this.autoSizeTabs();
10766         }else{
10767             item.autoSize();
10768         }
10769     },
10770
10771     /**
10772      * Removes a {@link Roo.TabPanelItem}.
10773      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10774      */
10775     removeTab : function(id){
10776         var items = this.items;
10777         var tab = items[id];
10778         if(!tab) { return; }
10779         var index = items.indexOf(tab);
10780         if(this.active == tab && items.length > 1){
10781             var newTab = this.getNextAvailable(index);
10782             if(newTab) {
10783                 newTab.activate();
10784             }
10785         }
10786         this.stripEl.dom.removeChild(tab.pnode.dom);
10787         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10788             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10789         }
10790         items.splice(index, 1);
10791         delete this.items[tab.id];
10792         tab.fireEvent("close", tab);
10793         tab.purgeListeners();
10794         this.autoSizeTabs();
10795     },
10796
10797     getNextAvailable : function(start){
10798         var items = this.items;
10799         var index = start;
10800         // look for a next tab that will slide over to
10801         // replace the one being removed
10802         while(index < items.length){
10803             var item = items[++index];
10804             if(item && !item.isHidden()){
10805                 return item;
10806             }
10807         }
10808         // if one isn't found select the previous tab (on the left)
10809         index = start;
10810         while(index >= 0){
10811             var item = items[--index];
10812             if(item && !item.isHidden()){
10813                 return item;
10814             }
10815         }
10816         return null;
10817     },
10818
10819     /**
10820      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10821      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10822      */
10823     disableTab : function(id){
10824         var tab = this.items[id];
10825         if(tab && this.active != tab){
10826             tab.disable();
10827         }
10828     },
10829
10830     /**
10831      * Enables a {@link Roo.TabPanelItem} that is disabled.
10832      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10833      */
10834     enableTab : function(id){
10835         var tab = this.items[id];
10836         tab.enable();
10837     },
10838
10839     /**
10840      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10841      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10842      * @return {Roo.TabPanelItem} The TabPanelItem.
10843      */
10844     activate : function(id){
10845         var tab = this.items[id];
10846         if(!tab){
10847             return null;
10848         }
10849         if(tab == this.active || tab.disabled){
10850             return tab;
10851         }
10852         var e = {};
10853         this.fireEvent("beforetabchange", this, e, tab);
10854         if(e.cancel !== true && !tab.disabled){
10855             if(this.active){
10856                 this.active.hide();
10857             }
10858             this.active = this.items[id];
10859             this.active.show();
10860             this.fireEvent("tabchange", this, this.active);
10861         }
10862         return tab;
10863     },
10864
10865     /**
10866      * Gets the active {@link Roo.TabPanelItem}.
10867      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10868      */
10869     getActiveTab : function(){
10870         return this.active;
10871     },
10872
10873     /**
10874      * Updates the tab body element to fit the height of the container element
10875      * for overflow scrolling
10876      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10877      */
10878     syncHeight : function(targetHeight){
10879         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10880         var bm = this.bodyEl.getMargins();
10881         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10882         this.bodyEl.setHeight(newHeight);
10883         return newHeight;
10884     },
10885
10886     onResize : function(){
10887         if(this.monitorResize){
10888             this.autoSizeTabs();
10889         }
10890     },
10891
10892     /**
10893      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10894      */
10895     beginUpdate : function(){
10896         this.updating = true;
10897     },
10898
10899     /**
10900      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10901      */
10902     endUpdate : function(){
10903         this.updating = false;
10904         this.autoSizeTabs();
10905     },
10906
10907     /**
10908      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10909      */
10910     autoSizeTabs : function(){
10911         var count = this.items.length;
10912         var vcount = count - this.hiddenCount;
10913         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10914         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10915         var availWidth = Math.floor(w / vcount);
10916         var b = this.stripBody;
10917         if(b.getWidth() > w){
10918             var tabs = this.items;
10919             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10920             if(availWidth < this.minTabWidth){
10921                 /*if(!this.sleft){    // incomplete scrolling code
10922                     this.createScrollButtons();
10923                 }
10924                 this.showScroll();
10925                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10926             }
10927         }else{
10928             if(this.currentTabWidth < this.preferredTabWidth){
10929                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10930             }
10931         }
10932     },
10933
10934     /**
10935      * Returns the number of tabs in this TabPanel.
10936      * @return {Number}
10937      */
10938      getCount : function(){
10939          return this.items.length;
10940      },
10941
10942     /**
10943      * Resizes all the tabs to the passed width
10944      * @param {Number} The new width
10945      */
10946     setTabWidth : function(width){
10947         this.currentTabWidth = width;
10948         for(var i = 0, len = this.items.length; i < len; i++) {
10949                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10950         }
10951     },
10952
10953     /**
10954      * Destroys this TabPanel
10955      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10956      */
10957     destroy : function(removeEl){
10958         Roo.EventManager.removeResizeListener(this.onResize, this);
10959         for(var i = 0, len = this.items.length; i < len; i++){
10960             this.items[i].purgeListeners();
10961         }
10962         if(removeEl === true){
10963             this.el.update("");
10964             this.el.remove();
10965         }
10966     }
10967 });
10968
10969 /**
10970  * @class Roo.TabPanelItem
10971  * @extends Roo.util.Observable
10972  * Represents an individual item (tab plus body) in a TabPanel.
10973  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
10974  * @param {String} id The id of this TabPanelItem
10975  * @param {String} text The text for the tab of this TabPanelItem
10976  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
10977  */
10978 Roo.TabPanelItem = function(tabPanel, id, text, closable){
10979     /**
10980      * The {@link Roo.TabPanel} this TabPanelItem belongs to
10981      * @type Roo.TabPanel
10982      */
10983     this.tabPanel = tabPanel;
10984     /**
10985      * The id for this TabPanelItem
10986      * @type String
10987      */
10988     this.id = id;
10989     /** @private */
10990     this.disabled = false;
10991     /** @private */
10992     this.text = text;
10993     /** @private */
10994     this.loaded = false;
10995     this.closable = closable;
10996
10997     /**
10998      * The body element for this TabPanelItem.
10999      * @type Roo.Element
11000      */
11001     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11002     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11003     this.bodyEl.setStyle("display", "block");
11004     this.bodyEl.setStyle("zoom", "1");
11005     this.hideAction();
11006
11007     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11008     /** @private */
11009     this.el = Roo.get(els.el, true);
11010     this.inner = Roo.get(els.inner, true);
11011     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11012     this.pnode = Roo.get(els.el.parentNode, true);
11013     this.el.on("mousedown", this.onTabMouseDown, this);
11014     this.el.on("click", this.onTabClick, this);
11015     /** @private */
11016     if(closable){
11017         var c = Roo.get(els.close, true);
11018         c.dom.title = this.closeText;
11019         c.addClassOnOver("close-over");
11020         c.on("click", this.closeClick, this);
11021      }
11022
11023     this.addEvents({
11024          /**
11025          * @event activate
11026          * Fires when this tab becomes the active tab.
11027          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11028          * @param {Roo.TabPanelItem} this
11029          */
11030         "activate": true,
11031         /**
11032          * @event beforeclose
11033          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11034          * @param {Roo.TabPanelItem} this
11035          * @param {Object} e Set cancel to true on this object to cancel the close.
11036          */
11037         "beforeclose": true,
11038         /**
11039          * @event close
11040          * Fires when this tab is closed.
11041          * @param {Roo.TabPanelItem} this
11042          */
11043          "close": true,
11044         /**
11045          * @event deactivate
11046          * Fires when this tab is no longer the active tab.
11047          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11048          * @param {Roo.TabPanelItem} this
11049          */
11050          "deactivate" : true
11051     });
11052     this.hidden = false;
11053
11054     Roo.TabPanelItem.superclass.constructor.call(this);
11055 };
11056
11057 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11058     purgeListeners : function(){
11059        Roo.util.Observable.prototype.purgeListeners.call(this);
11060        this.el.removeAllListeners();
11061     },
11062     /**
11063      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11064      */
11065     show : function(){
11066         this.pnode.addClass("on");
11067         this.showAction();
11068         if(Roo.isOpera){
11069             this.tabPanel.stripWrap.repaint();
11070         }
11071         this.fireEvent("activate", this.tabPanel, this);
11072     },
11073
11074     /**
11075      * Returns true if this tab is the active tab.
11076      * @return {Boolean}
11077      */
11078     isActive : function(){
11079         return this.tabPanel.getActiveTab() == this;
11080     },
11081
11082     /**
11083      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11084      */
11085     hide : function(){
11086         this.pnode.removeClass("on");
11087         this.hideAction();
11088         this.fireEvent("deactivate", this.tabPanel, this);
11089     },
11090
11091     hideAction : function(){
11092         this.bodyEl.hide();
11093         this.bodyEl.setStyle("position", "absolute");
11094         this.bodyEl.setLeft("-20000px");
11095         this.bodyEl.setTop("-20000px");
11096     },
11097
11098     showAction : function(){
11099         this.bodyEl.setStyle("position", "relative");
11100         this.bodyEl.setTop("");
11101         this.bodyEl.setLeft("");
11102         this.bodyEl.show();
11103     },
11104
11105     /**
11106      * Set the tooltip for the tab.
11107      * @param {String} tooltip The tab's tooltip
11108      */
11109     setTooltip : function(text){
11110         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11111             this.textEl.dom.qtip = text;
11112             this.textEl.dom.removeAttribute('title');
11113         }else{
11114             this.textEl.dom.title = text;
11115         }
11116     },
11117
11118     onTabClick : function(e){
11119         e.preventDefault();
11120         this.tabPanel.activate(this.id);
11121     },
11122
11123     onTabMouseDown : function(e){
11124         e.preventDefault();
11125         this.tabPanel.activate(this.id);
11126     },
11127
11128     getWidth : function(){
11129         return this.inner.getWidth();
11130     },
11131
11132     setWidth : function(width){
11133         var iwidth = width - this.pnode.getPadding("lr");
11134         this.inner.setWidth(iwidth);
11135         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11136         this.pnode.setWidth(width);
11137     },
11138
11139     /**
11140      * Show or hide the tab
11141      * @param {Boolean} hidden True to hide or false to show.
11142      */
11143     setHidden : function(hidden){
11144         this.hidden = hidden;
11145         this.pnode.setStyle("display", hidden ? "none" : "");
11146     },
11147
11148     /**
11149      * Returns true if this tab is "hidden"
11150      * @return {Boolean}
11151      */
11152     isHidden : function(){
11153         return this.hidden;
11154     },
11155
11156     /**
11157      * Returns the text for this tab
11158      * @return {String}
11159      */
11160     getText : function(){
11161         return this.text;
11162     },
11163
11164     autoSize : function(){
11165         //this.el.beginMeasure();
11166         this.textEl.setWidth(1);
11167         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11168         //this.el.endMeasure();
11169     },
11170
11171     /**
11172      * Sets the text for the tab (Note: this also sets the tooltip text)
11173      * @param {String} text The tab's text and tooltip
11174      */
11175     setText : function(text){
11176         this.text = text;
11177         this.textEl.update(text);
11178         this.setTooltip(text);
11179         if(!this.tabPanel.resizeTabs){
11180             this.autoSize();
11181         }
11182     },
11183     /**
11184      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11185      */
11186     activate : function(){
11187         this.tabPanel.activate(this.id);
11188     },
11189
11190     /**
11191      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11192      */
11193     disable : function(){
11194         if(this.tabPanel.active != this){
11195             this.disabled = true;
11196             this.pnode.addClass("disabled");
11197         }
11198     },
11199
11200     /**
11201      * Enables this TabPanelItem if it was previously disabled.
11202      */
11203     enable : function(){
11204         this.disabled = false;
11205         this.pnode.removeClass("disabled");
11206     },
11207
11208     /**
11209      * Sets the content for this TabPanelItem.
11210      * @param {String} content The content
11211      * @param {Boolean} loadScripts true to look for and load scripts
11212      */
11213     setContent : function(content, loadScripts){
11214         this.bodyEl.update(content, loadScripts);
11215     },
11216
11217     /**
11218      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11219      * @return {Roo.UpdateManager} The UpdateManager
11220      */
11221     getUpdateManager : function(){
11222         return this.bodyEl.getUpdateManager();
11223     },
11224
11225     /**
11226      * Set a URL to be used to load the content for this TabPanelItem.
11227      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11228      * @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)
11229      * @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)
11230      * @return {Roo.UpdateManager} The UpdateManager
11231      */
11232     setUrl : function(url, params, loadOnce){
11233         if(this.refreshDelegate){
11234             this.un('activate', this.refreshDelegate);
11235         }
11236         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11237         this.on("activate", this.refreshDelegate);
11238         return this.bodyEl.getUpdateManager();
11239     },
11240
11241     /** @private */
11242     _handleRefresh : function(url, params, loadOnce){
11243         if(!loadOnce || !this.loaded){
11244             var updater = this.bodyEl.getUpdateManager();
11245             updater.update(url, params, this._setLoaded.createDelegate(this));
11246         }
11247     },
11248
11249     /**
11250      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11251      *   Will fail silently if the setUrl method has not been called.
11252      *   This does not activate the panel, just updates its content.
11253      */
11254     refresh : function(){
11255         if(this.refreshDelegate){
11256            this.loaded = false;
11257            this.refreshDelegate();
11258         }
11259     },
11260
11261     /** @private */
11262     _setLoaded : function(){
11263         this.loaded = true;
11264     },
11265
11266     /** @private */
11267     closeClick : function(e){
11268         var o = {};
11269         e.stopEvent();
11270         this.fireEvent("beforeclose", this, o);
11271         if(o.cancel !== true){
11272             this.tabPanel.removeTab(this.id);
11273         }
11274     },
11275     /**
11276      * The text displayed in the tooltip for the close icon.
11277      * @type String
11278      */
11279     closeText : "Close this tab"
11280 });
11281
11282 /** @private */
11283 Roo.TabPanel.prototype.createStrip = function(container){
11284     var strip = document.createElement("div");
11285     strip.className = "x-tabs-wrap";
11286     container.appendChild(strip);
11287     return strip;
11288 };
11289 /** @private */
11290 Roo.TabPanel.prototype.createStripList = function(strip){
11291     // div wrapper for retard IE
11292     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>';
11293     return strip.firstChild.firstChild.firstChild.firstChild;
11294 };
11295 /** @private */
11296 Roo.TabPanel.prototype.createBody = function(container){
11297     var body = document.createElement("div");
11298     Roo.id(body, "tab-body");
11299     Roo.fly(body).addClass("x-tabs-body");
11300     container.appendChild(body);
11301     return body;
11302 };
11303 /** @private */
11304 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11305     var body = Roo.getDom(id);
11306     if(!body){
11307         body = document.createElement("div");
11308         body.id = id;
11309     }
11310     Roo.fly(body).addClass("x-tabs-item-body");
11311     bodyEl.insertBefore(body, bodyEl.firstChild);
11312     return body;
11313 };
11314 /** @private */
11315 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11316     var td = document.createElement("td");
11317     stripEl.appendChild(td);
11318     if(closable){
11319         td.className = "x-tabs-closable";
11320         if(!this.closeTpl){
11321             this.closeTpl = new Roo.Template(
11322                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11323                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11324                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11325             );
11326         }
11327         var el = this.closeTpl.overwrite(td, {"text": text});
11328         var close = el.getElementsByTagName("div")[0];
11329         var inner = el.getElementsByTagName("em")[0];
11330         return {"el": el, "close": close, "inner": inner};
11331     } else {
11332         if(!this.tabTpl){
11333             this.tabTpl = new Roo.Template(
11334                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11335                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11336             );
11337         }
11338         var el = this.tabTpl.overwrite(td, {"text": text});
11339         var inner = el.getElementsByTagName("em")[0];
11340         return {"el": el, "inner": inner};
11341     }
11342 };/*
11343  * Based on:
11344  * Ext JS Library 1.1.1
11345  * Copyright(c) 2006-2007, Ext JS, LLC.
11346  *
11347  * Originally Released Under LGPL - original licence link has changed is not relivant.
11348  *
11349  * Fork - LGPL
11350  * <script type="text/javascript">
11351  */
11352
11353 /**
11354  * @class Roo.Button
11355  * @extends Roo.util.Observable
11356  * Simple Button class
11357  * @cfg {String} text The button text
11358  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11359  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11360  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11361  * @cfg {Object} scope The scope of the handler
11362  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11363  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11364  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11365  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11366  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11367  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11368    applies if enableToggle = true)
11369  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11370  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11371   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11372  * @constructor
11373  * Create a new button
11374  * @param {Object} config The config object
11375  */
11376 Roo.Button = function(renderTo, config)
11377 {
11378     if (!config) {
11379         config = renderTo;
11380         renderTo = config.renderTo || false;
11381     }
11382     
11383     Roo.apply(this, config);
11384     this.addEvents({
11385         /**
11386              * @event click
11387              * Fires when this button is clicked
11388              * @param {Button} this
11389              * @param {EventObject} e The click event
11390              */
11391             "click" : true,
11392         /**
11393              * @event toggle
11394              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11395              * @param {Button} this
11396              * @param {Boolean} pressed
11397              */
11398             "toggle" : true,
11399         /**
11400              * @event mouseover
11401              * Fires when the mouse hovers over the button
11402              * @param {Button} this
11403              * @param {Event} e The event object
11404              */
11405         'mouseover' : true,
11406         /**
11407              * @event mouseout
11408              * Fires when the mouse exits the button
11409              * @param {Button} this
11410              * @param {Event} e The event object
11411              */
11412         'mouseout': true,
11413          /**
11414              * @event render
11415              * Fires when the button is rendered
11416              * @param {Button} this
11417              */
11418         'render': true
11419     });
11420     if(this.menu){
11421         this.menu = Roo.menu.MenuMgr.get(this.menu);
11422     }
11423     if(renderTo){
11424         this.render(renderTo);
11425     }
11426     
11427     Roo.util.Observable.call(this);
11428 };
11429
11430 Roo.extend(Roo.Button, Roo.util.Observable, {
11431     /**
11432      * 
11433      */
11434     
11435     /**
11436      * Read-only. True if this button is hidden
11437      * @type Boolean
11438      */
11439     hidden : false,
11440     /**
11441      * Read-only. True if this button is disabled
11442      * @type Boolean
11443      */
11444     disabled : false,
11445     /**
11446      * Read-only. True if this button is pressed (only if enableToggle = true)
11447      * @type Boolean
11448      */
11449     pressed : false,
11450
11451     /**
11452      * @cfg {Number} tabIndex 
11453      * The DOM tabIndex for this button (defaults to undefined)
11454      */
11455     tabIndex : undefined,
11456
11457     /**
11458      * @cfg {Boolean} enableToggle
11459      * True to enable pressed/not pressed toggling (defaults to false)
11460      */
11461     enableToggle: false,
11462     /**
11463      * @cfg {Mixed} menu
11464      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11465      */
11466     menu : undefined,
11467     /**
11468      * @cfg {String} menuAlign
11469      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11470      */
11471     menuAlign : "tl-bl?",
11472
11473     /**
11474      * @cfg {String} iconCls
11475      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11476      */
11477     iconCls : undefined,
11478     /**
11479      * @cfg {String} type
11480      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11481      */
11482     type : 'button',
11483
11484     // private
11485     menuClassTarget: 'tr',
11486
11487     /**
11488      * @cfg {String} clickEvent
11489      * The type of event to map to the button's event handler (defaults to 'click')
11490      */
11491     clickEvent : 'click',
11492
11493     /**
11494      * @cfg {Boolean} handleMouseEvents
11495      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11496      */
11497     handleMouseEvents : true,
11498
11499     /**
11500      * @cfg {String} tooltipType
11501      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11502      */
11503     tooltipType : 'qtip',
11504
11505     /**
11506      * @cfg {String} cls
11507      * A CSS class to apply to the button's main element.
11508      */
11509     
11510     /**
11511      * @cfg {Roo.Template} template (Optional)
11512      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11513      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11514      * require code modifications if required elements (e.g. a button) aren't present.
11515      */
11516
11517     // private
11518     render : function(renderTo){
11519         var btn;
11520         if(this.hideParent){
11521             this.parentEl = Roo.get(renderTo);
11522         }
11523         if(!this.dhconfig){
11524             if(!this.template){
11525                 if(!Roo.Button.buttonTemplate){
11526                     // hideous table template
11527                     Roo.Button.buttonTemplate = new Roo.Template(
11528                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11529                         '<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>',
11530                         "</tr></tbody></table>");
11531                 }
11532                 this.template = Roo.Button.buttonTemplate;
11533             }
11534             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11535             var btnEl = btn.child("button:first");
11536             btnEl.on('focus', this.onFocus, this);
11537             btnEl.on('blur', this.onBlur, this);
11538             if(this.cls){
11539                 btn.addClass(this.cls);
11540             }
11541             if(this.icon){
11542                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11543             }
11544             if(this.iconCls){
11545                 btnEl.addClass(this.iconCls);
11546                 if(!this.cls){
11547                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11548                 }
11549             }
11550             if(this.tabIndex !== undefined){
11551                 btnEl.dom.tabIndex = this.tabIndex;
11552             }
11553             if(this.tooltip){
11554                 if(typeof this.tooltip == 'object'){
11555                     Roo.QuickTips.tips(Roo.apply({
11556                           target: btnEl.id
11557                     }, this.tooltip));
11558                 } else {
11559                     btnEl.dom[this.tooltipType] = this.tooltip;
11560                 }
11561             }
11562         }else{
11563             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11564         }
11565         this.el = btn;
11566         if(this.id){
11567             this.el.dom.id = this.el.id = this.id;
11568         }
11569         if(this.menu){
11570             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11571             this.menu.on("show", this.onMenuShow, this);
11572             this.menu.on("hide", this.onMenuHide, this);
11573         }
11574         btn.addClass("x-btn");
11575         if(Roo.isIE && !Roo.isIE7){
11576             this.autoWidth.defer(1, this);
11577         }else{
11578             this.autoWidth();
11579         }
11580         if(this.handleMouseEvents){
11581             btn.on("mouseover", this.onMouseOver, this);
11582             btn.on("mouseout", this.onMouseOut, this);
11583             btn.on("mousedown", this.onMouseDown, this);
11584         }
11585         btn.on(this.clickEvent, this.onClick, this);
11586         //btn.on("mouseup", this.onMouseUp, this);
11587         if(this.hidden){
11588             this.hide();
11589         }
11590         if(this.disabled){
11591             this.disable();
11592         }
11593         Roo.ButtonToggleMgr.register(this);
11594         if(this.pressed){
11595             this.el.addClass("x-btn-pressed");
11596         }
11597         if(this.repeat){
11598             var repeater = new Roo.util.ClickRepeater(btn,
11599                 typeof this.repeat == "object" ? this.repeat : {}
11600             );
11601             repeater.on("click", this.onClick,  this);
11602         }
11603         this.fireEvent('render', this);
11604         
11605     },
11606     /**
11607      * Returns the button's underlying element
11608      * @return {Roo.Element} The element
11609      */
11610     getEl : function(){
11611         return this.el;  
11612     },
11613     
11614     /**
11615      * Destroys this Button and removes any listeners.
11616      */
11617     destroy : function(){
11618         Roo.ButtonToggleMgr.unregister(this);
11619         this.el.removeAllListeners();
11620         this.purgeListeners();
11621         this.el.remove();
11622     },
11623
11624     // private
11625     autoWidth : function(){
11626         if(this.el){
11627             this.el.setWidth("auto");
11628             if(Roo.isIE7 && Roo.isStrict){
11629                 var ib = this.el.child('button');
11630                 if(ib && ib.getWidth() > 20){
11631                     ib.clip();
11632                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11633                 }
11634             }
11635             if(this.minWidth){
11636                 if(this.hidden){
11637                     this.el.beginMeasure();
11638                 }
11639                 if(this.el.getWidth() < this.minWidth){
11640                     this.el.setWidth(this.minWidth);
11641                 }
11642                 if(this.hidden){
11643                     this.el.endMeasure();
11644                 }
11645             }
11646         }
11647     },
11648
11649     /**
11650      * Assigns this button's click handler
11651      * @param {Function} handler The function to call when the button is clicked
11652      * @param {Object} scope (optional) Scope for the function passed in
11653      */
11654     setHandler : function(handler, scope){
11655         this.handler = handler;
11656         this.scope = scope;  
11657     },
11658     
11659     /**
11660      * Sets this button's text
11661      * @param {String} text The button text
11662      */
11663     setText : function(text){
11664         this.text = text;
11665         if(this.el){
11666             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11667         }
11668         this.autoWidth();
11669     },
11670     
11671     /**
11672      * Gets the text for this button
11673      * @return {String} The button text
11674      */
11675     getText : function(){
11676         return this.text;  
11677     },
11678     
11679     /**
11680      * Show this button
11681      */
11682     show: function(){
11683         this.hidden = false;
11684         if(this.el){
11685             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11686         }
11687     },
11688     
11689     /**
11690      * Hide this button
11691      */
11692     hide: function(){
11693         this.hidden = true;
11694         if(this.el){
11695             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11696         }
11697     },
11698     
11699     /**
11700      * Convenience function for boolean show/hide
11701      * @param {Boolean} visible True to show, false to hide
11702      */
11703     setVisible: function(visible){
11704         if(visible) {
11705             this.show();
11706         }else{
11707             this.hide();
11708         }
11709     },
11710     
11711     /**
11712      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11713      * @param {Boolean} state (optional) Force a particular state
11714      */
11715     toggle : function(state){
11716         state = state === undefined ? !this.pressed : state;
11717         if(state != this.pressed){
11718             if(state){
11719                 this.el.addClass("x-btn-pressed");
11720                 this.pressed = true;
11721                 this.fireEvent("toggle", this, true);
11722             }else{
11723                 this.el.removeClass("x-btn-pressed");
11724                 this.pressed = false;
11725                 this.fireEvent("toggle", this, false);
11726             }
11727             if(this.toggleHandler){
11728                 this.toggleHandler.call(this.scope || this, this, state);
11729             }
11730         }
11731     },
11732     
11733     /**
11734      * Focus the button
11735      */
11736     focus : function(){
11737         this.el.child('button:first').focus();
11738     },
11739     
11740     /**
11741      * Disable this button
11742      */
11743     disable : function(){
11744         if(this.el){
11745             this.el.addClass("x-btn-disabled");
11746         }
11747         this.disabled = true;
11748     },
11749     
11750     /**
11751      * Enable this button
11752      */
11753     enable : function(){
11754         if(this.el){
11755             this.el.removeClass("x-btn-disabled");
11756         }
11757         this.disabled = false;
11758     },
11759
11760     /**
11761      * Convenience function for boolean enable/disable
11762      * @param {Boolean} enabled True to enable, false to disable
11763      */
11764     setDisabled : function(v){
11765         this[v !== true ? "enable" : "disable"]();
11766     },
11767
11768     // private
11769     onClick : function(e){
11770         if(e){
11771             e.preventDefault();
11772         }
11773         if(e.button != 0){
11774             return;
11775         }
11776         if(!this.disabled){
11777             if(this.enableToggle){
11778                 this.toggle();
11779             }
11780             if(this.menu && !this.menu.isVisible()){
11781                 this.menu.show(this.el, this.menuAlign);
11782             }
11783             this.fireEvent("click", this, e);
11784             if(this.handler){
11785                 this.el.removeClass("x-btn-over");
11786                 this.handler.call(this.scope || this, this, e);
11787             }
11788         }
11789     },
11790     // private
11791     onMouseOver : function(e){
11792         if(!this.disabled){
11793             this.el.addClass("x-btn-over");
11794             this.fireEvent('mouseover', this, e);
11795         }
11796     },
11797     // private
11798     onMouseOut : function(e){
11799         if(!e.within(this.el,  true)){
11800             this.el.removeClass("x-btn-over");
11801             this.fireEvent('mouseout', this, e);
11802         }
11803     },
11804     // private
11805     onFocus : function(e){
11806         if(!this.disabled){
11807             this.el.addClass("x-btn-focus");
11808         }
11809     },
11810     // private
11811     onBlur : function(e){
11812         this.el.removeClass("x-btn-focus");
11813     },
11814     // private
11815     onMouseDown : function(e){
11816         if(!this.disabled && e.button == 0){
11817             this.el.addClass("x-btn-click");
11818             Roo.get(document).on('mouseup', this.onMouseUp, this);
11819         }
11820     },
11821     // private
11822     onMouseUp : function(e){
11823         if(e.button == 0){
11824             this.el.removeClass("x-btn-click");
11825             Roo.get(document).un('mouseup', this.onMouseUp, this);
11826         }
11827     },
11828     // private
11829     onMenuShow : function(e){
11830         this.el.addClass("x-btn-menu-active");
11831     },
11832     // private
11833     onMenuHide : function(e){
11834         this.el.removeClass("x-btn-menu-active");
11835     }   
11836 });
11837
11838 // Private utility class used by Button
11839 Roo.ButtonToggleMgr = function(){
11840    var groups = {};
11841    
11842    function toggleGroup(btn, state){
11843        if(state){
11844            var g = groups[btn.toggleGroup];
11845            for(var i = 0, l = g.length; i < l; i++){
11846                if(g[i] != btn){
11847                    g[i].toggle(false);
11848                }
11849            }
11850        }
11851    }
11852    
11853    return {
11854        register : function(btn){
11855            if(!btn.toggleGroup){
11856                return;
11857            }
11858            var g = groups[btn.toggleGroup];
11859            if(!g){
11860                g = groups[btn.toggleGroup] = [];
11861            }
11862            g.push(btn);
11863            btn.on("toggle", toggleGroup);
11864        },
11865        
11866        unregister : function(btn){
11867            if(!btn.toggleGroup){
11868                return;
11869            }
11870            var g = groups[btn.toggleGroup];
11871            if(g){
11872                g.remove(btn);
11873                btn.un("toggle", toggleGroup);
11874            }
11875        }
11876    };
11877 }();/*
11878  * Based on:
11879  * Ext JS Library 1.1.1
11880  * Copyright(c) 2006-2007, Ext JS, LLC.
11881  *
11882  * Originally Released Under LGPL - original licence link has changed is not relivant.
11883  *
11884  * Fork - LGPL
11885  * <script type="text/javascript">
11886  */
11887  
11888 /**
11889  * @class Roo.SplitButton
11890  * @extends Roo.Button
11891  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11892  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11893  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11894  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11895  * @cfg {String} arrowTooltip The title attribute of the arrow
11896  * @constructor
11897  * Create a new menu button
11898  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11899  * @param {Object} config The config object
11900  */
11901 Roo.SplitButton = function(renderTo, config){
11902     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11903     /**
11904      * @event arrowclick
11905      * Fires when this button's arrow is clicked
11906      * @param {SplitButton} this
11907      * @param {EventObject} e The click event
11908      */
11909     this.addEvents({"arrowclick":true});
11910 };
11911
11912 Roo.extend(Roo.SplitButton, Roo.Button, {
11913     render : function(renderTo){
11914         // this is one sweet looking template!
11915         var tpl = new Roo.Template(
11916             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11917             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11918             '<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>',
11919             "</tbody></table></td><td>",
11920             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11921             '<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>',
11922             "</tbody></table></td></tr></table>"
11923         );
11924         var btn = tpl.append(renderTo, [this.text, this.type], true);
11925         var btnEl = btn.child("button");
11926         if(this.cls){
11927             btn.addClass(this.cls);
11928         }
11929         if(this.icon){
11930             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11931         }
11932         if(this.iconCls){
11933             btnEl.addClass(this.iconCls);
11934             if(!this.cls){
11935                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11936             }
11937         }
11938         this.el = btn;
11939         if(this.handleMouseEvents){
11940             btn.on("mouseover", this.onMouseOver, this);
11941             btn.on("mouseout", this.onMouseOut, this);
11942             btn.on("mousedown", this.onMouseDown, this);
11943             btn.on("mouseup", this.onMouseUp, this);
11944         }
11945         btn.on(this.clickEvent, this.onClick, this);
11946         if(this.tooltip){
11947             if(typeof this.tooltip == 'object'){
11948                 Roo.QuickTips.tips(Roo.apply({
11949                       target: btnEl.id
11950                 }, this.tooltip));
11951             } else {
11952                 btnEl.dom[this.tooltipType] = this.tooltip;
11953             }
11954         }
11955         if(this.arrowTooltip){
11956             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11957         }
11958         if(this.hidden){
11959             this.hide();
11960         }
11961         if(this.disabled){
11962             this.disable();
11963         }
11964         if(this.pressed){
11965             this.el.addClass("x-btn-pressed");
11966         }
11967         if(Roo.isIE && !Roo.isIE7){
11968             this.autoWidth.defer(1, this);
11969         }else{
11970             this.autoWidth();
11971         }
11972         if(this.menu){
11973             this.menu.on("show", this.onMenuShow, this);
11974             this.menu.on("hide", this.onMenuHide, this);
11975         }
11976         this.fireEvent('render', this);
11977     },
11978
11979     // private
11980     autoWidth : function(){
11981         if(this.el){
11982             var tbl = this.el.child("table:first");
11983             var tbl2 = this.el.child("table:last");
11984             this.el.setWidth("auto");
11985             tbl.setWidth("auto");
11986             if(Roo.isIE7 && Roo.isStrict){
11987                 var ib = this.el.child('button:first');
11988                 if(ib && ib.getWidth() > 20){
11989                     ib.clip();
11990                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11991                 }
11992             }
11993             if(this.minWidth){
11994                 if(this.hidden){
11995                     this.el.beginMeasure();
11996                 }
11997                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
11998                     tbl.setWidth(this.minWidth-tbl2.getWidth());
11999                 }
12000                 if(this.hidden){
12001                     this.el.endMeasure();
12002                 }
12003             }
12004             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12005         } 
12006     },
12007     /**
12008      * Sets this button's click handler
12009      * @param {Function} handler The function to call when the button is clicked
12010      * @param {Object} scope (optional) Scope for the function passed above
12011      */
12012     setHandler : function(handler, scope){
12013         this.handler = handler;
12014         this.scope = scope;  
12015     },
12016     
12017     /**
12018      * Sets this button's arrow click handler
12019      * @param {Function} handler The function to call when the arrow is clicked
12020      * @param {Object} scope (optional) Scope for the function passed above
12021      */
12022     setArrowHandler : function(handler, scope){
12023         this.arrowHandler = handler;
12024         this.scope = scope;  
12025     },
12026     
12027     /**
12028      * Focus the button
12029      */
12030     focus : function(){
12031         if(this.el){
12032             this.el.child("button:first").focus();
12033         }
12034     },
12035
12036     // private
12037     onClick : function(e){
12038         e.preventDefault();
12039         if(!this.disabled){
12040             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12041                 if(this.menu && !this.menu.isVisible()){
12042                     this.menu.show(this.el, this.menuAlign);
12043                 }
12044                 this.fireEvent("arrowclick", this, e);
12045                 if(this.arrowHandler){
12046                     this.arrowHandler.call(this.scope || this, this, e);
12047                 }
12048             }else{
12049                 this.fireEvent("click", this, e);
12050                 if(this.handler){
12051                     this.handler.call(this.scope || this, this, e);
12052                 }
12053             }
12054         }
12055     },
12056     // private
12057     onMouseDown : function(e){
12058         if(!this.disabled){
12059             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12060         }
12061     },
12062     // private
12063     onMouseUp : function(e){
12064         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12065     }   
12066 });
12067
12068
12069 // backwards compat
12070 Roo.MenuButton = Roo.SplitButton;/*
12071  * Based on:
12072  * Ext JS Library 1.1.1
12073  * Copyright(c) 2006-2007, Ext JS, LLC.
12074  *
12075  * Originally Released Under LGPL - original licence link has changed is not relivant.
12076  *
12077  * Fork - LGPL
12078  * <script type="text/javascript">
12079  */
12080
12081 /**
12082  * @class Roo.Toolbar
12083  * Basic Toolbar class.
12084  * @constructor
12085  * Creates a new Toolbar
12086  * @param {Object} config The config object
12087  */ 
12088 Roo.Toolbar = function(container, buttons, config)
12089 {
12090     /// old consturctor format still supported..
12091     if(container instanceof Array){ // omit the container for later rendering
12092         buttons = container;
12093         config = buttons;
12094         container = null;
12095     }
12096     if (typeof(container) == 'object' && container.xtype) {
12097         config = container;
12098         container = config.container;
12099         buttons = config.buttons; // not really - use items!!
12100     }
12101     var xitems = [];
12102     if (config && config.items) {
12103         xitems = config.items;
12104         delete config.items;
12105     }
12106     Roo.apply(this, config);
12107     this.buttons = buttons;
12108     
12109     if(container){
12110         this.render(container);
12111     }
12112     Roo.each(xitems, function(b) {
12113         this.add(b);
12114     }, this);
12115     
12116 };
12117
12118 Roo.Toolbar.prototype = {
12119     /**
12120      * @cfg {Roo.data.Store} items
12121      * array of button configs or elements to add
12122      */
12123     
12124     /**
12125      * @cfg {String/HTMLElement/Element} container
12126      * The id or element that will contain the toolbar
12127      */
12128     // private
12129     render : function(ct){
12130         this.el = Roo.get(ct);
12131         if(this.cls){
12132             this.el.addClass(this.cls);
12133         }
12134         // using a table allows for vertical alignment
12135         // 100% width is needed by Safari...
12136         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12137         this.tr = this.el.child("tr", true);
12138         var autoId = 0;
12139         this.items = new Roo.util.MixedCollection(false, function(o){
12140             return o.id || ("item" + (++autoId));
12141         });
12142         if(this.buttons){
12143             this.add.apply(this, this.buttons);
12144             delete this.buttons;
12145         }
12146     },
12147
12148     /**
12149      * Adds element(s) to the toolbar -- this function takes a variable number of 
12150      * arguments of mixed type and adds them to the toolbar.
12151      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12152      * <ul>
12153      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12154      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12155      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12156      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12157      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12158      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12159      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12160      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12161      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12162      * </ul>
12163      * @param {Mixed} arg2
12164      * @param {Mixed} etc.
12165      */
12166     add : function(){
12167         var a = arguments, l = a.length;
12168         for(var i = 0; i < l; i++){
12169             this._add(a[i]);
12170         }
12171     },
12172     // private..
12173     _add : function(el) {
12174         
12175         if (el.xtype) {
12176             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12177         }
12178         
12179         if (el.applyTo){ // some kind of form field
12180             return this.addField(el);
12181         } 
12182         if (el.render){ // some kind of Toolbar.Item
12183             return this.addItem(el);
12184         }
12185         if (typeof el == "string"){ // string
12186             if(el == "separator" || el == "-"){
12187                 return this.addSeparator();
12188             }
12189             if (el == " "){
12190                 return this.addSpacer();
12191             }
12192             if(el == "->"){
12193                 return this.addFill();
12194             }
12195             return this.addText(el);
12196             
12197         }
12198         if(el.tagName){ // element
12199             return this.addElement(el);
12200         }
12201         if(typeof el == "object"){ // must be button config?
12202             return this.addButton(el);
12203         }
12204         // and now what?!?!
12205         return false;
12206         
12207     },
12208     
12209     /**
12210      * Add an Xtype element
12211      * @param {Object} xtype Xtype Object
12212      * @return {Object} created Object
12213      */
12214     addxtype : function(e){
12215         return this.add(e);  
12216     },
12217     
12218     /**
12219      * Returns the Element for this toolbar.
12220      * @return {Roo.Element}
12221      */
12222     getEl : function(){
12223         return this.el;  
12224     },
12225     
12226     /**
12227      * Adds a separator
12228      * @return {Roo.Toolbar.Item} The separator item
12229      */
12230     addSeparator : function(){
12231         return this.addItem(new Roo.Toolbar.Separator());
12232     },
12233
12234     /**
12235      * Adds a spacer element
12236      * @return {Roo.Toolbar.Spacer} The spacer item
12237      */
12238     addSpacer : function(){
12239         return this.addItem(new Roo.Toolbar.Spacer());
12240     },
12241
12242     /**
12243      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12244      * @return {Roo.Toolbar.Fill} The fill item
12245      */
12246     addFill : function(){
12247         return this.addItem(new Roo.Toolbar.Fill());
12248     },
12249
12250     /**
12251      * Adds any standard HTML element to the toolbar
12252      * @param {String/HTMLElement/Element} el The element or id of the element to add
12253      * @return {Roo.Toolbar.Item} The element's item
12254      */
12255     addElement : function(el){
12256         return this.addItem(new Roo.Toolbar.Item(el));
12257     },
12258     /**
12259      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12260      * @type Roo.util.MixedCollection  
12261      */
12262     items : false,
12263      
12264     /**
12265      * Adds any Toolbar.Item or subclass
12266      * @param {Roo.Toolbar.Item} item
12267      * @return {Roo.Toolbar.Item} The item
12268      */
12269     addItem : function(item){
12270         var td = this.nextBlock();
12271         item.render(td);
12272         this.items.add(item);
12273         return item;
12274     },
12275     
12276     /**
12277      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12278      * @param {Object/Array} config A button config or array of configs
12279      * @return {Roo.Toolbar.Button/Array}
12280      */
12281     addButton : function(config){
12282         if(config instanceof Array){
12283             var buttons = [];
12284             for(var i = 0, len = config.length; i < len; i++) {
12285                 buttons.push(this.addButton(config[i]));
12286             }
12287             return buttons;
12288         }
12289         var b = config;
12290         if(!(config instanceof Roo.Toolbar.Button)){
12291             b = config.split ?
12292                 new Roo.Toolbar.SplitButton(config) :
12293                 new Roo.Toolbar.Button(config);
12294         }
12295         var td = this.nextBlock();
12296         b.render(td);
12297         this.items.add(b);
12298         return b;
12299     },
12300     
12301     /**
12302      * Adds text to the toolbar
12303      * @param {String} text The text to add
12304      * @return {Roo.Toolbar.Item} The element's item
12305      */
12306     addText : function(text){
12307         return this.addItem(new Roo.Toolbar.TextItem(text));
12308     },
12309     
12310     /**
12311      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12312      * @param {Number} index The index where the item is to be inserted
12313      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12314      * @return {Roo.Toolbar.Button/Item}
12315      */
12316     insertButton : function(index, item){
12317         if(item instanceof Array){
12318             var buttons = [];
12319             for(var i = 0, len = item.length; i < len; i++) {
12320                buttons.push(this.insertButton(index + i, item[i]));
12321             }
12322             return buttons;
12323         }
12324         if (!(item instanceof Roo.Toolbar.Button)){
12325            item = new Roo.Toolbar.Button(item);
12326         }
12327         var td = document.createElement("td");
12328         this.tr.insertBefore(td, this.tr.childNodes[index]);
12329         item.render(td);
12330         this.items.insert(index, item);
12331         return item;
12332     },
12333     
12334     /**
12335      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12336      * @param {Object} config
12337      * @return {Roo.Toolbar.Item} The element's item
12338      */
12339     addDom : function(config, returnEl){
12340         var td = this.nextBlock();
12341         Roo.DomHelper.overwrite(td, config);
12342         var ti = new Roo.Toolbar.Item(td.firstChild);
12343         ti.render(td);
12344         this.items.add(ti);
12345         return ti;
12346     },
12347
12348     /**
12349      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12350      * @type Roo.util.MixedCollection  
12351      */
12352     fields : false,
12353     
12354     /**
12355      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12356      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12357      * @param {Roo.form.Field} field
12358      * @return {Roo.ToolbarItem}
12359      */
12360      
12361       
12362     addField : function(field) {
12363         if (!this.fields) {
12364             var autoId = 0;
12365             this.fields = new Roo.util.MixedCollection(false, function(o){
12366                 return o.id || ("item" + (++autoId));
12367             });
12368
12369         }
12370         
12371         var td = this.nextBlock();
12372         field.render(td);
12373         var ti = new Roo.Toolbar.Item(td.firstChild);
12374         ti.render(td);
12375         this.items.add(ti);
12376         this.fields.add(field);
12377         return ti;
12378     },
12379     /**
12380      * Hide the toolbar
12381      * @method hide
12382      */
12383      
12384       
12385     hide : function()
12386     {
12387         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12388         this.el.child('div').hide();
12389     },
12390     /**
12391      * Show the toolbar
12392      * @method show
12393      */
12394     show : function()
12395     {
12396         this.el.child('div').show();
12397     },
12398       
12399     // private
12400     nextBlock : function(){
12401         var td = document.createElement("td");
12402         this.tr.appendChild(td);
12403         return td;
12404     },
12405
12406     // private
12407     destroy : function(){
12408         if(this.items){ // rendered?
12409             Roo.destroy.apply(Roo, this.items.items);
12410         }
12411         if(this.fields){ // rendered?
12412             Roo.destroy.apply(Roo, this.fields.items);
12413         }
12414         Roo.Element.uncache(this.el, this.tr);
12415     }
12416 };
12417
12418 /**
12419  * @class Roo.Toolbar.Item
12420  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12421  * @constructor
12422  * Creates a new Item
12423  * @param {HTMLElement} el 
12424  */
12425 Roo.Toolbar.Item = function(el){
12426     this.el = Roo.getDom(el);
12427     this.id = Roo.id(this.el);
12428     this.hidden = false;
12429 };
12430
12431 Roo.Toolbar.Item.prototype = {
12432     
12433     /**
12434      * Get this item's HTML Element
12435      * @return {HTMLElement}
12436      */
12437     getEl : function(){
12438        return this.el;  
12439     },
12440
12441     // private
12442     render : function(td){
12443         this.td = td;
12444         td.appendChild(this.el);
12445     },
12446     
12447     /**
12448      * Removes and destroys this item.
12449      */
12450     destroy : function(){
12451         this.td.parentNode.removeChild(this.td);
12452     },
12453     
12454     /**
12455      * Shows this item.
12456      */
12457     show: function(){
12458         this.hidden = false;
12459         this.td.style.display = "";
12460     },
12461     
12462     /**
12463      * Hides this item.
12464      */
12465     hide: function(){
12466         this.hidden = true;
12467         this.td.style.display = "none";
12468     },
12469     
12470     /**
12471      * Convenience function for boolean show/hide.
12472      * @param {Boolean} visible true to show/false to hide
12473      */
12474     setVisible: function(visible){
12475         if(visible) {
12476             this.show();
12477         }else{
12478             this.hide();
12479         }
12480     },
12481     
12482     /**
12483      * Try to focus this item.
12484      */
12485     focus : function(){
12486         Roo.fly(this.el).focus();
12487     },
12488     
12489     /**
12490      * Disables this item.
12491      */
12492     disable : function(){
12493         Roo.fly(this.td).addClass("x-item-disabled");
12494         this.disabled = true;
12495         this.el.disabled = true;
12496     },
12497     
12498     /**
12499      * Enables this item.
12500      */
12501     enable : function(){
12502         Roo.fly(this.td).removeClass("x-item-disabled");
12503         this.disabled = false;
12504         this.el.disabled = false;
12505     }
12506 };
12507
12508
12509 /**
12510  * @class Roo.Toolbar.Separator
12511  * @extends Roo.Toolbar.Item
12512  * A simple toolbar separator class
12513  * @constructor
12514  * Creates a new Separator
12515  */
12516 Roo.Toolbar.Separator = function(){
12517     var s = document.createElement("span");
12518     s.className = "ytb-sep";
12519     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12520 };
12521 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12522     enable:Roo.emptyFn,
12523     disable:Roo.emptyFn,
12524     focus:Roo.emptyFn
12525 });
12526
12527 /**
12528  * @class Roo.Toolbar.Spacer
12529  * @extends Roo.Toolbar.Item
12530  * A simple element that adds extra horizontal space to a toolbar.
12531  * @constructor
12532  * Creates a new Spacer
12533  */
12534 Roo.Toolbar.Spacer = function(){
12535     var s = document.createElement("div");
12536     s.className = "ytb-spacer";
12537     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12538 };
12539 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12540     enable:Roo.emptyFn,
12541     disable:Roo.emptyFn,
12542     focus:Roo.emptyFn
12543 });
12544
12545 /**
12546  * @class Roo.Toolbar.Fill
12547  * @extends Roo.Toolbar.Spacer
12548  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12549  * @constructor
12550  * Creates a new Spacer
12551  */
12552 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12553     // private
12554     render : function(td){
12555         td.style.width = '100%';
12556         Roo.Toolbar.Fill.superclass.render.call(this, td);
12557     }
12558 });
12559
12560 /**
12561  * @class Roo.Toolbar.TextItem
12562  * @extends Roo.Toolbar.Item
12563  * A simple class that renders text directly into a toolbar.
12564  * @constructor
12565  * Creates a new TextItem
12566  * @param {String} text
12567  */
12568 Roo.Toolbar.TextItem = function(text){
12569     if (typeof(text) == 'object') {
12570         text = text.text;
12571     }
12572     var s = document.createElement("span");
12573     s.className = "ytb-text";
12574     s.innerHTML = text;
12575     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12576 };
12577 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12578     enable:Roo.emptyFn,
12579     disable:Roo.emptyFn,
12580     focus:Roo.emptyFn
12581 });
12582
12583 /**
12584  * @class Roo.Toolbar.Button
12585  * @extends Roo.Button
12586  * A button that renders into a toolbar.
12587  * @constructor
12588  * Creates a new Button
12589  * @param {Object} config A standard {@link Roo.Button} config object
12590  */
12591 Roo.Toolbar.Button = function(config){
12592     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12593 };
12594 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12595     render : function(td){
12596         this.td = td;
12597         Roo.Toolbar.Button.superclass.render.call(this, td);
12598     },
12599     
12600     /**
12601      * Removes and destroys this button
12602      */
12603     destroy : function(){
12604         Roo.Toolbar.Button.superclass.destroy.call(this);
12605         this.td.parentNode.removeChild(this.td);
12606     },
12607     
12608     /**
12609      * Shows this button
12610      */
12611     show: function(){
12612         this.hidden = false;
12613         this.td.style.display = "";
12614     },
12615     
12616     /**
12617      * Hides this button
12618      */
12619     hide: function(){
12620         this.hidden = true;
12621         this.td.style.display = "none";
12622     },
12623
12624     /**
12625      * Disables this item
12626      */
12627     disable : function(){
12628         Roo.fly(this.td).addClass("x-item-disabled");
12629         this.disabled = true;
12630     },
12631
12632     /**
12633      * Enables this item
12634      */
12635     enable : function(){
12636         Roo.fly(this.td).removeClass("x-item-disabled");
12637         this.disabled = false;
12638     }
12639 });
12640 // backwards compat
12641 Roo.ToolbarButton = Roo.Toolbar.Button;
12642
12643 /**
12644  * @class Roo.Toolbar.SplitButton
12645  * @extends Roo.SplitButton
12646  * A menu button that renders into a toolbar.
12647  * @constructor
12648  * Creates a new SplitButton
12649  * @param {Object} config A standard {@link Roo.SplitButton} config object
12650  */
12651 Roo.Toolbar.SplitButton = function(config){
12652     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12653 };
12654 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12655     render : function(td){
12656         this.td = td;
12657         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12658     },
12659     
12660     /**
12661      * Removes and destroys this button
12662      */
12663     destroy : function(){
12664         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12665         this.td.parentNode.removeChild(this.td);
12666     },
12667     
12668     /**
12669      * Shows this button
12670      */
12671     show: function(){
12672         this.hidden = false;
12673         this.td.style.display = "";
12674     },
12675     
12676     /**
12677      * Hides this button
12678      */
12679     hide: function(){
12680         this.hidden = true;
12681         this.td.style.display = "none";
12682     }
12683 });
12684
12685 // backwards compat
12686 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12687  * Based on:
12688  * Ext JS Library 1.1.1
12689  * Copyright(c) 2006-2007, Ext JS, LLC.
12690  *
12691  * Originally Released Under LGPL - original licence link has changed is not relivant.
12692  *
12693  * Fork - LGPL
12694  * <script type="text/javascript">
12695  */
12696  
12697 /**
12698  * @class Roo.PagingToolbar
12699  * @extends Roo.Toolbar
12700  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12701  * @constructor
12702  * Create a new PagingToolbar
12703  * @param {Object} config The config object
12704  */
12705 Roo.PagingToolbar = function(el, ds, config)
12706 {
12707     // old args format still supported... - xtype is prefered..
12708     if (typeof(el) == 'object' && el.xtype) {
12709         // created from xtype...
12710         config = el;
12711         ds = el.dataSource;
12712         el = config.container;
12713     }
12714     
12715     
12716     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12717     this.ds = ds;
12718     this.cursor = 0;
12719     this.renderButtons(this.el);
12720     this.bind(ds);
12721 };
12722
12723 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12724     /**
12725      * @cfg {Roo.data.Store} dataSource
12726      * The underlying data store providing the paged data
12727      */
12728     /**
12729      * @cfg {String/HTMLElement/Element} container
12730      * container The id or element that will contain the toolbar
12731      */
12732     /**
12733      * @cfg {Boolean} displayInfo
12734      * True to display the displayMsg (defaults to false)
12735      */
12736     /**
12737      * @cfg {Number} pageSize
12738      * The number of records to display per page (defaults to 20)
12739      */
12740     pageSize: 20,
12741     /**
12742      * @cfg {String} displayMsg
12743      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12744      */
12745     displayMsg : 'Displaying {0} - {1} of {2}',
12746     /**
12747      * @cfg {String} emptyMsg
12748      * The message to display when no records are found (defaults to "No data to display")
12749      */
12750     emptyMsg : 'No data to display',
12751     /**
12752      * Customizable piece of the default paging text (defaults to "Page")
12753      * @type String
12754      */
12755     beforePageText : "Page",
12756     /**
12757      * Customizable piece of the default paging text (defaults to "of %0")
12758      * @type String
12759      */
12760     afterPageText : "of {0}",
12761     /**
12762      * Customizable piece of the default paging text (defaults to "First Page")
12763      * @type String
12764      */
12765     firstText : "First Page",
12766     /**
12767      * Customizable piece of the default paging text (defaults to "Previous Page")
12768      * @type String
12769      */
12770     prevText : "Previous Page",
12771     /**
12772      * Customizable piece of the default paging text (defaults to "Next Page")
12773      * @type String
12774      */
12775     nextText : "Next Page",
12776     /**
12777      * Customizable piece of the default paging text (defaults to "Last Page")
12778      * @type String
12779      */
12780     lastText : "Last Page",
12781     /**
12782      * Customizable piece of the default paging text (defaults to "Refresh")
12783      * @type String
12784      */
12785     refreshText : "Refresh",
12786
12787     // private
12788     renderButtons : function(el){
12789         Roo.PagingToolbar.superclass.render.call(this, el);
12790         this.first = this.addButton({
12791             tooltip: this.firstText,
12792             cls: "x-btn-icon x-grid-page-first",
12793             disabled: true,
12794             handler: this.onClick.createDelegate(this, ["first"])
12795         });
12796         this.prev = this.addButton({
12797             tooltip: this.prevText,
12798             cls: "x-btn-icon x-grid-page-prev",
12799             disabled: true,
12800             handler: this.onClick.createDelegate(this, ["prev"])
12801         });
12802         this.addSeparator();
12803         this.add(this.beforePageText);
12804         this.field = Roo.get(this.addDom({
12805            tag: "input",
12806            type: "text",
12807            size: "3",
12808            value: "1",
12809            cls: "x-grid-page-number"
12810         }).el);
12811         this.field.on("keydown", this.onPagingKeydown, this);
12812         this.field.on("focus", function(){this.dom.select();});
12813         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12814         this.field.setHeight(18);
12815         this.addSeparator();
12816         this.next = this.addButton({
12817             tooltip: this.nextText,
12818             cls: "x-btn-icon x-grid-page-next",
12819             disabled: true,
12820             handler: this.onClick.createDelegate(this, ["next"])
12821         });
12822         this.last = this.addButton({
12823             tooltip: this.lastText,
12824             cls: "x-btn-icon x-grid-page-last",
12825             disabled: true,
12826             handler: this.onClick.createDelegate(this, ["last"])
12827         });
12828         this.addSeparator();
12829         this.loading = this.addButton({
12830             tooltip: this.refreshText,
12831             cls: "x-btn-icon x-grid-loading",
12832             handler: this.onClick.createDelegate(this, ["refresh"])
12833         });
12834
12835         if(this.displayInfo){
12836             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12837         }
12838     },
12839
12840     // private
12841     updateInfo : function(){
12842         if(this.displayEl){
12843             var count = this.ds.getCount();
12844             var msg = count == 0 ?
12845                 this.emptyMsg :
12846                 String.format(
12847                     this.displayMsg,
12848                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12849                 );
12850             this.displayEl.update(msg);
12851         }
12852     },
12853
12854     // private
12855     onLoad : function(ds, r, o){
12856        this.cursor = o.params ? o.params.start : 0;
12857        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12858
12859        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12860        this.field.dom.value = ap;
12861        this.first.setDisabled(ap == 1);
12862        this.prev.setDisabled(ap == 1);
12863        this.next.setDisabled(ap == ps);
12864        this.last.setDisabled(ap == ps);
12865        this.loading.enable();
12866        this.updateInfo();
12867     },
12868
12869     // private
12870     getPageData : function(){
12871         var total = this.ds.getTotalCount();
12872         return {
12873             total : total,
12874             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12875             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12876         };
12877     },
12878
12879     // private
12880     onLoadError : function(){
12881         this.loading.enable();
12882     },
12883
12884     // private
12885     onPagingKeydown : function(e){
12886         var k = e.getKey();
12887         var d = this.getPageData();
12888         if(k == e.RETURN){
12889             var v = this.field.dom.value, pageNum;
12890             if(!v || isNaN(pageNum = parseInt(v, 10))){
12891                 this.field.dom.value = d.activePage;
12892                 return;
12893             }
12894             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12895             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12896             e.stopEvent();
12897         }
12898         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))
12899         {
12900           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12901           this.field.dom.value = pageNum;
12902           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12903           e.stopEvent();
12904         }
12905         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12906         {
12907           var v = this.field.dom.value, pageNum; 
12908           var increment = (e.shiftKey) ? 10 : 1;
12909           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12910             increment *= -1;
12911           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12912             this.field.dom.value = d.activePage;
12913             return;
12914           }
12915           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12916           {
12917             this.field.dom.value = parseInt(v, 10) + increment;
12918             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12919             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12920           }
12921           e.stopEvent();
12922         }
12923     },
12924
12925     // private
12926     beforeLoad : function(){
12927         if(this.loading){
12928             this.loading.disable();
12929         }
12930     },
12931
12932     // private
12933     onClick : function(which){
12934         var ds = this.ds;
12935         switch(which){
12936             case "first":
12937                 ds.load({params:{start: 0, limit: this.pageSize}});
12938             break;
12939             case "prev":
12940                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12941             break;
12942             case "next":
12943                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12944             break;
12945             case "last":
12946                 var total = ds.getTotalCount();
12947                 var extra = total % this.pageSize;
12948                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12949                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12950             break;
12951             case "refresh":
12952                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12953             break;
12954         }
12955     },
12956
12957     /**
12958      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12959      * @param {Roo.data.Store} store The data store to unbind
12960      */
12961     unbind : function(ds){
12962         ds.un("beforeload", this.beforeLoad, this);
12963         ds.un("load", this.onLoad, this);
12964         ds.un("loadexception", this.onLoadError, this);
12965         ds.un("remove", this.updateInfo, this);
12966         ds.un("add", this.updateInfo, this);
12967         this.ds = undefined;
12968     },
12969
12970     /**
12971      * Binds the paging toolbar to the specified {@link Roo.data.Store}
12972      * @param {Roo.data.Store} store The data store to bind
12973      */
12974     bind : function(ds){
12975         ds.on("beforeload", this.beforeLoad, this);
12976         ds.on("load", this.onLoad, this);
12977         ds.on("loadexception", this.onLoadError, this);
12978         ds.on("remove", this.updateInfo, this);
12979         ds.on("add", this.updateInfo, this);
12980         this.ds = ds;
12981     }
12982 });/*
12983  * Based on:
12984  * Ext JS Library 1.1.1
12985  * Copyright(c) 2006-2007, Ext JS, LLC.
12986  *
12987  * Originally Released Under LGPL - original licence link has changed is not relivant.
12988  *
12989  * Fork - LGPL
12990  * <script type="text/javascript">
12991  */
12992
12993 /**
12994  * @class Roo.Resizable
12995  * @extends Roo.util.Observable
12996  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
12997  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
12998  * 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
12999  * the element will be wrapped for you automatically.</p>
13000  * <p>Here is the list of valid resize handles:</p>
13001  * <pre>
13002 Value   Description
13003 ------  -------------------
13004  'n'     north
13005  's'     south
13006  'e'     east
13007  'w'     west
13008  'nw'    northwest
13009  'sw'    southwest
13010  'se'    southeast
13011  'ne'    northeast
13012  'all'   all
13013 </pre>
13014  * <p>Here's an example showing the creation of a typical Resizable:</p>
13015  * <pre><code>
13016 var resizer = new Roo.Resizable("element-id", {
13017     handles: 'all',
13018     minWidth: 200,
13019     minHeight: 100,
13020     maxWidth: 500,
13021     maxHeight: 400,
13022     pinned: true
13023 });
13024 resizer.on("resize", myHandler);
13025 </code></pre>
13026  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13027  * resizer.east.setDisplayed(false);</p>
13028  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13029  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13030  * resize operation's new size (defaults to [0, 0])
13031  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13032  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13033  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13034  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13035  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13036  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13037  * @cfg {Number} width The width of the element in pixels (defaults to null)
13038  * @cfg {Number} height The height of the element in pixels (defaults to null)
13039  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13040  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13041  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13042  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13043  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13044  * in favor of the handles config option (defaults to false)
13045  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13046  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13047  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13048  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13049  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13050  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13051  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13052  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13053  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13054  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13055  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13056  * @constructor
13057  * Create a new resizable component
13058  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13059  * @param {Object} config configuration options
13060   */
13061 Roo.Resizable = function(el, config){
13062     this.el = Roo.get(el);
13063
13064     if(config && config.wrap){
13065         config.resizeChild = this.el;
13066         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13067         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13068         this.el.setStyle("overflow", "hidden");
13069         this.el.setPositioning(config.resizeChild.getPositioning());
13070         config.resizeChild.clearPositioning();
13071         if(!config.width || !config.height){
13072             var csize = config.resizeChild.getSize();
13073             this.el.setSize(csize.width, csize.height);
13074         }
13075         if(config.pinned && !config.adjustments){
13076             config.adjustments = "auto";
13077         }
13078     }
13079
13080     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13081     this.proxy.unselectable();
13082     this.proxy.enableDisplayMode('block');
13083
13084     Roo.apply(this, config);
13085
13086     if(this.pinned){
13087         this.disableTrackOver = true;
13088         this.el.addClass("x-resizable-pinned");
13089     }
13090     // if the element isn't positioned, make it relative
13091     var position = this.el.getStyle("position");
13092     if(position != "absolute" && position != "fixed"){
13093         this.el.setStyle("position", "relative");
13094     }
13095     if(!this.handles){ // no handles passed, must be legacy style
13096         this.handles = 's,e,se';
13097         if(this.multiDirectional){
13098             this.handles += ',n,w';
13099         }
13100     }
13101     if(this.handles == "all"){
13102         this.handles = "n s e w ne nw se sw";
13103     }
13104     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13105     var ps = Roo.Resizable.positions;
13106     for(var i = 0, len = hs.length; i < len; i++){
13107         if(hs[i] && ps[hs[i]]){
13108             var pos = ps[hs[i]];
13109             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13110         }
13111     }
13112     // legacy
13113     this.corner = this.southeast;
13114
13115     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
13116         this.updateBox = true;
13117     }
13118
13119     this.activeHandle = null;
13120
13121     if(this.resizeChild){
13122         if(typeof this.resizeChild == "boolean"){
13123             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13124         }else{
13125             this.resizeChild = Roo.get(this.resizeChild, true);
13126         }
13127     }
13128
13129     if(this.adjustments == "auto"){
13130         var rc = this.resizeChild;
13131         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13132         if(rc && (hw || hn)){
13133             rc.position("relative");
13134             rc.setLeft(hw ? hw.el.getWidth() : 0);
13135             rc.setTop(hn ? hn.el.getHeight() : 0);
13136         }
13137         this.adjustments = [
13138             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13139             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13140         ];
13141     }
13142
13143     if(this.draggable){
13144         this.dd = this.dynamic ?
13145             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13146         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13147     }
13148
13149     // public events
13150     this.addEvents({
13151         /**
13152          * @event beforeresize
13153          * Fired before resize is allowed. Set enabled to false to cancel resize.
13154          * @param {Roo.Resizable} this
13155          * @param {Roo.EventObject} e The mousedown event
13156          */
13157         "beforeresize" : true,
13158         /**
13159          * @event resize
13160          * Fired after a resize.
13161          * @param {Roo.Resizable} this
13162          * @param {Number} width The new width
13163          * @param {Number} height The new height
13164          * @param {Roo.EventObject} e The mouseup event
13165          */
13166         "resize" : true
13167     });
13168
13169     if(this.width !== null && this.height !== null){
13170         this.resizeTo(this.width, this.height);
13171     }else{
13172         this.updateChildSize();
13173     }
13174     if(Roo.isIE){
13175         this.el.dom.style.zoom = 1;
13176     }
13177     Roo.Resizable.superclass.constructor.call(this);
13178 };
13179
13180 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13181         resizeChild : false,
13182         adjustments : [0, 0],
13183         minWidth : 5,
13184         minHeight : 5,
13185         maxWidth : 10000,
13186         maxHeight : 10000,
13187         enabled : true,
13188         animate : false,
13189         duration : .35,
13190         dynamic : false,
13191         handles : false,
13192         multiDirectional : false,
13193         disableTrackOver : false,
13194         easing : 'easeOutStrong',
13195         widthIncrement : 0,
13196         heightIncrement : 0,
13197         pinned : false,
13198         width : null,
13199         height : null,
13200         preserveRatio : false,
13201         transparent: false,
13202         minX: 0,
13203         minY: 0,
13204         draggable: false,
13205
13206         /**
13207          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13208          */
13209         constrainTo: undefined,
13210         /**
13211          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13212          */
13213         resizeRegion: undefined,
13214
13215
13216     /**
13217      * Perform a manual resize
13218      * @param {Number} width
13219      * @param {Number} height
13220      */
13221     resizeTo : function(width, height){
13222         this.el.setSize(width, height);
13223         this.updateChildSize();
13224         this.fireEvent("resize", this, width, height, null);
13225     },
13226
13227     // private
13228     startSizing : function(e, handle){
13229         this.fireEvent("beforeresize", this, e);
13230         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13231
13232             if(!this.overlay){
13233                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13234                 this.overlay.unselectable();
13235                 this.overlay.enableDisplayMode("block");
13236                 this.overlay.on("mousemove", this.onMouseMove, this);
13237                 this.overlay.on("mouseup", this.onMouseUp, this);
13238             }
13239             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13240
13241             this.resizing = true;
13242             this.startBox = this.el.getBox();
13243             this.startPoint = e.getXY();
13244             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13245                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13246
13247             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13248             this.overlay.show();
13249
13250             if(this.constrainTo) {
13251                 var ct = Roo.get(this.constrainTo);
13252                 this.resizeRegion = ct.getRegion().adjust(
13253                     ct.getFrameWidth('t'),
13254                     ct.getFrameWidth('l'),
13255                     -ct.getFrameWidth('b'),
13256                     -ct.getFrameWidth('r')
13257                 );
13258             }
13259
13260             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13261             this.proxy.show();
13262             this.proxy.setBox(this.startBox);
13263             if(!this.dynamic){
13264                 this.proxy.setStyle('visibility', 'visible');
13265             }
13266         }
13267     },
13268
13269     // private
13270     onMouseDown : function(handle, e){
13271         if(this.enabled){
13272             e.stopEvent();
13273             this.activeHandle = handle;
13274             this.startSizing(e, handle);
13275         }
13276     },
13277
13278     // private
13279     onMouseUp : function(e){
13280         var size = this.resizeElement();
13281         this.resizing = false;
13282         this.handleOut();
13283         this.overlay.hide();
13284         this.proxy.hide();
13285         this.fireEvent("resize", this, size.width, size.height, e);
13286     },
13287
13288     // private
13289     updateChildSize : function(){
13290         if(this.resizeChild){
13291             var el = this.el;
13292             var child = this.resizeChild;
13293             var adj = this.adjustments;
13294             if(el.dom.offsetWidth){
13295                 var b = el.getSize(true);
13296                 child.setSize(b.width+adj[0], b.height+adj[1]);
13297             }
13298             // Second call here for IE
13299             // The first call enables instant resizing and
13300             // the second call corrects scroll bars if they
13301             // exist
13302             if(Roo.isIE){
13303                 setTimeout(function(){
13304                     if(el.dom.offsetWidth){
13305                         var b = el.getSize(true);
13306                         child.setSize(b.width+adj[0], b.height+adj[1]);
13307                     }
13308                 }, 10);
13309             }
13310         }
13311     },
13312
13313     // private
13314     snap : function(value, inc, min){
13315         if(!inc || !value) return value;
13316         var newValue = value;
13317         var m = value % inc;
13318         if(m > 0){
13319             if(m > (inc/2)){
13320                 newValue = value + (inc-m);
13321             }else{
13322                 newValue = value - m;
13323             }
13324         }
13325         return Math.max(min, newValue);
13326     },
13327
13328     // private
13329     resizeElement : function(){
13330         var box = this.proxy.getBox();
13331         if(this.updateBox){
13332             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13333         }else{
13334             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13335         }
13336         this.updateChildSize();
13337         if(!this.dynamic){
13338             this.proxy.hide();
13339         }
13340         return box;
13341     },
13342
13343     // private
13344     constrain : function(v, diff, m, mx){
13345         if(v - diff < m){
13346             diff = v - m;
13347         }else if(v - diff > mx){
13348             diff = mx - v;
13349         }
13350         return diff;
13351     },
13352
13353     // private
13354     onMouseMove : function(e){
13355         if(this.enabled){
13356             try{// try catch so if something goes wrong the user doesn't get hung
13357
13358             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13359                 return;
13360             }
13361
13362             //var curXY = this.startPoint;
13363             var curSize = this.curSize || this.startBox;
13364             var x = this.startBox.x, y = this.startBox.y;
13365             var ox = x, oy = y;
13366             var w = curSize.width, h = curSize.height;
13367             var ow = w, oh = h;
13368             var mw = this.minWidth, mh = this.minHeight;
13369             var mxw = this.maxWidth, mxh = this.maxHeight;
13370             var wi = this.widthIncrement;
13371             var hi = this.heightIncrement;
13372
13373             var eventXY = e.getXY();
13374             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13375             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13376
13377             var pos = this.activeHandle.position;
13378
13379             switch(pos){
13380                 case "east":
13381                     w += diffX;
13382                     w = Math.min(Math.max(mw, w), mxw);
13383                     break;
13384                 case "south":
13385                     h += diffY;
13386                     h = Math.min(Math.max(mh, h), mxh);
13387                     break;
13388                 case "southeast":
13389                     w += diffX;
13390                     h += diffY;
13391                     w = Math.min(Math.max(mw, w), mxw);
13392                     h = Math.min(Math.max(mh, h), mxh);
13393                     break;
13394                 case "north":
13395                     diffY = this.constrain(h, diffY, mh, mxh);
13396                     y += diffY;
13397                     h -= diffY;
13398                     break;
13399                 case "west":
13400                     diffX = this.constrain(w, diffX, mw, mxw);
13401                     x += diffX;
13402                     w -= diffX;
13403                     break;
13404                 case "northeast":
13405                     w += diffX;
13406                     w = Math.min(Math.max(mw, w), mxw);
13407                     diffY = this.constrain(h, diffY, mh, mxh);
13408                     y += diffY;
13409                     h -= diffY;
13410                     break;
13411                 case "northwest":
13412                     diffX = this.constrain(w, diffX, mw, mxw);
13413                     diffY = this.constrain(h, diffY, mh, mxh);
13414                     y += diffY;
13415                     h -= diffY;
13416                     x += diffX;
13417                     w -= diffX;
13418                     break;
13419                case "southwest":
13420                     diffX = this.constrain(w, diffX, mw, mxw);
13421                     h += diffY;
13422                     h = Math.min(Math.max(mh, h), mxh);
13423                     x += diffX;
13424                     w -= diffX;
13425                     break;
13426             }
13427
13428             var sw = this.snap(w, wi, mw);
13429             var sh = this.snap(h, hi, mh);
13430             if(sw != w || sh != h){
13431                 switch(pos){
13432                     case "northeast":
13433                         y -= sh - h;
13434                     break;
13435                     case "north":
13436                         y -= sh - h;
13437                         break;
13438                     case "southwest":
13439                         x -= sw - w;
13440                     break;
13441                     case "west":
13442                         x -= sw - w;
13443                         break;
13444                     case "northwest":
13445                         x -= sw - w;
13446                         y -= sh - h;
13447                     break;
13448                 }
13449                 w = sw;
13450                 h = sh;
13451             }
13452
13453             if(this.preserveRatio){
13454                 switch(pos){
13455                     case "southeast":
13456                     case "east":
13457                         h = oh * (w/ow);
13458                         h = Math.min(Math.max(mh, h), mxh);
13459                         w = ow * (h/oh);
13460                        break;
13461                     case "south":
13462                         w = ow * (h/oh);
13463                         w = Math.min(Math.max(mw, w), mxw);
13464                         h = oh * (w/ow);
13465                         break;
13466                     case "northeast":
13467                         w = ow * (h/oh);
13468                         w = Math.min(Math.max(mw, w), mxw);
13469                         h = oh * (w/ow);
13470                     break;
13471                     case "north":
13472                         var tw = w;
13473                         w = ow * (h/oh);
13474                         w = Math.min(Math.max(mw, w), mxw);
13475                         h = oh * (w/ow);
13476                         x += (tw - w) / 2;
13477                         break;
13478                     case "southwest":
13479                         h = oh * (w/ow);
13480                         h = Math.min(Math.max(mh, h), mxh);
13481                         var tw = w;
13482                         w = ow * (h/oh);
13483                         x += tw - w;
13484                         break;
13485                     case "west":
13486                         var th = h;
13487                         h = oh * (w/ow);
13488                         h = Math.min(Math.max(mh, h), mxh);
13489                         y += (th - h) / 2;
13490                         var tw = w;
13491                         w = ow * (h/oh);
13492                         x += tw - w;
13493                        break;
13494                     case "northwest":
13495                         var tw = w;
13496                         var th = h;
13497                         h = oh * (w/ow);
13498                         h = Math.min(Math.max(mh, h), mxh);
13499                         w = ow * (h/oh);
13500                         y += th - h;
13501                          x += tw - w;
13502                        break;
13503
13504                 }
13505             }
13506             this.proxy.setBounds(x, y, w, h);
13507             if(this.dynamic){
13508                 this.resizeElement();
13509             }
13510             }catch(e){}
13511         }
13512     },
13513
13514     // private
13515     handleOver : function(){
13516         if(this.enabled){
13517             this.el.addClass("x-resizable-over");
13518         }
13519     },
13520
13521     // private
13522     handleOut : function(){
13523         if(!this.resizing){
13524             this.el.removeClass("x-resizable-over");
13525         }
13526     },
13527
13528     /**
13529      * Returns the element this component is bound to.
13530      * @return {Roo.Element}
13531      */
13532     getEl : function(){
13533         return this.el;
13534     },
13535
13536     /**
13537      * Returns the resizeChild element (or null).
13538      * @return {Roo.Element}
13539      */
13540     getResizeChild : function(){
13541         return this.resizeChild;
13542     },
13543
13544     /**
13545      * Destroys this resizable. If the element was wrapped and
13546      * removeEl is not true then the element remains.
13547      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13548      */
13549     destroy : function(removeEl){
13550         this.proxy.remove();
13551         if(this.overlay){
13552             this.overlay.removeAllListeners();
13553             this.overlay.remove();
13554         }
13555         var ps = Roo.Resizable.positions;
13556         for(var k in ps){
13557             if(typeof ps[k] != "function" && this[ps[k]]){
13558                 var h = this[ps[k]];
13559                 h.el.removeAllListeners();
13560                 h.el.remove();
13561             }
13562         }
13563         if(removeEl){
13564             this.el.update("");
13565             this.el.remove();
13566         }
13567     }
13568 });
13569
13570 // private
13571 // hash to map config positions to true positions
13572 Roo.Resizable.positions = {
13573     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
13574 };
13575
13576 // private
13577 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13578     if(!this.tpl){
13579         // only initialize the template if resizable is used
13580         var tpl = Roo.DomHelper.createTemplate(
13581             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13582         );
13583         tpl.compile();
13584         Roo.Resizable.Handle.prototype.tpl = tpl;
13585     }
13586     this.position = pos;
13587     this.rz = rz;
13588     this.el = this.tpl.append(rz.el.dom, [this.position], true);
13589     this.el.unselectable();
13590     if(transparent){
13591         this.el.setOpacity(0);
13592     }
13593     this.el.on("mousedown", this.onMouseDown, this);
13594     if(!disableTrackOver){
13595         this.el.on("mouseover", this.onMouseOver, this);
13596         this.el.on("mouseout", this.onMouseOut, this);
13597     }
13598 };
13599
13600 // private
13601 Roo.Resizable.Handle.prototype = {
13602     afterResize : function(rz){
13603         // do nothing
13604     },
13605     // private
13606     onMouseDown : function(e){
13607         this.rz.onMouseDown(this, e);
13608     },
13609     // private
13610     onMouseOver : function(e){
13611         this.rz.handleOver(this, e);
13612     },
13613     // private
13614     onMouseOut : function(e){
13615         this.rz.handleOut(this, e);
13616     }
13617 };/*
13618  * Based on:
13619  * Ext JS Library 1.1.1
13620  * Copyright(c) 2006-2007, Ext JS, LLC.
13621  *
13622  * Originally Released Under LGPL - original licence link has changed is not relivant.
13623  *
13624  * Fork - LGPL
13625  * <script type="text/javascript">
13626  */
13627
13628 /**
13629  * @class Roo.Editor
13630  * @extends Roo.Component
13631  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13632  * @constructor
13633  * Create a new Editor
13634  * @param {Roo.form.Field} field The Field object (or descendant)
13635  * @param {Object} config The config object
13636  */
13637 Roo.Editor = function(field, config){
13638     Roo.Editor.superclass.constructor.call(this, config);
13639     this.field = field;
13640     this.addEvents({
13641         /**
13642              * @event beforestartedit
13643              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13644              * false from the handler of this event.
13645              * @param {Editor} this
13646              * @param {Roo.Element} boundEl The underlying element bound to this editor
13647              * @param {Mixed} value The field value being set
13648              */
13649         "beforestartedit" : true,
13650         /**
13651              * @event startedit
13652              * Fires when this editor is displayed
13653              * @param {Roo.Element} boundEl The underlying element bound to this editor
13654              * @param {Mixed} value The starting field value
13655              */
13656         "startedit" : true,
13657         /**
13658              * @event beforecomplete
13659              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13660              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13661              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13662              * event will not fire since no edit actually occurred.
13663              * @param {Editor} this
13664              * @param {Mixed} value The current field value
13665              * @param {Mixed} startValue The original field value
13666              */
13667         "beforecomplete" : true,
13668         /**
13669              * @event complete
13670              * Fires after editing is complete and any changed value has been written to the underlying field.
13671              * @param {Editor} this
13672              * @param {Mixed} value The current field value
13673              * @param {Mixed} startValue The original field value
13674              */
13675         "complete" : true,
13676         /**
13677          * @event specialkey
13678          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13679          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13680          * @param {Roo.form.Field} this
13681          * @param {Roo.EventObject} e The event object
13682          */
13683         "specialkey" : true
13684     });
13685 };
13686
13687 Roo.extend(Roo.Editor, Roo.Component, {
13688     /**
13689      * @cfg {Boolean/String} autosize
13690      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13691      * or "height" to adopt the height only (defaults to false)
13692      */
13693     /**
13694      * @cfg {Boolean} revertInvalid
13695      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13696      * validation fails (defaults to true)
13697      */
13698     /**
13699      * @cfg {Boolean} ignoreNoChange
13700      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13701      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13702      * will never be ignored.
13703      */
13704     /**
13705      * @cfg {Boolean} hideEl
13706      * False to keep the bound element visible while the editor is displayed (defaults to true)
13707      */
13708     /**
13709      * @cfg {Mixed} value
13710      * The data value of the underlying field (defaults to "")
13711      */
13712     value : "",
13713     /**
13714      * @cfg {String} alignment
13715      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13716      */
13717     alignment: "c-c?",
13718     /**
13719      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13720      * for bottom-right shadow (defaults to "frame")
13721      */
13722     shadow : "frame",
13723     /**
13724      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13725      */
13726     constrain : false,
13727     /**
13728      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13729      */
13730     completeOnEnter : false,
13731     /**
13732      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13733      */
13734     cancelOnEsc : false,
13735     /**
13736      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13737      */
13738     updateEl : false,
13739
13740     // private
13741     onRender : function(ct, position){
13742         this.el = new Roo.Layer({
13743             shadow: this.shadow,
13744             cls: "x-editor",
13745             parentEl : ct,
13746             shim : this.shim,
13747             shadowOffset:4,
13748             id: this.id,
13749             constrain: this.constrain
13750         });
13751         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13752         if(this.field.msgTarget != 'title'){
13753             this.field.msgTarget = 'qtip';
13754         }
13755         this.field.render(this.el);
13756         if(Roo.isGecko){
13757             this.field.el.dom.setAttribute('autocomplete', 'off');
13758         }
13759         this.field.on("specialkey", this.onSpecialKey, this);
13760         if(this.swallowKeys){
13761             this.field.el.swallowEvent(['keydown','keypress']);
13762         }
13763         this.field.show();
13764         this.field.on("blur", this.onBlur, this);
13765         if(this.field.grow){
13766             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13767         }
13768     },
13769
13770     onSpecialKey : function(field, e){
13771         if(this.completeOnEnter && e.getKey() == e.ENTER){
13772             e.stopEvent();
13773             this.completeEdit();
13774         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13775             this.cancelEdit();
13776         }else{
13777             this.fireEvent('specialkey', field, e);
13778         }
13779     },
13780
13781     /**
13782      * Starts the editing process and shows the editor.
13783      * @param {String/HTMLElement/Element} el The element to edit
13784      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13785       * to the innerHTML of el.
13786      */
13787     startEdit : function(el, value){
13788         if(this.editing){
13789             this.completeEdit();
13790         }
13791         this.boundEl = Roo.get(el);
13792         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13793         if(!this.rendered){
13794             this.render(this.parentEl || document.body);
13795         }
13796         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13797             return;
13798         }
13799         this.startValue = v;
13800         this.field.setValue(v);
13801         if(this.autoSize){
13802             var sz = this.boundEl.getSize();
13803             switch(this.autoSize){
13804                 case "width":
13805                 this.setSize(sz.width,  "");
13806                 break;
13807                 case "height":
13808                 this.setSize("",  sz.height);
13809                 break;
13810                 default:
13811                 this.setSize(sz.width,  sz.height);
13812             }
13813         }
13814         this.el.alignTo(this.boundEl, this.alignment);
13815         this.editing = true;
13816         if(Roo.QuickTips){
13817             Roo.QuickTips.disable();
13818         }
13819         this.show();
13820     },
13821
13822     /**
13823      * Sets the height and width of this editor.
13824      * @param {Number} width The new width
13825      * @param {Number} height The new height
13826      */
13827     setSize : function(w, h){
13828         this.field.setSize(w, h);
13829         if(this.el){
13830             this.el.sync();
13831         }
13832     },
13833
13834     /**
13835      * Realigns the editor to the bound field based on the current alignment config value.
13836      */
13837     realign : function(){
13838         this.el.alignTo(this.boundEl, this.alignment);
13839     },
13840
13841     /**
13842      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13843      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13844      */
13845     completeEdit : function(remainVisible){
13846         if(!this.editing){
13847             return;
13848         }
13849         var v = this.getValue();
13850         if(this.revertInvalid !== false && !this.field.isValid()){
13851             v = this.startValue;
13852             this.cancelEdit(true);
13853         }
13854         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13855             this.editing = false;
13856             this.hide();
13857             return;
13858         }
13859         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13860             this.editing = false;
13861             if(this.updateEl && this.boundEl){
13862                 this.boundEl.update(v);
13863             }
13864             if(remainVisible !== true){
13865                 this.hide();
13866             }
13867             this.fireEvent("complete", this, v, this.startValue);
13868         }
13869     },
13870
13871     // private
13872     onShow : function(){
13873         this.el.show();
13874         if(this.hideEl !== false){
13875             this.boundEl.hide();
13876         }
13877         this.field.show();
13878         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13879             this.fixIEFocus = true;
13880             this.deferredFocus.defer(50, this);
13881         }else{
13882             this.field.focus();
13883         }
13884         this.fireEvent("startedit", this.boundEl, this.startValue);
13885     },
13886
13887     deferredFocus : function(){
13888         if(this.editing){
13889             this.field.focus();
13890         }
13891     },
13892
13893     /**
13894      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13895      * reverted to the original starting value.
13896      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13897      * cancel (defaults to false)
13898      */
13899     cancelEdit : function(remainVisible){
13900         if(this.editing){
13901             this.setValue(this.startValue);
13902             if(remainVisible !== true){
13903                 this.hide();
13904             }
13905         }
13906     },
13907
13908     // private
13909     onBlur : function(){
13910         if(this.allowBlur !== true && this.editing){
13911             this.completeEdit();
13912         }
13913     },
13914
13915     // private
13916     onHide : function(){
13917         if(this.editing){
13918             this.completeEdit();
13919             return;
13920         }
13921         this.field.blur();
13922         if(this.field.collapse){
13923             this.field.collapse();
13924         }
13925         this.el.hide();
13926         if(this.hideEl !== false){
13927             this.boundEl.show();
13928         }
13929         if(Roo.QuickTips){
13930             Roo.QuickTips.enable();
13931         }
13932     },
13933
13934     /**
13935      * Sets the data value of the editor
13936      * @param {Mixed} value Any valid value supported by the underlying field
13937      */
13938     setValue : function(v){
13939         this.field.setValue(v);
13940     },
13941
13942     /**
13943      * Gets the data value of the editor
13944      * @return {Mixed} The data value
13945      */
13946     getValue : function(){
13947         return this.field.getValue();
13948     }
13949 });/*
13950  * Based on:
13951  * Ext JS Library 1.1.1
13952  * Copyright(c) 2006-2007, Ext JS, LLC.
13953  *
13954  * Originally Released Under LGPL - original licence link has changed is not relivant.
13955  *
13956  * Fork - LGPL
13957  * <script type="text/javascript">
13958  */
13959  
13960 /**
13961  * @class Roo.BasicDialog
13962  * @extends Roo.util.Observable
13963  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
13964  * <pre><code>
13965 var dlg = new Roo.BasicDialog("my-dlg", {
13966     height: 200,
13967     width: 300,
13968     minHeight: 100,
13969     minWidth: 150,
13970     modal: true,
13971     proxyDrag: true,
13972     shadow: true
13973 });
13974 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
13975 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
13976 dlg.addButton('Cancel', dlg.hide, dlg);
13977 dlg.show();
13978 </code></pre>
13979   <b>A Dialog should always be a direct child of the body element.</b>
13980  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
13981  * @cfg {String} title Default text to display in the title bar (defaults to null)
13982  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13983  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
13984  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
13985  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
13986  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
13987  * (defaults to null with no animation)
13988  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
13989  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
13990  * property for valid values (defaults to 'all')
13991  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
13992  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
13993  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
13994  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
13995  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
13996  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
13997  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
13998  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
13999  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14000  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14001  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14002  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14003  * draggable = true (defaults to false)
14004  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14005  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14006  * shadow (defaults to false)
14007  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14008  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14009  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14010  * @cfg {Array} buttons Array of buttons
14011  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14012  * @constructor
14013  * Create a new BasicDialog.
14014  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14015  * @param {Object} config Configuration options
14016  */
14017 Roo.BasicDialog = function(el, config){
14018     this.el = Roo.get(el);
14019     var dh = Roo.DomHelper;
14020     if(!this.el && config && config.autoCreate){
14021         if(typeof config.autoCreate == "object"){
14022             if(!config.autoCreate.id){
14023                 config.autoCreate.id = el;
14024             }
14025             this.el = dh.append(document.body,
14026                         config.autoCreate, true);
14027         }else{
14028             this.el = dh.append(document.body,
14029                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14030         }
14031     }
14032     el = this.el;
14033     el.setDisplayed(true);
14034     el.hide = this.hideAction;
14035     this.id = el.id;
14036     el.addClass("x-dlg");
14037
14038     Roo.apply(this, config);
14039
14040     this.proxy = el.createProxy("x-dlg-proxy");
14041     this.proxy.hide = this.hideAction;
14042     this.proxy.setOpacity(.5);
14043     this.proxy.hide();
14044
14045     if(config.width){
14046         el.setWidth(config.width);
14047     }
14048     if(config.height){
14049         el.setHeight(config.height);
14050     }
14051     this.size = el.getSize();
14052     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14053         this.xy = [config.x,config.y];
14054     }else{
14055         this.xy = el.getCenterXY(true);
14056     }
14057     /** The header element @type Roo.Element */
14058     this.header = el.child("> .x-dlg-hd");
14059     /** The body element @type Roo.Element */
14060     this.body = el.child("> .x-dlg-bd");
14061     /** The footer element @type Roo.Element */
14062     this.footer = el.child("> .x-dlg-ft");
14063
14064     if(!this.header){
14065         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14066     }
14067     if(!this.body){
14068         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14069     }
14070
14071     this.header.unselectable();
14072     if(this.title){
14073         this.header.update(this.title);
14074     }
14075     // this element allows the dialog to be focused for keyboard event
14076     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14077     this.focusEl.swallowEvent("click", true);
14078
14079     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14080
14081     // wrap the body and footer for special rendering
14082     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14083     if(this.footer){
14084         this.bwrap.dom.appendChild(this.footer.dom);
14085     }
14086
14087     this.bg = this.el.createChild({
14088         tag: "div", cls:"x-dlg-bg",
14089         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14090     });
14091     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14092
14093
14094     if(this.autoScroll !== false && !this.autoTabs){
14095         this.body.setStyle("overflow", "auto");
14096     }
14097
14098     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14099
14100     if(this.closable !== false){
14101         this.el.addClass("x-dlg-closable");
14102         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14103         this.close.on("click", this.closeClick, this);
14104         this.close.addClassOnOver("x-dlg-close-over");
14105     }
14106     if(this.collapsible !== false){
14107         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14108         this.collapseBtn.on("click", this.collapseClick, this);
14109         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14110         this.header.on("dblclick", this.collapseClick, this);
14111     }
14112     if(this.resizable !== false){
14113         this.el.addClass("x-dlg-resizable");
14114         this.resizer = new Roo.Resizable(el, {
14115             minWidth: this.minWidth || 80,
14116             minHeight:this.minHeight || 80,
14117             handles: this.resizeHandles || "all",
14118             pinned: true
14119         });
14120         this.resizer.on("beforeresize", this.beforeResize, this);
14121         this.resizer.on("resize", this.onResize, this);
14122     }
14123     if(this.draggable !== false){
14124         el.addClass("x-dlg-draggable");
14125         if (!this.proxyDrag) {
14126             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14127         }
14128         else {
14129             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14130         }
14131         dd.setHandleElId(this.header.id);
14132         dd.endDrag = this.endMove.createDelegate(this);
14133         dd.startDrag = this.startMove.createDelegate(this);
14134         dd.onDrag = this.onDrag.createDelegate(this);
14135         dd.scroll = false;
14136         this.dd = dd;
14137     }
14138     if(this.modal){
14139         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14140         this.mask.enableDisplayMode("block");
14141         this.mask.hide();
14142         this.el.addClass("x-dlg-modal");
14143     }
14144     if(this.shadow){
14145         this.shadow = new Roo.Shadow({
14146             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14147             offset : this.shadowOffset
14148         });
14149     }else{
14150         this.shadowOffset = 0;
14151     }
14152     if(Roo.useShims && this.shim !== false){
14153         this.shim = this.el.createShim();
14154         this.shim.hide = this.hideAction;
14155         this.shim.hide();
14156     }else{
14157         this.shim = false;
14158     }
14159     if(this.autoTabs){
14160         this.initTabs();
14161     }
14162     if (this.buttons) { 
14163         var bts= this.buttons;
14164         this.buttons = [];
14165         Roo.each(bts, function(b) {
14166             this.addButton(b);
14167         }, this);
14168     }
14169     
14170     
14171     this.addEvents({
14172         /**
14173          * @event keydown
14174          * Fires when a key is pressed
14175          * @param {Roo.BasicDialog} this
14176          * @param {Roo.EventObject} e
14177          */
14178         "keydown" : true,
14179         /**
14180          * @event move
14181          * Fires when this dialog is moved by the user.
14182          * @param {Roo.BasicDialog} this
14183          * @param {Number} x The new page X
14184          * @param {Number} y The new page Y
14185          */
14186         "move" : true,
14187         /**
14188          * @event resize
14189          * Fires when this dialog is resized by the user.
14190          * @param {Roo.BasicDialog} this
14191          * @param {Number} width The new width
14192          * @param {Number} height The new height
14193          */
14194         "resize" : true,
14195         /**
14196          * @event beforehide
14197          * Fires before this dialog is hidden.
14198          * @param {Roo.BasicDialog} this
14199          */
14200         "beforehide" : true,
14201         /**
14202          * @event hide
14203          * Fires when this dialog is hidden.
14204          * @param {Roo.BasicDialog} this
14205          */
14206         "hide" : true,
14207         /**
14208          * @event beforeshow
14209          * Fires before this dialog is shown.
14210          * @param {Roo.BasicDialog} this
14211          */
14212         "beforeshow" : true,
14213         /**
14214          * @event show
14215          * Fires when this dialog is shown.
14216          * @param {Roo.BasicDialog} this
14217          */
14218         "show" : true
14219     });
14220     el.on("keydown", this.onKeyDown, this);
14221     el.on("mousedown", this.toFront, this);
14222     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14223     this.el.hide();
14224     Roo.DialogManager.register(this);
14225     Roo.BasicDialog.superclass.constructor.call(this);
14226 };
14227
14228 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14229     shadowOffset: Roo.isIE ? 6 : 5,
14230     minHeight: 80,
14231     minWidth: 200,
14232     minButtonWidth: 75,
14233     defaultButton: null,
14234     buttonAlign: "right",
14235     tabTag: 'div',
14236     firstShow: true,
14237
14238     /**
14239      * Sets the dialog title text
14240      * @param {String} text The title text to display
14241      * @return {Roo.BasicDialog} this
14242      */
14243     setTitle : function(text){
14244         this.header.update(text);
14245         return this;
14246     },
14247
14248     // private
14249     closeClick : function(){
14250         this.hide();
14251     },
14252
14253     // private
14254     collapseClick : function(){
14255         this[this.collapsed ? "expand" : "collapse"]();
14256     },
14257
14258     /**
14259      * Collapses the dialog to its minimized state (only the title bar is visible).
14260      * Equivalent to the user clicking the collapse dialog button.
14261      */
14262     collapse : function(){
14263         if(!this.collapsed){
14264             this.collapsed = true;
14265             this.el.addClass("x-dlg-collapsed");
14266             this.restoreHeight = this.el.getHeight();
14267             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14268         }
14269     },
14270
14271     /**
14272      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14273      * clicking the expand dialog button.
14274      */
14275     expand : function(){
14276         if(this.collapsed){
14277             this.collapsed = false;
14278             this.el.removeClass("x-dlg-collapsed");
14279             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14280         }
14281     },
14282
14283     /**
14284      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14285      * @return {Roo.TabPanel} The tabs component
14286      */
14287     initTabs : function(){
14288         var tabs = this.getTabs();
14289         while(tabs.getTab(0)){
14290             tabs.removeTab(0);
14291         }
14292         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14293             var dom = el.dom;
14294             tabs.addTab(Roo.id(dom), dom.title);
14295             dom.title = "";
14296         });
14297         tabs.activate(0);
14298         return tabs;
14299     },
14300
14301     // private
14302     beforeResize : function(){
14303         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14304     },
14305
14306     // private
14307     onResize : function(){
14308         this.refreshSize();
14309         this.syncBodyHeight();
14310         this.adjustAssets();
14311         this.focus();
14312         this.fireEvent("resize", this, this.size.width, this.size.height);
14313     },
14314
14315     // private
14316     onKeyDown : function(e){
14317         if(this.isVisible()){
14318             this.fireEvent("keydown", this, e);
14319         }
14320     },
14321
14322     /**
14323      * Resizes the dialog.
14324      * @param {Number} width
14325      * @param {Number} height
14326      * @return {Roo.BasicDialog} this
14327      */
14328     resizeTo : function(width, height){
14329         this.el.setSize(width, height);
14330         this.size = {width: width, height: height};
14331         this.syncBodyHeight();
14332         if(this.fixedcenter){
14333             this.center();
14334         }
14335         if(this.isVisible()){
14336             this.constrainXY();
14337             this.adjustAssets();
14338         }
14339         this.fireEvent("resize", this, width, height);
14340         return this;
14341     },
14342
14343
14344     /**
14345      * Resizes the dialog to fit the specified content size.
14346      * @param {Number} width
14347      * @param {Number} height
14348      * @return {Roo.BasicDialog} this
14349      */
14350     setContentSize : function(w, h){
14351         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14352         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14353         //if(!this.el.isBorderBox()){
14354             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14355             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14356         //}
14357         if(this.tabs){
14358             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14359             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14360         }
14361         this.resizeTo(w, h);
14362         return this;
14363     },
14364
14365     /**
14366      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14367      * executed in response to a particular key being pressed while the dialog is active.
14368      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14369      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14370      * @param {Function} fn The function to call
14371      * @param {Object} scope (optional) The scope of the function
14372      * @return {Roo.BasicDialog} this
14373      */
14374     addKeyListener : function(key, fn, scope){
14375         var keyCode, shift, ctrl, alt;
14376         if(typeof key == "object" && !(key instanceof Array)){
14377             keyCode = key["key"];
14378             shift = key["shift"];
14379             ctrl = key["ctrl"];
14380             alt = key["alt"];
14381         }else{
14382             keyCode = key;
14383         }
14384         var handler = function(dlg, e){
14385             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14386                 var k = e.getKey();
14387                 if(keyCode instanceof Array){
14388                     for(var i = 0, len = keyCode.length; i < len; i++){
14389                         if(keyCode[i] == k){
14390                           fn.call(scope || window, dlg, k, e);
14391                           return;
14392                         }
14393                     }
14394                 }else{
14395                     if(k == keyCode){
14396                         fn.call(scope || window, dlg, k, e);
14397                     }
14398                 }
14399             }
14400         };
14401         this.on("keydown", handler);
14402         return this;
14403     },
14404
14405     /**
14406      * Returns the TabPanel component (creates it if it doesn't exist).
14407      * Note: If you wish to simply check for the existence of tabs without creating them,
14408      * check for a null 'tabs' property.
14409      * @return {Roo.TabPanel} The tabs component
14410      */
14411     getTabs : function(){
14412         if(!this.tabs){
14413             this.el.addClass("x-dlg-auto-tabs");
14414             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14415             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14416         }
14417         return this.tabs;
14418     },
14419
14420     /**
14421      * Adds a button to the footer section of the dialog.
14422      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14423      * object or a valid Roo.DomHelper element config
14424      * @param {Function} handler The function called when the button is clicked
14425      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14426      * @return {Roo.Button} The new button
14427      */
14428     addButton : function(config, handler, scope){
14429         var dh = Roo.DomHelper;
14430         if(!this.footer){
14431             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14432         }
14433         if(!this.btnContainer){
14434             var tb = this.footer.createChild({
14435
14436                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14437                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14438             }, null, true);
14439             this.btnContainer = tb.firstChild.firstChild.firstChild;
14440         }
14441         var bconfig = {
14442             handler: handler,
14443             scope: scope,
14444             minWidth: this.minButtonWidth,
14445             hideParent:true
14446         };
14447         if(typeof config == "string"){
14448             bconfig.text = config;
14449         }else{
14450             if(config.tag){
14451                 bconfig.dhconfig = config;
14452             }else{
14453                 Roo.apply(bconfig, config);
14454             }
14455         }
14456         var fc = false;
14457         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14458             bconfig.position = Math.max(0, bconfig.position);
14459             fc = this.btnContainer.childNodes[bconfig.position];
14460         }
14461          
14462         var btn = new Roo.Button(
14463             fc ? 
14464                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14465                 : this.btnContainer.appendChild(document.createElement("td")),
14466             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14467             bconfig
14468         );
14469         this.syncBodyHeight();
14470         if(!this.buttons){
14471             /**
14472              * Array of all the buttons that have been added to this dialog via addButton
14473              * @type Array
14474              */
14475             this.buttons = [];
14476         }
14477         this.buttons.push(btn);
14478         return btn;
14479     },
14480
14481     /**
14482      * Sets the default button to be focused when the dialog is displayed.
14483      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14484      * @return {Roo.BasicDialog} this
14485      */
14486     setDefaultButton : function(btn){
14487         this.defaultButton = btn;
14488         return this;
14489     },
14490
14491     // private
14492     getHeaderFooterHeight : function(safe){
14493         var height = 0;
14494         if(this.header){
14495            height += this.header.getHeight();
14496         }
14497         if(this.footer){
14498            var fm = this.footer.getMargins();
14499             height += (this.footer.getHeight()+fm.top+fm.bottom);
14500         }
14501         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14502         height += this.centerBg.getPadding("tb");
14503         return height;
14504     },
14505
14506     // private
14507     syncBodyHeight : function(){
14508         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14509         var height = this.size.height - this.getHeaderFooterHeight(false);
14510         bd.setHeight(height-bd.getMargins("tb"));
14511         var hh = this.header.getHeight();
14512         var h = this.size.height-hh;
14513         cb.setHeight(h);
14514         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14515         bw.setHeight(h-cb.getPadding("tb"));
14516         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14517         bd.setWidth(bw.getWidth(true));
14518         if(this.tabs){
14519             this.tabs.syncHeight();
14520             if(Roo.isIE){
14521                 this.tabs.el.repaint();
14522             }
14523         }
14524     },
14525
14526     /**
14527      * Restores the previous state of the dialog if Roo.state is configured.
14528      * @return {Roo.BasicDialog} this
14529      */
14530     restoreState : function(){
14531         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14532         if(box && box.width){
14533             this.xy = [box.x, box.y];
14534             this.resizeTo(box.width, box.height);
14535         }
14536         return this;
14537     },
14538
14539     // private
14540     beforeShow : function(){
14541         this.expand();
14542         if(this.fixedcenter){
14543             this.xy = this.el.getCenterXY(true);
14544         }
14545         if(this.modal){
14546             Roo.get(document.body).addClass("x-body-masked");
14547             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14548             this.mask.show();
14549         }
14550         this.constrainXY();
14551     },
14552
14553     // private
14554     animShow : function(){
14555         var b = Roo.get(this.animateTarget, true).getBox();
14556         this.proxy.setSize(b.width, b.height);
14557         this.proxy.setLocation(b.x, b.y);
14558         this.proxy.show();
14559         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14560                     true, .35, this.showEl.createDelegate(this));
14561     },
14562
14563     /**
14564      * Shows the dialog.
14565      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14566      * @return {Roo.BasicDialog} this
14567      */
14568     show : function(animateTarget){
14569         if (this.fireEvent("beforeshow", this) === false){
14570             return;
14571         }
14572         if(this.syncHeightBeforeShow){
14573             this.syncBodyHeight();
14574         }else if(this.firstShow){
14575             this.firstShow = false;
14576             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14577         }
14578         this.animateTarget = animateTarget || this.animateTarget;
14579         if(!this.el.isVisible()){
14580             this.beforeShow();
14581             if(this.animateTarget){
14582                 this.animShow();
14583             }else{
14584                 this.showEl();
14585             }
14586         }
14587         return this;
14588     },
14589
14590     // private
14591     showEl : function(){
14592         this.proxy.hide();
14593         this.el.setXY(this.xy);
14594         this.el.show();
14595         this.adjustAssets(true);
14596         this.toFront();
14597         this.focus();
14598         // IE peekaboo bug - fix found by Dave Fenwick
14599         if(Roo.isIE){
14600             this.el.repaint();
14601         }
14602         this.fireEvent("show", this);
14603     },
14604
14605     /**
14606      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14607      * dialog itself will receive focus.
14608      */
14609     focus : function(){
14610         if(this.defaultButton){
14611             this.defaultButton.focus();
14612         }else{
14613             this.focusEl.focus();
14614         }
14615     },
14616
14617     // private
14618     constrainXY : function(){
14619         if(this.constraintoviewport !== false){
14620             if(!this.viewSize){
14621                 if(this.container){
14622                     var s = this.container.getSize();
14623                     this.viewSize = [s.width, s.height];
14624                 }else{
14625                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14626                 }
14627             }
14628             var s = Roo.get(this.container||document).getScroll();
14629
14630             var x = this.xy[0], y = this.xy[1];
14631             var w = this.size.width, h = this.size.height;
14632             var vw = this.viewSize[0], vh = this.viewSize[1];
14633             // only move it if it needs it
14634             var moved = false;
14635             // first validate right/bottom
14636             if(x + w > vw+s.left){
14637                 x = vw - w;
14638                 moved = true;
14639             }
14640             if(y + h > vh+s.top){
14641                 y = vh - h;
14642                 moved = true;
14643             }
14644             // then make sure top/left isn't negative
14645             if(x < s.left){
14646                 x = s.left;
14647                 moved = true;
14648             }
14649             if(y < s.top){
14650                 y = s.top;
14651                 moved = true;
14652             }
14653             if(moved){
14654                 // cache xy
14655                 this.xy = [x, y];
14656                 if(this.isVisible()){
14657                     this.el.setLocation(x, y);
14658                     this.adjustAssets();
14659                 }
14660             }
14661         }
14662     },
14663
14664     // private
14665     onDrag : function(){
14666         if(!this.proxyDrag){
14667             this.xy = this.el.getXY();
14668             this.adjustAssets();
14669         }
14670     },
14671
14672     // private
14673     adjustAssets : function(doShow){
14674         var x = this.xy[0], y = this.xy[1];
14675         var w = this.size.width, h = this.size.height;
14676         if(doShow === true){
14677             if(this.shadow){
14678                 this.shadow.show(this.el);
14679             }
14680             if(this.shim){
14681                 this.shim.show();
14682             }
14683         }
14684         if(this.shadow && this.shadow.isVisible()){
14685             this.shadow.show(this.el);
14686         }
14687         if(this.shim && this.shim.isVisible()){
14688             this.shim.setBounds(x, y, w, h);
14689         }
14690     },
14691
14692     // private
14693     adjustViewport : function(w, h){
14694         if(!w || !h){
14695             w = Roo.lib.Dom.getViewWidth();
14696             h = Roo.lib.Dom.getViewHeight();
14697         }
14698         // cache the size
14699         this.viewSize = [w, h];
14700         if(this.modal && this.mask.isVisible()){
14701             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14702             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14703         }
14704         if(this.isVisible()){
14705             this.constrainXY();
14706         }
14707     },
14708
14709     /**
14710      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14711      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14712      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14713      */
14714     destroy : function(removeEl){
14715         if(this.isVisible()){
14716             this.animateTarget = null;
14717             this.hide();
14718         }
14719         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14720         if(this.tabs){
14721             this.tabs.destroy(removeEl);
14722         }
14723         Roo.destroy(
14724              this.shim,
14725              this.proxy,
14726              this.resizer,
14727              this.close,
14728              this.mask
14729         );
14730         if(this.dd){
14731             this.dd.unreg();
14732         }
14733         if(this.buttons){
14734            for(var i = 0, len = this.buttons.length; i < len; i++){
14735                this.buttons[i].destroy();
14736            }
14737         }
14738         this.el.removeAllListeners();
14739         if(removeEl === true){
14740             this.el.update("");
14741             this.el.remove();
14742         }
14743         Roo.DialogManager.unregister(this);
14744     },
14745
14746     // private
14747     startMove : function(){
14748         if(this.proxyDrag){
14749             this.proxy.show();
14750         }
14751         if(this.constraintoviewport !== false){
14752             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14753         }
14754     },
14755
14756     // private
14757     endMove : function(){
14758         if(!this.proxyDrag){
14759             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14760         }else{
14761             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14762             this.proxy.hide();
14763         }
14764         this.refreshSize();
14765         this.adjustAssets();
14766         this.focus();
14767         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14768     },
14769
14770     /**
14771      * Brings this dialog to the front of any other visible dialogs
14772      * @return {Roo.BasicDialog} this
14773      */
14774     toFront : function(){
14775         Roo.DialogManager.bringToFront(this);
14776         return this;
14777     },
14778
14779     /**
14780      * Sends this dialog to the back (under) of any other visible dialogs
14781      * @return {Roo.BasicDialog} this
14782      */
14783     toBack : function(){
14784         Roo.DialogManager.sendToBack(this);
14785         return this;
14786     },
14787
14788     /**
14789      * Centers this dialog in the viewport
14790      * @return {Roo.BasicDialog} this
14791      */
14792     center : function(){
14793         var xy = this.el.getCenterXY(true);
14794         this.moveTo(xy[0], xy[1]);
14795         return this;
14796     },
14797
14798     /**
14799      * Moves the dialog's top-left corner to the specified point
14800      * @param {Number} x
14801      * @param {Number} y
14802      * @return {Roo.BasicDialog} this
14803      */
14804     moveTo : function(x, y){
14805         this.xy = [x,y];
14806         if(this.isVisible()){
14807             this.el.setXY(this.xy);
14808             this.adjustAssets();
14809         }
14810         return this;
14811     },
14812
14813     /**
14814      * Aligns the dialog to the specified element
14815      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14816      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14817      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14818      * @return {Roo.BasicDialog} this
14819      */
14820     alignTo : function(element, position, offsets){
14821         this.xy = this.el.getAlignToXY(element, position, offsets);
14822         if(this.isVisible()){
14823             this.el.setXY(this.xy);
14824             this.adjustAssets();
14825         }
14826         return this;
14827     },
14828
14829     /**
14830      * Anchors an element to another element and realigns it when the window is resized.
14831      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14832      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14833      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14834      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14835      * is a number, it is used as the buffer delay (defaults to 50ms).
14836      * @return {Roo.BasicDialog} this
14837      */
14838     anchorTo : function(el, alignment, offsets, monitorScroll){
14839         var action = function(){
14840             this.alignTo(el, alignment, offsets);
14841         };
14842         Roo.EventManager.onWindowResize(action, this);
14843         var tm = typeof monitorScroll;
14844         if(tm != 'undefined'){
14845             Roo.EventManager.on(window, 'scroll', action, this,
14846                 {buffer: tm == 'number' ? monitorScroll : 50});
14847         }
14848         action.call(this);
14849         return this;
14850     },
14851
14852     /**
14853      * Returns true if the dialog is visible
14854      * @return {Boolean}
14855      */
14856     isVisible : function(){
14857         return this.el.isVisible();
14858     },
14859
14860     // private
14861     animHide : function(callback){
14862         var b = Roo.get(this.animateTarget).getBox();
14863         this.proxy.show();
14864         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14865         this.el.hide();
14866         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14867                     this.hideEl.createDelegate(this, [callback]));
14868     },
14869
14870     /**
14871      * Hides the dialog.
14872      * @param {Function} callback (optional) Function to call when the dialog is hidden
14873      * @return {Roo.BasicDialog} this
14874      */
14875     hide : function(callback){
14876         if (this.fireEvent("beforehide", this) === false){
14877             return;
14878         }
14879         if(this.shadow){
14880             this.shadow.hide();
14881         }
14882         if(this.shim) {
14883           this.shim.hide();
14884         }
14885         if(this.animateTarget){
14886            this.animHide(callback);
14887         }else{
14888             this.el.hide();
14889             this.hideEl(callback);
14890         }
14891         return this;
14892     },
14893
14894     // private
14895     hideEl : function(callback){
14896         this.proxy.hide();
14897         if(this.modal){
14898             this.mask.hide();
14899             Roo.get(document.body).removeClass("x-body-masked");
14900         }
14901         this.fireEvent("hide", this);
14902         if(typeof callback == "function"){
14903             callback();
14904         }
14905     },
14906
14907     // private
14908     hideAction : function(){
14909         this.setLeft("-10000px");
14910         this.setTop("-10000px");
14911         this.setStyle("visibility", "hidden");
14912     },
14913
14914     // private
14915     refreshSize : function(){
14916         this.size = this.el.getSize();
14917         this.xy = this.el.getXY();
14918         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14919     },
14920
14921     // private
14922     // z-index is managed by the DialogManager and may be overwritten at any time
14923     setZIndex : function(index){
14924         if(this.modal){
14925             this.mask.setStyle("z-index", index);
14926         }
14927         if(this.shim){
14928             this.shim.setStyle("z-index", ++index);
14929         }
14930         if(this.shadow){
14931             this.shadow.setZIndex(++index);
14932         }
14933         this.el.setStyle("z-index", ++index);
14934         if(this.proxy){
14935             this.proxy.setStyle("z-index", ++index);
14936         }
14937         if(this.resizer){
14938             this.resizer.proxy.setStyle("z-index", ++index);
14939         }
14940
14941         this.lastZIndex = index;
14942     },
14943
14944     /**
14945      * Returns the element for this dialog
14946      * @return {Roo.Element} The underlying dialog Element
14947      */
14948     getEl : function(){
14949         return this.el;
14950     }
14951 });
14952
14953 /**
14954  * @class Roo.DialogManager
14955  * Provides global access to BasicDialogs that have been created and
14956  * support for z-indexing (layering) multiple open dialogs.
14957  */
14958 Roo.DialogManager = function(){
14959     var list = {};
14960     var accessList = [];
14961     var front = null;
14962
14963     // private
14964     var sortDialogs = function(d1, d2){
14965         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
14966     };
14967
14968     // private
14969     var orderDialogs = function(){
14970         accessList.sort(sortDialogs);
14971         var seed = Roo.DialogManager.zseed;
14972         for(var i = 0, len = accessList.length; i < len; i++){
14973             var dlg = accessList[i];
14974             if(dlg){
14975                 dlg.setZIndex(seed + (i*10));
14976             }
14977         }
14978     };
14979
14980     return {
14981         /**
14982          * The starting z-index for BasicDialogs (defaults to 9000)
14983          * @type Number The z-index value
14984          */
14985         zseed : 9000,
14986
14987         // private
14988         register : function(dlg){
14989             list[dlg.id] = dlg;
14990             accessList.push(dlg);
14991         },
14992
14993         // private
14994         unregister : function(dlg){
14995             delete list[dlg.id];
14996             var i=0;
14997             var len=0;
14998             if(!accessList.indexOf){
14999                 for(  i = 0, len = accessList.length; i < len; i++){
15000                     if(accessList[i] == dlg){
15001                         accessList.splice(i, 1);
15002                         return;
15003                     }
15004                 }
15005             }else{
15006                  i = accessList.indexOf(dlg);
15007                 if(i != -1){
15008                     accessList.splice(i, 1);
15009                 }
15010             }
15011         },
15012
15013         /**
15014          * Gets a registered dialog by id
15015          * @param {String/Object} id The id of the dialog or a dialog
15016          * @return {Roo.BasicDialog} this
15017          */
15018         get : function(id){
15019             return typeof id == "object" ? id : list[id];
15020         },
15021
15022         /**
15023          * Brings the specified dialog to the front
15024          * @param {String/Object} dlg The id of the dialog or a dialog
15025          * @return {Roo.BasicDialog} this
15026          */
15027         bringToFront : function(dlg){
15028             dlg = this.get(dlg);
15029             if(dlg != front){
15030                 front = dlg;
15031                 dlg._lastAccess = new Date().getTime();
15032                 orderDialogs();
15033             }
15034             return dlg;
15035         },
15036
15037         /**
15038          * Sends the specified dialog to the back
15039          * @param {String/Object} dlg The id of the dialog or a dialog
15040          * @return {Roo.BasicDialog} this
15041          */
15042         sendToBack : function(dlg){
15043             dlg = this.get(dlg);
15044             dlg._lastAccess = -(new Date().getTime());
15045             orderDialogs();
15046             return dlg;
15047         },
15048
15049         /**
15050          * Hides all dialogs
15051          */
15052         hideAll : function(){
15053             for(var id in list){
15054                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15055                     list[id].hide();
15056                 }
15057             }
15058         }
15059     };
15060 }();
15061
15062 /**
15063  * @class Roo.LayoutDialog
15064  * @extends Roo.BasicDialog
15065  * Dialog which provides adjustments for working with a layout in a Dialog.
15066  * Add your necessary layout config options to the dialog's config.<br>
15067  * Example usage (including a nested layout):
15068  * <pre><code>
15069 if(!dialog){
15070     dialog = new Roo.LayoutDialog("download-dlg", {
15071         modal: true,
15072         width:600,
15073         height:450,
15074         shadow:true,
15075         minWidth:500,
15076         minHeight:350,
15077         autoTabs:true,
15078         proxyDrag:true,
15079         // layout config merges with the dialog config
15080         center:{
15081             tabPosition: "top",
15082             alwaysShowTabs: true
15083         }
15084     });
15085     dialog.addKeyListener(27, dialog.hide, dialog);
15086     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15087     dialog.addButton("Build It!", this.getDownload, this);
15088
15089     // we can even add nested layouts
15090     var innerLayout = new Roo.BorderLayout("dl-inner", {
15091         east: {
15092             initialSize: 200,
15093             autoScroll:true,
15094             split:true
15095         },
15096         center: {
15097             autoScroll:true
15098         }
15099     });
15100     innerLayout.beginUpdate();
15101     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15102     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15103     innerLayout.endUpdate(true);
15104
15105     var layout = dialog.getLayout();
15106     layout.beginUpdate();
15107     layout.add("center", new Roo.ContentPanel("standard-panel",
15108                         {title: "Download the Source", fitToFrame:true}));
15109     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15110                {title: "Build your own roo.js"}));
15111     layout.getRegion("center").showPanel(sp);
15112     layout.endUpdate();
15113 }
15114 </code></pre>
15115     * @constructor
15116     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15117     * @param {Object} config configuration options
15118   */
15119 Roo.LayoutDialog = function(el, cfg){
15120     
15121     var config=  cfg;
15122     if (typeof(cfg) == 'undefined') {
15123         config = Roo.apply({}, el);
15124         el = Roo.get( document.documentElement || document.body).createChild();
15125         //config.autoCreate = true;
15126     }
15127     
15128     
15129     config.autoTabs = false;
15130     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15131     this.body.setStyle({overflow:"hidden", position:"relative"});
15132     this.layout = new Roo.BorderLayout(this.body.dom, config);
15133     this.layout.monitorWindowResize = false;
15134     this.el.addClass("x-dlg-auto-layout");
15135     // fix case when center region overwrites center function
15136     this.center = Roo.BasicDialog.prototype.center;
15137     this.on("show", this.layout.layout, this.layout, true);
15138     if (config.items) {
15139         var xitems = config.items;
15140         delete config.items;
15141         Roo.each(xitems, this.addxtype, this);
15142     }
15143     
15144     
15145 };
15146 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15147     /**
15148      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15149      * @deprecated
15150      */
15151     endUpdate : function(){
15152         this.layout.endUpdate();
15153     },
15154
15155     /**
15156      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15157      *  @deprecated
15158      */
15159     beginUpdate : function(){
15160         this.layout.beginUpdate();
15161     },
15162
15163     /**
15164      * Get the BorderLayout for this dialog
15165      * @return {Roo.BorderLayout}
15166      */
15167     getLayout : function(){
15168         return this.layout;
15169     },
15170
15171     showEl : function(){
15172         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15173         if(Roo.isIE7){
15174             this.layout.layout();
15175         }
15176     },
15177
15178     // private
15179     // Use the syncHeightBeforeShow config option to control this automatically
15180     syncBodyHeight : function(){
15181         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15182         if(this.layout){this.layout.layout();}
15183     },
15184     
15185       /**
15186      * Add an xtype element (actually adds to the layout.)
15187      * @return {Object} xdata xtype object data.
15188      */
15189     
15190     addxtype : function(c) {
15191         return this.layout.addxtype(c);
15192     }
15193 });/*
15194  * Based on:
15195  * Ext JS Library 1.1.1
15196  * Copyright(c) 2006-2007, Ext JS, LLC.
15197  *
15198  * Originally Released Under LGPL - original licence link has changed is not relivant.
15199  *
15200  * Fork - LGPL
15201  * <script type="text/javascript">
15202  */
15203  
15204 /**
15205  * @class Roo.MessageBox
15206  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15207  * Example usage:
15208  *<pre><code>
15209 // Basic alert:
15210 Roo.Msg.alert('Status', 'Changes saved successfully.');
15211
15212 // Prompt for user data:
15213 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15214     if (btn == 'ok'){
15215         // process text value...
15216     }
15217 });
15218
15219 // Show a dialog using config options:
15220 Roo.Msg.show({
15221    title:'Save Changes?',
15222    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15223    buttons: Roo.Msg.YESNOCANCEL,
15224    fn: processResult,
15225    animEl: 'elId'
15226 });
15227 </code></pre>
15228  * @singleton
15229  */
15230 Roo.MessageBox = function(){
15231     var dlg, opt, mask, waitTimer;
15232     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15233     var buttons, activeTextEl, bwidth;
15234
15235     // private
15236     var handleButton = function(button){
15237         dlg.hide();
15238         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15239     };
15240
15241     // private
15242     var handleHide = function(){
15243         if(opt && opt.cls){
15244             dlg.el.removeClass(opt.cls);
15245         }
15246         if(waitTimer){
15247             Roo.TaskMgr.stop(waitTimer);
15248             waitTimer = null;
15249         }
15250     };
15251
15252     // private
15253     var updateButtons = function(b){
15254         var width = 0;
15255         if(!b){
15256             buttons["ok"].hide();
15257             buttons["cancel"].hide();
15258             buttons["yes"].hide();
15259             buttons["no"].hide();
15260             dlg.footer.dom.style.display = 'none';
15261             return width;
15262         }
15263         dlg.footer.dom.style.display = '';
15264         for(var k in buttons){
15265             if(typeof buttons[k] != "function"){
15266                 if(b[k]){
15267                     buttons[k].show();
15268                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15269                     width += buttons[k].el.getWidth()+15;
15270                 }else{
15271                     buttons[k].hide();
15272                 }
15273             }
15274         }
15275         return width;
15276     };
15277
15278     // private
15279     var handleEsc = function(d, k, e){
15280         if(opt && opt.closable !== false){
15281             dlg.hide();
15282         }
15283         if(e){
15284             e.stopEvent();
15285         }
15286     };
15287
15288     return {
15289         /**
15290          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15291          * @return {Roo.BasicDialog} The BasicDialog element
15292          */
15293         getDialog : function(){
15294            if(!dlg){
15295                 dlg = new Roo.BasicDialog("x-msg-box", {
15296                     autoCreate : true,
15297                     shadow: true,
15298                     draggable: true,
15299                     resizable:false,
15300                     constraintoviewport:false,
15301                     fixedcenter:true,
15302                     collapsible : false,
15303                     shim:true,
15304                     modal: true,
15305                     width:400, height:100,
15306                     buttonAlign:"center",
15307                     closeClick : function(){
15308                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15309                             handleButton("no");
15310                         }else{
15311                             handleButton("cancel");
15312                         }
15313                     }
15314                 });
15315                 dlg.on("hide", handleHide);
15316                 mask = dlg.mask;
15317                 dlg.addKeyListener(27, handleEsc);
15318                 buttons = {};
15319                 var bt = this.buttonText;
15320                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15321                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15322                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15323                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15324                 bodyEl = dlg.body.createChild({
15325
15326                     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>'
15327                 });
15328                 msgEl = bodyEl.dom.firstChild;
15329                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15330                 textboxEl.enableDisplayMode();
15331                 textboxEl.addKeyListener([10,13], function(){
15332                     if(dlg.isVisible() && opt && opt.buttons){
15333                         if(opt.buttons.ok){
15334                             handleButton("ok");
15335                         }else if(opt.buttons.yes){
15336                             handleButton("yes");
15337                         }
15338                     }
15339                 });
15340                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15341                 textareaEl.enableDisplayMode();
15342                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15343                 progressEl.enableDisplayMode();
15344                 var pf = progressEl.dom.firstChild;
15345                 if (pf) {
15346                     pp = Roo.get(pf.firstChild);
15347                     pp.setHeight(pf.offsetHeight);
15348                 }
15349                 
15350             }
15351             return dlg;
15352         },
15353
15354         /**
15355          * Updates the message box body text
15356          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15357          * the XHTML-compliant non-breaking space character '&amp;#160;')
15358          * @return {Roo.MessageBox} This message box
15359          */
15360         updateText : function(text){
15361             if(!dlg.isVisible() && !opt.width){
15362                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15363             }
15364             msgEl.innerHTML = text || '&#160;';
15365             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15366                         Math.max(opt.minWidth || this.minWidth, bwidth));
15367             if(opt.prompt){
15368                 activeTextEl.setWidth(w);
15369             }
15370             if(dlg.isVisible()){
15371                 dlg.fixedcenter = false;
15372             }
15373             dlg.setContentSize(w, bodyEl.getHeight());
15374             if(dlg.isVisible()){
15375                 dlg.fixedcenter = true;
15376             }
15377             return this;
15378         },
15379
15380         /**
15381          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15382          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15383          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15384          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15385          * @return {Roo.MessageBox} This message box
15386          */
15387         updateProgress : function(value, text){
15388             if(text){
15389                 this.updateText(text);
15390             }
15391             if (pp) { // weird bug on my firefox - for some reason this is not defined
15392                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15393             }
15394             return this;
15395         },        
15396
15397         /**
15398          * Returns true if the message box is currently displayed
15399          * @return {Boolean} True if the message box is visible, else false
15400          */
15401         isVisible : function(){
15402             return dlg && dlg.isVisible();  
15403         },
15404
15405         /**
15406          * Hides the message box if it is displayed
15407          */
15408         hide : function(){
15409             if(this.isVisible()){
15410                 dlg.hide();
15411             }  
15412         },
15413
15414         /**
15415          * Displays a new message box, or reinitializes an existing message box, based on the config options
15416          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15417          * The following config object properties are supported:
15418          * <pre>
15419 Property    Type             Description
15420 ----------  ---------------  ------------------------------------------------------------------------------------
15421 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15422                                    closes (defaults to undefined)
15423 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15424                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15425 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15426                                    progress and wait dialogs will ignore this property and always hide the
15427                                    close button as they can only be closed programmatically.
15428 cls               String           A custom CSS class to apply to the message box element
15429 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15430                                    displayed (defaults to 75)
15431 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15432                                    function will be btn (the name of the button that was clicked, if applicable,
15433                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15434                                    Progress and wait dialogs will ignore this option since they do not respond to
15435                                    user actions and can only be closed programmatically, so any required function
15436                                    should be called by the same code after it closes the dialog.
15437 icon              String           A CSS class that provides a background image to be used as an icon for
15438                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15439 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15440 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15441 modal             Boolean          False to allow user interaction with the page while the message box is
15442                                    displayed (defaults to true)
15443 msg               String           A string that will replace the existing message box body text (defaults
15444                                    to the XHTML-compliant non-breaking space character '&#160;')
15445 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15446 progress          Boolean          True to display a progress bar (defaults to false)
15447 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15448 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15449 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15450 title             String           The title text
15451 value             String           The string value to set into the active textbox element if displayed
15452 wait              Boolean          True to display a progress bar (defaults to false)
15453 width             Number           The width of the dialog in pixels
15454 </pre>
15455          *
15456          * Example usage:
15457          * <pre><code>
15458 Roo.Msg.show({
15459    title: 'Address',
15460    msg: 'Please enter your address:',
15461    width: 300,
15462    buttons: Roo.MessageBox.OKCANCEL,
15463    multiline: true,
15464    fn: saveAddress,
15465    animEl: 'addAddressBtn'
15466 });
15467 </code></pre>
15468          * @param {Object} config Configuration options
15469          * @return {Roo.MessageBox} This message box
15470          */
15471         show : function(options){
15472             if(this.isVisible()){
15473                 this.hide();
15474             }
15475             var d = this.getDialog();
15476             opt = options;
15477             d.setTitle(opt.title || "&#160;");
15478             d.close.setDisplayed(opt.closable !== false);
15479             activeTextEl = textboxEl;
15480             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15481             if(opt.prompt){
15482                 if(opt.multiline){
15483                     textboxEl.hide();
15484                     textareaEl.show();
15485                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15486                         opt.multiline : this.defaultTextHeight);
15487                     activeTextEl = textareaEl;
15488                 }else{
15489                     textboxEl.show();
15490                     textareaEl.hide();
15491                 }
15492             }else{
15493                 textboxEl.hide();
15494                 textareaEl.hide();
15495             }
15496             progressEl.setDisplayed(opt.progress === true);
15497             this.updateProgress(0);
15498             activeTextEl.dom.value = opt.value || "";
15499             if(opt.prompt){
15500                 dlg.setDefaultButton(activeTextEl);
15501             }else{
15502                 var bs = opt.buttons;
15503                 var db = null;
15504                 if(bs && bs.ok){
15505                     db = buttons["ok"];
15506                 }else if(bs && bs.yes){
15507                     db = buttons["yes"];
15508                 }
15509                 dlg.setDefaultButton(db);
15510             }
15511             bwidth = updateButtons(opt.buttons);
15512             this.updateText(opt.msg);
15513             if(opt.cls){
15514                 d.el.addClass(opt.cls);
15515             }
15516             d.proxyDrag = opt.proxyDrag === true;
15517             d.modal = opt.modal !== false;
15518             d.mask = opt.modal !== false ? mask : false;
15519             if(!d.isVisible()){
15520                 // force it to the end of the z-index stack so it gets a cursor in FF
15521                 document.body.appendChild(dlg.el.dom);
15522                 d.animateTarget = null;
15523                 d.show(options.animEl);
15524             }
15525             return this;
15526         },
15527
15528         /**
15529          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15530          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15531          * and closing the message box when the process is complete.
15532          * @param {String} title The title bar text
15533          * @param {String} msg The message box body text
15534          * @return {Roo.MessageBox} This message box
15535          */
15536         progress : function(title, msg){
15537             this.show({
15538                 title : title,
15539                 msg : msg,
15540                 buttons: false,
15541                 progress:true,
15542                 closable:false,
15543                 minWidth: this.minProgressWidth,
15544                 modal : true
15545             });
15546             return this;
15547         },
15548
15549         /**
15550          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15551          * If a callback function is passed it will be called after the user clicks the button, and the
15552          * id of the button that was clicked will be passed as the only parameter to the callback
15553          * (could also be the top-right close button).
15554          * @param {String} title The title bar text
15555          * @param {String} msg The message box body text
15556          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15557          * @param {Object} scope (optional) The scope of the callback function
15558          * @return {Roo.MessageBox} This message box
15559          */
15560         alert : function(title, msg, fn, scope){
15561             this.show({
15562                 title : title,
15563                 msg : msg,
15564                 buttons: this.OK,
15565                 fn: fn,
15566                 scope : scope,
15567                 modal : true
15568             });
15569             return this;
15570         },
15571
15572         /**
15573          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15574          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15575          * You are responsible for closing the message box when the process is complete.
15576          * @param {String} msg The message box body text
15577          * @param {String} title (optional) The title bar text
15578          * @return {Roo.MessageBox} This message box
15579          */
15580         wait : function(msg, title){
15581             this.show({
15582                 title : title,
15583                 msg : msg,
15584                 buttons: false,
15585                 closable:false,
15586                 progress:true,
15587                 modal:true,
15588                 width:300,
15589                 wait:true
15590             });
15591             waitTimer = Roo.TaskMgr.start({
15592                 run: function(i){
15593                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15594                 },
15595                 interval: 1000
15596             });
15597             return this;
15598         },
15599
15600         /**
15601          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15602          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15603          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15604          * @param {String} title The title bar text
15605          * @param {String} msg The message box body text
15606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15607          * @param {Object} scope (optional) The scope of the callback function
15608          * @return {Roo.MessageBox} This message box
15609          */
15610         confirm : function(title, msg, fn, scope){
15611             this.show({
15612                 title : title,
15613                 msg : msg,
15614                 buttons: this.YESNO,
15615                 fn: fn,
15616                 scope : scope,
15617                 modal : true
15618             });
15619             return this;
15620         },
15621
15622         /**
15623          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15624          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15625          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15626          * (could also be the top-right close button) and the text that was entered will be passed as the two
15627          * parameters to the callback.
15628          * @param {String} title The title bar text
15629          * @param {String} msg The message box body text
15630          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15631          * @param {Object} scope (optional) The scope of the callback function
15632          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15633          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15634          * @return {Roo.MessageBox} This message box
15635          */
15636         prompt : function(title, msg, fn, scope, multiline){
15637             this.show({
15638                 title : title,
15639                 msg : msg,
15640                 buttons: this.OKCANCEL,
15641                 fn: fn,
15642                 minWidth:250,
15643                 scope : scope,
15644                 prompt:true,
15645                 multiline: multiline,
15646                 modal : true
15647             });
15648             return this;
15649         },
15650
15651         /**
15652          * Button config that displays a single OK button
15653          * @type Object
15654          */
15655         OK : {ok:true},
15656         /**
15657          * Button config that displays Yes and No buttons
15658          * @type Object
15659          */
15660         YESNO : {yes:true, no:true},
15661         /**
15662          * Button config that displays OK and Cancel buttons
15663          * @type Object
15664          */
15665         OKCANCEL : {ok:true, cancel:true},
15666         /**
15667          * Button config that displays Yes, No and Cancel buttons
15668          * @type Object
15669          */
15670         YESNOCANCEL : {yes:true, no:true, cancel:true},
15671
15672         /**
15673          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15674          * @type Number
15675          */
15676         defaultTextHeight : 75,
15677         /**
15678          * The maximum width in pixels of the message box (defaults to 600)
15679          * @type Number
15680          */
15681         maxWidth : 600,
15682         /**
15683          * The minimum width in pixels of the message box (defaults to 100)
15684          * @type Number
15685          */
15686         minWidth : 100,
15687         /**
15688          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15689          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15690          * @type Number
15691          */
15692         minProgressWidth : 250,
15693         /**
15694          * An object containing the default button text strings that can be overriden for localized language support.
15695          * Supported properties are: ok, cancel, yes and no.
15696          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15697          * @type Object
15698          */
15699         buttonText : {
15700             ok : "OK",
15701             cancel : "Cancel",
15702             yes : "Yes",
15703             no : "No"
15704         }
15705     };
15706 }();
15707
15708 /**
15709  * Shorthand for {@link Roo.MessageBox}
15710  */
15711 Roo.Msg = Roo.MessageBox;/*
15712  * Based on:
15713  * Ext JS Library 1.1.1
15714  * Copyright(c) 2006-2007, Ext JS, LLC.
15715  *
15716  * Originally Released Under LGPL - original licence link has changed is not relivant.
15717  *
15718  * Fork - LGPL
15719  * <script type="text/javascript">
15720  */
15721 /**
15722  * @class Roo.QuickTips
15723  * Provides attractive and customizable tooltips for any element.
15724  * @singleton
15725  */
15726 Roo.QuickTips = function(){
15727     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15728     var ce, bd, xy, dd;
15729     var visible = false, disabled = true, inited = false;
15730     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15731     
15732     var onOver = function(e){
15733         if(disabled){
15734             return;
15735         }
15736         var t = e.getTarget();
15737         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15738             return;
15739         }
15740         if(ce && t == ce.el){
15741             clearTimeout(hideProc);
15742             return;
15743         }
15744         if(t && tagEls[t.id]){
15745             tagEls[t.id].el = t;
15746             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15747             return;
15748         }
15749         var ttp, et = Roo.fly(t);
15750         var ns = cfg.namespace;
15751         if(tm.interceptTitles && t.title){
15752             ttp = t.title;
15753             t.qtip = ttp;
15754             t.removeAttribute("title");
15755             e.preventDefault();
15756         }else{
15757             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15758         }
15759         if(ttp){
15760             showProc = show.defer(tm.showDelay, tm, [{
15761                 el: t, 
15762                 text: ttp, 
15763                 width: et.getAttributeNS(ns, cfg.width),
15764                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15765                 title: et.getAttributeNS(ns, cfg.title),
15766                     cls: et.getAttributeNS(ns, cfg.cls)
15767             }]);
15768         }
15769     };
15770     
15771     var onOut = function(e){
15772         clearTimeout(showProc);
15773         var t = e.getTarget();
15774         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15775             hideProc = setTimeout(hide, tm.hideDelay);
15776         }
15777     };
15778     
15779     var onMove = function(e){
15780         if(disabled){
15781             return;
15782         }
15783         xy = e.getXY();
15784         xy[1] += 18;
15785         if(tm.trackMouse && ce){
15786             el.setXY(xy);
15787         }
15788     };
15789     
15790     var onDown = function(e){
15791         clearTimeout(showProc);
15792         clearTimeout(hideProc);
15793         if(!e.within(el)){
15794             if(tm.hideOnClick){
15795                 hide();
15796                 tm.disable();
15797                 tm.enable.defer(100, tm);
15798             }
15799         }
15800     };
15801     
15802     var getPad = function(){
15803         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15804     };
15805
15806     var show = function(o){
15807         if(disabled){
15808             return;
15809         }
15810         clearTimeout(dismissProc);
15811         ce = o;
15812         if(removeCls){ // in case manually hidden
15813             el.removeClass(removeCls);
15814             removeCls = null;
15815         }
15816         if(ce.cls){
15817             el.addClass(ce.cls);
15818             removeCls = ce.cls;
15819         }
15820         if(ce.title){
15821             tipTitle.update(ce.title);
15822             tipTitle.show();
15823         }else{
15824             tipTitle.update('');
15825             tipTitle.hide();
15826         }
15827         el.dom.style.width  = tm.maxWidth+'px';
15828         //tipBody.dom.style.width = '';
15829         tipBodyText.update(o.text);
15830         var p = getPad(), w = ce.width;
15831         if(!w){
15832             var td = tipBodyText.dom;
15833             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15834             if(aw > tm.maxWidth){
15835                 w = tm.maxWidth;
15836             }else if(aw < tm.minWidth){
15837                 w = tm.minWidth;
15838             }else{
15839                 w = aw;
15840             }
15841         }
15842         //tipBody.setWidth(w);
15843         el.setWidth(parseInt(w, 10) + p);
15844         if(ce.autoHide === false){
15845             close.setDisplayed(true);
15846             if(dd){
15847                 dd.unlock();
15848             }
15849         }else{
15850             close.setDisplayed(false);
15851             if(dd){
15852                 dd.lock();
15853             }
15854         }
15855         if(xy){
15856             el.avoidY = xy[1]-18;
15857             el.setXY(xy);
15858         }
15859         if(tm.animate){
15860             el.setOpacity(.1);
15861             el.setStyle("visibility", "visible");
15862             el.fadeIn({callback: afterShow});
15863         }else{
15864             afterShow();
15865         }
15866     };
15867     
15868     var afterShow = function(){
15869         if(ce){
15870             el.show();
15871             esc.enable();
15872             if(tm.autoDismiss && ce.autoHide !== false){
15873                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15874             }
15875         }
15876     };
15877     
15878     var hide = function(noanim){
15879         clearTimeout(dismissProc);
15880         clearTimeout(hideProc);
15881         ce = null;
15882         if(el.isVisible()){
15883             esc.disable();
15884             if(noanim !== true && tm.animate){
15885                 el.fadeOut({callback: afterHide});
15886             }else{
15887                 afterHide();
15888             } 
15889         }
15890     };
15891     
15892     var afterHide = function(){
15893         el.hide();
15894         if(removeCls){
15895             el.removeClass(removeCls);
15896             removeCls = null;
15897         }
15898     };
15899     
15900     return {
15901         /**
15902         * @cfg {Number} minWidth
15903         * The minimum width of the quick tip (defaults to 40)
15904         */
15905        minWidth : 40,
15906         /**
15907         * @cfg {Number} maxWidth
15908         * The maximum width of the quick tip (defaults to 300)
15909         */
15910        maxWidth : 300,
15911         /**
15912         * @cfg {Boolean} interceptTitles
15913         * True to automatically use the element's DOM title value if available (defaults to false)
15914         */
15915        interceptTitles : false,
15916         /**
15917         * @cfg {Boolean} trackMouse
15918         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15919         */
15920        trackMouse : false,
15921         /**
15922         * @cfg {Boolean} hideOnClick
15923         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15924         */
15925        hideOnClick : true,
15926         /**
15927         * @cfg {Number} showDelay
15928         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15929         */
15930        showDelay : 500,
15931         /**
15932         * @cfg {Number} hideDelay
15933         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15934         */
15935        hideDelay : 200,
15936         /**
15937         * @cfg {Boolean} autoHide
15938         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
15939         * Used in conjunction with hideDelay.
15940         */
15941        autoHide : true,
15942         /**
15943         * @cfg {Boolean}
15944         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
15945         * (defaults to true).  Used in conjunction with autoDismissDelay.
15946         */
15947        autoDismiss : true,
15948         /**
15949         * @cfg {Number}
15950         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
15951         */
15952        autoDismissDelay : 5000,
15953        /**
15954         * @cfg {Boolean} animate
15955         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
15956         */
15957        animate : false,
15958
15959        /**
15960         * @cfg {String} title
15961         * Title text to display (defaults to '').  This can be any valid HTML markup.
15962         */
15963         title: '',
15964        /**
15965         * @cfg {String} text
15966         * Body text to display (defaults to '').  This can be any valid HTML markup.
15967         */
15968         text : '',
15969        /**
15970         * @cfg {String} cls
15971         * A CSS class to apply to the base quick tip element (defaults to '').
15972         */
15973         cls : '',
15974        /**
15975         * @cfg {Number} width
15976         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
15977         * minWidth or maxWidth.
15978         */
15979         width : null,
15980
15981     /**
15982      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
15983      * or display QuickTips in a page.
15984      */
15985        init : function(){
15986           tm = Roo.QuickTips;
15987           cfg = tm.tagConfig;
15988           if(!inited){
15989               if(!Roo.isReady){ // allow calling of init() before onReady
15990                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
15991                   return;
15992               }
15993               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
15994               el.fxDefaults = {stopFx: true};
15995               // maximum custom styling
15996               //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>');
15997               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>');              
15998               tipTitle = el.child('h3');
15999               tipTitle.enableDisplayMode("block");
16000               tipBody = el.child('div.x-tip-bd');
16001               tipBodyText = el.child('div.x-tip-bd-inner');
16002               //bdLeft = el.child('div.x-tip-bd-left');
16003               //bdRight = el.child('div.x-tip-bd-right');
16004               close = el.child('div.x-tip-close');
16005               close.enableDisplayMode("block");
16006               close.on("click", hide);
16007               var d = Roo.get(document);
16008               d.on("mousedown", onDown);
16009               d.on("mouseover", onOver);
16010               d.on("mouseout", onOut);
16011               d.on("mousemove", onMove);
16012               esc = d.addKeyListener(27, hide);
16013               esc.disable();
16014               if(Roo.dd.DD){
16015                   dd = el.initDD("default", null, {
16016                       onDrag : function(){
16017                           el.sync();  
16018                       }
16019                   });
16020                   dd.setHandleElId(tipTitle.id);
16021                   dd.lock();
16022               }
16023               inited = true;
16024           }
16025           this.enable(); 
16026        },
16027
16028     /**
16029      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16030      * are supported:
16031      * <pre>
16032 Property    Type                   Description
16033 ----------  ---------------------  ------------------------------------------------------------------------
16034 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16035      * </ul>
16036      * @param {Object} config The config object
16037      */
16038        register : function(config){
16039            var cs = config instanceof Array ? config : arguments;
16040            for(var i = 0, len = cs.length; i < len; i++) {
16041                var c = cs[i];
16042                var target = c.target;
16043                if(target){
16044                    if(target instanceof Array){
16045                        for(var j = 0, jlen = target.length; j < jlen; j++){
16046                            tagEls[target[j]] = c;
16047                        }
16048                    }else{
16049                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16050                    }
16051                }
16052            }
16053        },
16054
16055     /**
16056      * Removes this quick tip from its element and destroys it.
16057      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16058      */
16059        unregister : function(el){
16060            delete tagEls[Roo.id(el)];
16061        },
16062
16063     /**
16064      * Enable this quick tip.
16065      */
16066        enable : function(){
16067            if(inited && disabled){
16068                locks.pop();
16069                if(locks.length < 1){
16070                    disabled = false;
16071                }
16072            }
16073        },
16074
16075     /**
16076      * Disable this quick tip.
16077      */
16078        disable : function(){
16079           disabled = true;
16080           clearTimeout(showProc);
16081           clearTimeout(hideProc);
16082           clearTimeout(dismissProc);
16083           if(ce){
16084               hide(true);
16085           }
16086           locks.push(1);
16087        },
16088
16089     /**
16090      * Returns true if the quick tip is enabled, else false.
16091      */
16092        isEnabled : function(){
16093             return !disabled;
16094        },
16095
16096         // private
16097        tagConfig : {
16098            namespace : "ext",
16099            attribute : "qtip",
16100            width : "width",
16101            target : "target",
16102            title : "qtitle",
16103            hide : "hide",
16104            cls : "qclass"
16105        }
16106    };
16107 }();
16108
16109 // backwards compat
16110 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16111  * Based on:
16112  * Ext JS Library 1.1.1
16113  * Copyright(c) 2006-2007, Ext JS, LLC.
16114  *
16115  * Originally Released Under LGPL - original licence link has changed is not relivant.
16116  *
16117  * Fork - LGPL
16118  * <script type="text/javascript">
16119  */
16120  
16121
16122 /**
16123  * @class Roo.tree.TreePanel
16124  * @extends Roo.data.Tree
16125
16126  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16127  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16128  * @cfg {Boolean} enableDD true to enable drag and drop
16129  * @cfg {Boolean} enableDrag true to enable just drag
16130  * @cfg {Boolean} enableDrop true to enable just drop
16131  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16132  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16133  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16134  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16135  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16136  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16137  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16138  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16139  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16140  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16141  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16142  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16143  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16144  * @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>
16145  * @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>
16146  * 
16147  * @constructor
16148  * @param {String/HTMLElement/Element} el The container element
16149  * @param {Object} config
16150  */
16151 Roo.tree.TreePanel = function(el, config){
16152     var root = false;
16153     var loader = false;
16154     if (config.root) {
16155         root = config.root;
16156         delete config.root;
16157     }
16158     if (config.loader) {
16159         loader = config.loader;
16160         delete config.loader;
16161     }
16162     
16163     Roo.apply(this, config);
16164     Roo.tree.TreePanel.superclass.constructor.call(this);
16165     this.el = Roo.get(el);
16166     this.el.addClass('x-tree');
16167     //console.log(root);
16168     if (root) {
16169         this.setRootNode( Roo.factory(root, Roo.tree));
16170     }
16171     if (loader) {
16172         this.loader = Roo.factory(loader, Roo.tree);
16173     }
16174    /**
16175     * Read-only. The id of the container element becomes this TreePanel's id.
16176     */
16177    this.id = this.el.id;
16178    this.addEvents({
16179         /**
16180         * @event beforeload
16181         * Fires before a node is loaded, return false to cancel
16182         * @param {Node} node The node being loaded
16183         */
16184         "beforeload" : true,
16185         /**
16186         * @event load
16187         * Fires when a node is loaded
16188         * @param {Node} node The node that was loaded
16189         */
16190         "load" : true,
16191         /**
16192         * @event textchange
16193         * Fires when the text for a node is changed
16194         * @param {Node} node The node
16195         * @param {String} text The new text
16196         * @param {String} oldText The old text
16197         */
16198         "textchange" : true,
16199         /**
16200         * @event beforeexpand
16201         * Fires before a node is expanded, return false to cancel.
16202         * @param {Node} node The node
16203         * @param {Boolean} deep
16204         * @param {Boolean} anim
16205         */
16206         "beforeexpand" : true,
16207         /**
16208         * @event beforecollapse
16209         * Fires before a node is collapsed, return false to cancel.
16210         * @param {Node} node The node
16211         * @param {Boolean} deep
16212         * @param {Boolean} anim
16213         */
16214         "beforecollapse" : true,
16215         /**
16216         * @event expand
16217         * Fires when a node is expanded
16218         * @param {Node} node The node
16219         */
16220         "expand" : true,
16221         /**
16222         * @event disabledchange
16223         * Fires when the disabled status of a node changes
16224         * @param {Node} node The node
16225         * @param {Boolean} disabled
16226         */
16227         "disabledchange" : true,
16228         /**
16229         * @event collapse
16230         * Fires when a node is collapsed
16231         * @param {Node} node The node
16232         */
16233         "collapse" : true,
16234         /**
16235         * @event beforeclick
16236         * Fires before click processing on a node. Return false to cancel the default action.
16237         * @param {Node} node The node
16238         * @param {Roo.EventObject} e The event object
16239         */
16240         "beforeclick":true,
16241         /**
16242         * @event checkchange
16243         * Fires when a node with a checkbox's checked property changes
16244         * @param {Node} this This node
16245         * @param {Boolean} checked
16246         */
16247         "checkchange":true,
16248         /**
16249         * @event click
16250         * Fires when a node is clicked
16251         * @param {Node} node The node
16252         * @param {Roo.EventObject} e The event object
16253         */
16254         "click":true,
16255         /**
16256         * @event dblclick
16257         * Fires when a node is double clicked
16258         * @param {Node} node The node
16259         * @param {Roo.EventObject} e The event object
16260         */
16261         "dblclick":true,
16262         /**
16263         * @event contextmenu
16264         * Fires when a node is right clicked
16265         * @param {Node} node The node
16266         * @param {Roo.EventObject} e The event object
16267         */
16268         "contextmenu":true,
16269         /**
16270         * @event beforechildrenrendered
16271         * Fires right before the child nodes for a node are rendered
16272         * @param {Node} node The node
16273         */
16274         "beforechildrenrendered":true,
16275        /**
16276              * @event startdrag
16277              * Fires when a node starts being dragged
16278              * @param {Roo.tree.TreePanel} this
16279              * @param {Roo.tree.TreeNode} node
16280              * @param {event} e The raw browser event
16281              */ 
16282             "startdrag" : true,
16283             /**
16284              * @event enddrag
16285              * Fires when a drag operation is complete
16286              * @param {Roo.tree.TreePanel} this
16287              * @param {Roo.tree.TreeNode} node
16288              * @param {event} e The raw browser event
16289              */
16290             "enddrag" : true,
16291             /**
16292              * @event dragdrop
16293              * Fires when a dragged node is dropped on a valid DD target
16294              * @param {Roo.tree.TreePanel} this
16295              * @param {Roo.tree.TreeNode} node
16296              * @param {DD} dd The dd it was dropped on
16297              * @param {event} e The raw browser event
16298              */
16299             "dragdrop" : true,
16300             /**
16301              * @event beforenodedrop
16302              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16303              * passed to handlers has the following properties:<br />
16304              * <ul style="padding:5px;padding-left:16px;">
16305              * <li>tree - The TreePanel</li>
16306              * <li>target - The node being targeted for the drop</li>
16307              * <li>data - The drag data from the drag source</li>
16308              * <li>point - The point of the drop - append, above or below</li>
16309              * <li>source - The drag source</li>
16310              * <li>rawEvent - Raw mouse event</li>
16311              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16312              * to be inserted by setting them on this object.</li>
16313              * <li>cancel - Set this to true to cancel the drop.</li>
16314              * </ul>
16315              * @param {Object} dropEvent
16316              */
16317             "beforenodedrop" : true,
16318             /**
16319              * @event nodedrop
16320              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16321              * passed to handlers has the following properties:<br />
16322              * <ul style="padding:5px;padding-left:16px;">
16323              * <li>tree - The TreePanel</li>
16324              * <li>target - The node being targeted for the drop</li>
16325              * <li>data - The drag data from the drag source</li>
16326              * <li>point - The point of the drop - append, above or below</li>
16327              * <li>source - The drag source</li>
16328              * <li>rawEvent - Raw mouse event</li>
16329              * <li>dropNode - Dropped node(s).</li>
16330              * </ul>
16331              * @param {Object} dropEvent
16332              */
16333             "nodedrop" : true,
16334              /**
16335              * @event nodedragover
16336              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16337              * passed to handlers has the following properties:<br />
16338              * <ul style="padding:5px;padding-left:16px;">
16339              * <li>tree - The TreePanel</li>
16340              * <li>target - The node being targeted for the drop</li>
16341              * <li>data - The drag data from the drag source</li>
16342              * <li>point - The point of the drop - append, above or below</li>
16343              * <li>source - The drag source</li>
16344              * <li>rawEvent - Raw mouse event</li>
16345              * <li>dropNode - Drop node(s) provided by the source.</li>
16346              * <li>cancel - Set this to true to signal drop not allowed.</li>
16347              * </ul>
16348              * @param {Object} dragOverEvent
16349              */
16350             "nodedragover" : true
16351         
16352    });
16353    if(this.singleExpand){
16354        this.on("beforeexpand", this.restrictExpand, this);
16355    }
16356 };
16357 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16358     rootVisible : true,
16359     animate: Roo.enableFx,
16360     lines : true,
16361     enableDD : false,
16362     hlDrop : Roo.enableFx,
16363   
16364     renderer: false,
16365     
16366     rendererTip: false,
16367     // private
16368     restrictExpand : function(node){
16369         var p = node.parentNode;
16370         if(p){
16371             if(p.expandedChild && p.expandedChild.parentNode == p){
16372                 p.expandedChild.collapse();
16373             }
16374             p.expandedChild = node;
16375         }
16376     },
16377
16378     // private override
16379     setRootNode : function(node){
16380         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16381         if(!this.rootVisible){
16382             node.ui = new Roo.tree.RootTreeNodeUI(node);
16383         }
16384         return node;
16385     },
16386
16387     /**
16388      * Returns the container element for this TreePanel
16389      */
16390     getEl : function(){
16391         return this.el;
16392     },
16393
16394     /**
16395      * Returns the default TreeLoader for this TreePanel
16396      */
16397     getLoader : function(){
16398         return this.loader;
16399     },
16400
16401     /**
16402      * Expand all nodes
16403      */
16404     expandAll : function(){
16405         this.root.expand(true);
16406     },
16407
16408     /**
16409      * Collapse all nodes
16410      */
16411     collapseAll : function(){
16412         this.root.collapse(true);
16413     },
16414
16415     /**
16416      * Returns the selection model used by this TreePanel
16417      */
16418     getSelectionModel : function(){
16419         if(!this.selModel){
16420             this.selModel = new Roo.tree.DefaultSelectionModel();
16421         }
16422         return this.selModel;
16423     },
16424
16425     /**
16426      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16427      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16428      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16429      * @return {Array}
16430      */
16431     getChecked : function(a, startNode){
16432         startNode = startNode || this.root;
16433         var r = [];
16434         var f = function(){
16435             if(this.attributes.checked){
16436                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16437             }
16438         }
16439         startNode.cascade(f);
16440         return r;
16441     },
16442
16443     /**
16444      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16445      * @param {String} path
16446      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16447      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16448      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16449      */
16450     expandPath : function(path, attr, callback){
16451         attr = attr || "id";
16452         var keys = path.split(this.pathSeparator);
16453         var curNode = this.root;
16454         if(curNode.attributes[attr] != keys[1]){ // invalid root
16455             if(callback){
16456                 callback(false, null);
16457             }
16458             return;
16459         }
16460         var index = 1;
16461         var f = function(){
16462             if(++index == keys.length){
16463                 if(callback){
16464                     callback(true, curNode);
16465                 }
16466                 return;
16467             }
16468             var c = curNode.findChild(attr, keys[index]);
16469             if(!c){
16470                 if(callback){
16471                     callback(false, curNode);
16472                 }
16473                 return;
16474             }
16475             curNode = c;
16476             c.expand(false, false, f);
16477         };
16478         curNode.expand(false, false, f);
16479     },
16480
16481     /**
16482      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16483      * @param {String} path
16484      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16485      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16486      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16487      */
16488     selectPath : function(path, attr, callback){
16489         attr = attr || "id";
16490         var keys = path.split(this.pathSeparator);
16491         var v = keys.pop();
16492         if(keys.length > 0){
16493             var f = function(success, node){
16494                 if(success && node){
16495                     var n = node.findChild(attr, v);
16496                     if(n){
16497                         n.select();
16498                         if(callback){
16499                             callback(true, n);
16500                         }
16501                     }else if(callback){
16502                         callback(false, n);
16503                     }
16504                 }else{
16505                     if(callback){
16506                         callback(false, n);
16507                     }
16508                 }
16509             };
16510             this.expandPath(keys.join(this.pathSeparator), attr, f);
16511         }else{
16512             this.root.select();
16513             if(callback){
16514                 callback(true, this.root);
16515             }
16516         }
16517     },
16518
16519     getTreeEl : function(){
16520         return this.el;
16521     },
16522
16523     /**
16524      * Trigger rendering of this TreePanel
16525      */
16526     render : function(){
16527         if (this.innerCt) {
16528             return this; // stop it rendering more than once!!
16529         }
16530         
16531         this.innerCt = this.el.createChild({tag:"ul",
16532                cls:"x-tree-root-ct " +
16533                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16534
16535         if(this.containerScroll){
16536             Roo.dd.ScrollManager.register(this.el);
16537         }
16538         if((this.enableDD || this.enableDrop) && !this.dropZone){
16539            /**
16540             * The dropZone used by this tree if drop is enabled
16541             * @type Roo.tree.TreeDropZone
16542             */
16543              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16544                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16545            });
16546         }
16547         if((this.enableDD || this.enableDrag) && !this.dragZone){
16548            /**
16549             * The dragZone used by this tree if drag is enabled
16550             * @type Roo.tree.TreeDragZone
16551             */
16552             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16553                ddGroup: this.ddGroup || "TreeDD",
16554                scroll: this.ddScroll
16555            });
16556         }
16557         this.getSelectionModel().init(this);
16558         if (!this.root) {
16559             console.log("ROOT not set in tree");
16560             return;
16561         }
16562         this.root.render();
16563         if(!this.rootVisible){
16564             this.root.renderChildren();
16565         }
16566         return this;
16567     }
16568 });/*
16569  * Based on:
16570  * Ext JS Library 1.1.1
16571  * Copyright(c) 2006-2007, Ext JS, LLC.
16572  *
16573  * Originally Released Under LGPL - original licence link has changed is not relivant.
16574  *
16575  * Fork - LGPL
16576  * <script type="text/javascript">
16577  */
16578  
16579
16580 /**
16581  * @class Roo.tree.DefaultSelectionModel
16582  * @extends Roo.util.Observable
16583  * The default single selection for a TreePanel.
16584  */
16585 Roo.tree.DefaultSelectionModel = function(){
16586    this.selNode = null;
16587    
16588    this.addEvents({
16589        /**
16590         * @event selectionchange
16591         * Fires when the selected node changes
16592         * @param {DefaultSelectionModel} this
16593         * @param {TreeNode} node the new selection
16594         */
16595        "selectionchange" : true,
16596
16597        /**
16598         * @event beforeselect
16599         * Fires before the selected node changes, return false to cancel the change
16600         * @param {DefaultSelectionModel} this
16601         * @param {TreeNode} node the new selection
16602         * @param {TreeNode} node the old selection
16603         */
16604        "beforeselect" : true
16605    });
16606 };
16607
16608 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16609     init : function(tree){
16610         this.tree = tree;
16611         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16612         tree.on("click", this.onNodeClick, this);
16613     },
16614     
16615     onNodeClick : function(node, e){
16616         if (e.ctrlKey && this.selNode == node)  {
16617             this.unselect(node);
16618             return;
16619         }
16620         this.select(node);
16621     },
16622     
16623     /**
16624      * Select a node.
16625      * @param {TreeNode} node The node to select
16626      * @return {TreeNode} The selected node
16627      */
16628     select : function(node){
16629         var last = this.selNode;
16630         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16631             if(last){
16632                 last.ui.onSelectedChange(false);
16633             }
16634             this.selNode = node;
16635             node.ui.onSelectedChange(true);
16636             this.fireEvent("selectionchange", this, node, last);
16637         }
16638         return node;
16639     },
16640     
16641     /**
16642      * Deselect a node.
16643      * @param {TreeNode} node The node to unselect
16644      */
16645     unselect : function(node){
16646         if(this.selNode == node){
16647             this.clearSelections();
16648         }    
16649     },
16650     
16651     /**
16652      * Clear all selections
16653      */
16654     clearSelections : function(){
16655         var n = this.selNode;
16656         if(n){
16657             n.ui.onSelectedChange(false);
16658             this.selNode = null;
16659             this.fireEvent("selectionchange", this, null);
16660         }
16661         return n;
16662     },
16663     
16664     /**
16665      * Get the selected node
16666      * @return {TreeNode} The selected node
16667      */
16668     getSelectedNode : function(){
16669         return this.selNode;    
16670     },
16671     
16672     /**
16673      * Returns true if the node is selected
16674      * @param {TreeNode} node The node to check
16675      * @return {Boolean}
16676      */
16677     isSelected : function(node){
16678         return this.selNode == node;  
16679     },
16680
16681     /**
16682      * Selects the node above the selected node in the tree, intelligently walking the nodes
16683      * @return TreeNode The new selection
16684      */
16685     selectPrevious : function(){
16686         var s = this.selNode || this.lastSelNode;
16687         if(!s){
16688             return null;
16689         }
16690         var ps = s.previousSibling;
16691         if(ps){
16692             if(!ps.isExpanded() || ps.childNodes.length < 1){
16693                 return this.select(ps);
16694             } else{
16695                 var lc = ps.lastChild;
16696                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16697                     lc = lc.lastChild;
16698                 }
16699                 return this.select(lc);
16700             }
16701         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16702             return this.select(s.parentNode);
16703         }
16704         return null;
16705     },
16706
16707     /**
16708      * Selects the node above the selected node in the tree, intelligently walking the nodes
16709      * @return TreeNode The new selection
16710      */
16711     selectNext : function(){
16712         var s = this.selNode || this.lastSelNode;
16713         if(!s){
16714             return null;
16715         }
16716         if(s.firstChild && s.isExpanded()){
16717              return this.select(s.firstChild);
16718          }else if(s.nextSibling){
16719              return this.select(s.nextSibling);
16720          }else if(s.parentNode){
16721             var newS = null;
16722             s.parentNode.bubble(function(){
16723                 if(this.nextSibling){
16724                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16725                     return false;
16726                 }
16727             });
16728             return newS;
16729          }
16730         return null;
16731     },
16732
16733     onKeyDown : function(e){
16734         var s = this.selNode || this.lastSelNode;
16735         // undesirable, but required
16736         var sm = this;
16737         if(!s){
16738             return;
16739         }
16740         var k = e.getKey();
16741         switch(k){
16742              case e.DOWN:
16743                  e.stopEvent();
16744                  this.selectNext();
16745              break;
16746              case e.UP:
16747                  e.stopEvent();
16748                  this.selectPrevious();
16749              break;
16750              case e.RIGHT:
16751                  e.preventDefault();
16752                  if(s.hasChildNodes()){
16753                      if(!s.isExpanded()){
16754                          s.expand();
16755                      }else if(s.firstChild){
16756                          this.select(s.firstChild, e);
16757                      }
16758                  }
16759              break;
16760              case e.LEFT:
16761                  e.preventDefault();
16762                  if(s.hasChildNodes() && s.isExpanded()){
16763                      s.collapse();
16764                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16765                      this.select(s.parentNode, e);
16766                  }
16767              break;
16768         };
16769     }
16770 });
16771
16772 /**
16773  * @class Roo.tree.MultiSelectionModel
16774  * @extends Roo.util.Observable
16775  * Multi selection for a TreePanel.
16776  */
16777 Roo.tree.MultiSelectionModel = function(){
16778    this.selNodes = [];
16779    this.selMap = {};
16780    this.addEvents({
16781        /**
16782         * @event selectionchange
16783         * Fires when the selected nodes change
16784         * @param {MultiSelectionModel} this
16785         * @param {Array} nodes Array of the selected nodes
16786         */
16787        "selectionchange" : true
16788    });
16789 };
16790
16791 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16792     init : function(tree){
16793         this.tree = tree;
16794         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16795         tree.on("click", this.onNodeClick, this);
16796     },
16797     
16798     onNodeClick : function(node, e){
16799         this.select(node, e, e.ctrlKey);
16800     },
16801     
16802     /**
16803      * Select a node.
16804      * @param {TreeNode} node The node to select
16805      * @param {EventObject} e (optional) An event associated with the selection
16806      * @param {Boolean} keepExisting True to retain existing selections
16807      * @return {TreeNode} The selected node
16808      */
16809     select : function(node, e, keepExisting){
16810         if(keepExisting !== true){
16811             this.clearSelections(true);
16812         }
16813         if(this.isSelected(node)){
16814             this.lastSelNode = node;
16815             return node;
16816         }
16817         this.selNodes.push(node);
16818         this.selMap[node.id] = node;
16819         this.lastSelNode = node;
16820         node.ui.onSelectedChange(true);
16821         this.fireEvent("selectionchange", this, this.selNodes);
16822         return node;
16823     },
16824     
16825     /**
16826      * Deselect a node.
16827      * @param {TreeNode} node The node to unselect
16828      */
16829     unselect : function(node){
16830         if(this.selMap[node.id]){
16831             node.ui.onSelectedChange(false);
16832             var sn = this.selNodes;
16833             var index = -1;
16834             if(sn.indexOf){
16835                 index = sn.indexOf(node);
16836             }else{
16837                 for(var i = 0, len = sn.length; i < len; i++){
16838                     if(sn[i] == node){
16839                         index = i;
16840                         break;
16841                     }
16842                 }
16843             }
16844             if(index != -1){
16845                 this.selNodes.splice(index, 1);
16846             }
16847             delete this.selMap[node.id];
16848             this.fireEvent("selectionchange", this, this.selNodes);
16849         }
16850     },
16851     
16852     /**
16853      * Clear all selections
16854      */
16855     clearSelections : function(suppressEvent){
16856         var sn = this.selNodes;
16857         if(sn.length > 0){
16858             for(var i = 0, len = sn.length; i < len; i++){
16859                 sn[i].ui.onSelectedChange(false);
16860             }
16861             this.selNodes = [];
16862             this.selMap = {};
16863             if(suppressEvent !== true){
16864                 this.fireEvent("selectionchange", this, this.selNodes);
16865             }
16866         }
16867     },
16868     
16869     /**
16870      * Returns true if the node is selected
16871      * @param {TreeNode} node The node to check
16872      * @return {Boolean}
16873      */
16874     isSelected : function(node){
16875         return this.selMap[node.id] ? true : false;  
16876     },
16877     
16878     /**
16879      * Returns an array of the selected nodes
16880      * @return {Array}
16881      */
16882     getSelectedNodes : function(){
16883         return this.selNodes;    
16884     },
16885
16886     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16887
16888     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16889
16890     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16891 });/*
16892  * Based on:
16893  * Ext JS Library 1.1.1
16894  * Copyright(c) 2006-2007, Ext JS, LLC.
16895  *
16896  * Originally Released Under LGPL - original licence link has changed is not relivant.
16897  *
16898  * Fork - LGPL
16899  * <script type="text/javascript">
16900  */
16901  
16902 /**
16903  * @class Roo.tree.TreeNode
16904  * @extends Roo.data.Node
16905  * @cfg {String} text The text for this node
16906  * @cfg {Boolean} expanded true to start the node expanded
16907  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16908  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16909  * @cfg {Boolean} disabled true to start the node disabled
16910  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16911  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16912  * @cfg {String} cls A css class to be added to the node
16913  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16914  * @cfg {String} href URL of the link used for the node (defaults to #)
16915  * @cfg {String} hrefTarget target frame for the link
16916  * @cfg {String} qtip An Ext QuickTip for the node
16917  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16918  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16919  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16920  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16921  * (defaults to undefined with no checkbox rendered)
16922  * @constructor
16923  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16924  */
16925 Roo.tree.TreeNode = function(attributes){
16926     attributes = attributes || {};
16927     if(typeof attributes == "string"){
16928         attributes = {text: attributes};
16929     }
16930     this.childrenRendered = false;
16931     this.rendered = false;
16932     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16933     this.expanded = attributes.expanded === true;
16934     this.isTarget = attributes.isTarget !== false;
16935     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16936     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
16937
16938     /**
16939      * Read-only. The text for this node. To change it use setText().
16940      * @type String
16941      */
16942     this.text = attributes.text;
16943     /**
16944      * True if this node is disabled.
16945      * @type Boolean
16946      */
16947     this.disabled = attributes.disabled === true;
16948
16949     this.addEvents({
16950         /**
16951         * @event textchange
16952         * Fires when the text for this node is changed
16953         * @param {Node} this This node
16954         * @param {String} text The new text
16955         * @param {String} oldText The old text
16956         */
16957         "textchange" : true,
16958         /**
16959         * @event beforeexpand
16960         * Fires before this node is expanded, return false to cancel.
16961         * @param {Node} this This node
16962         * @param {Boolean} deep
16963         * @param {Boolean} anim
16964         */
16965         "beforeexpand" : true,
16966         /**
16967         * @event beforecollapse
16968         * Fires before this node is collapsed, return false to cancel.
16969         * @param {Node} this This node
16970         * @param {Boolean} deep
16971         * @param {Boolean} anim
16972         */
16973         "beforecollapse" : true,
16974         /**
16975         * @event expand
16976         * Fires when this node is expanded
16977         * @param {Node} this This node
16978         */
16979         "expand" : true,
16980         /**
16981         * @event disabledchange
16982         * Fires when the disabled status of this node changes
16983         * @param {Node} this This node
16984         * @param {Boolean} disabled
16985         */
16986         "disabledchange" : true,
16987         /**
16988         * @event collapse
16989         * Fires when this node is collapsed
16990         * @param {Node} this This node
16991         */
16992         "collapse" : true,
16993         /**
16994         * @event beforeclick
16995         * Fires before click processing. Return false to cancel the default action.
16996         * @param {Node} this This node
16997         * @param {Roo.EventObject} e The event object
16998         */
16999         "beforeclick":true,
17000         /**
17001         * @event checkchange
17002         * Fires when a node with a checkbox's checked property changes
17003         * @param {Node} this This node
17004         * @param {Boolean} checked
17005         */
17006         "checkchange":true,
17007         /**
17008         * @event click
17009         * Fires when this node is clicked
17010         * @param {Node} this This node
17011         * @param {Roo.EventObject} e The event object
17012         */
17013         "click":true,
17014         /**
17015         * @event dblclick
17016         * Fires when this node is double clicked
17017         * @param {Node} this This node
17018         * @param {Roo.EventObject} e The event object
17019         */
17020         "dblclick":true,
17021         /**
17022         * @event contextmenu
17023         * Fires when this node is right clicked
17024         * @param {Node} this This node
17025         * @param {Roo.EventObject} e The event object
17026         */
17027         "contextmenu":true,
17028         /**
17029         * @event beforechildrenrendered
17030         * Fires right before the child nodes for this node are rendered
17031         * @param {Node} this This node
17032         */
17033         "beforechildrenrendered":true
17034     });
17035
17036     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17037
17038     /**
17039      * Read-only. The UI for this node
17040      * @type TreeNodeUI
17041      */
17042     this.ui = new uiClass(this);
17043 };
17044 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17045     preventHScroll: true,
17046     /**
17047      * Returns true if this node is expanded
17048      * @return {Boolean}
17049      */
17050     isExpanded : function(){
17051         return this.expanded;
17052     },
17053
17054     /**
17055      * Returns the UI object for this node
17056      * @return {TreeNodeUI}
17057      */
17058     getUI : function(){
17059         return this.ui;
17060     },
17061
17062     // private override
17063     setFirstChild : function(node){
17064         var of = this.firstChild;
17065         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17066         if(this.childrenRendered && of && node != of){
17067             of.renderIndent(true, true);
17068         }
17069         if(this.rendered){
17070             this.renderIndent(true, true);
17071         }
17072     },
17073
17074     // private override
17075     setLastChild : function(node){
17076         var ol = this.lastChild;
17077         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17078         if(this.childrenRendered && ol && node != ol){
17079             ol.renderIndent(true, true);
17080         }
17081         if(this.rendered){
17082             this.renderIndent(true, true);
17083         }
17084     },
17085
17086     // these methods are overridden to provide lazy rendering support
17087     // private override
17088     appendChild : function(){
17089         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17090         if(node && this.childrenRendered){
17091             node.render();
17092         }
17093         this.ui.updateExpandIcon();
17094         return node;
17095     },
17096
17097     // private override
17098     removeChild : function(node){
17099         this.ownerTree.getSelectionModel().unselect(node);
17100         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17101         // if it's been rendered remove dom node
17102         if(this.childrenRendered){
17103             node.ui.remove();
17104         }
17105         if(this.childNodes.length < 1){
17106             this.collapse(false, false);
17107         }else{
17108             this.ui.updateExpandIcon();
17109         }
17110         if(!this.firstChild) {
17111             this.childrenRendered = false;
17112         }
17113         return node;
17114     },
17115
17116     // private override
17117     insertBefore : function(node, refNode){
17118         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17119         if(newNode && refNode && this.childrenRendered){
17120             node.render();
17121         }
17122         this.ui.updateExpandIcon();
17123         return newNode;
17124     },
17125
17126     /**
17127      * Sets the text for this node
17128      * @param {String} text
17129      */
17130     setText : function(text){
17131         var oldText = this.text;
17132         this.text = text;
17133         this.attributes.text = text;
17134         if(this.rendered){ // event without subscribing
17135             this.ui.onTextChange(this, text, oldText);
17136         }
17137         this.fireEvent("textchange", this, text, oldText);
17138     },
17139
17140     /**
17141      * Triggers selection of this node
17142      */
17143     select : function(){
17144         this.getOwnerTree().getSelectionModel().select(this);
17145     },
17146
17147     /**
17148      * Triggers deselection of this node
17149      */
17150     unselect : function(){
17151         this.getOwnerTree().getSelectionModel().unselect(this);
17152     },
17153
17154     /**
17155      * Returns true if this node is selected
17156      * @return {Boolean}
17157      */
17158     isSelected : function(){
17159         return this.getOwnerTree().getSelectionModel().isSelected(this);
17160     },
17161
17162     /**
17163      * Expand this node.
17164      * @param {Boolean} deep (optional) True to expand all children as well
17165      * @param {Boolean} anim (optional) false to cancel the default animation
17166      * @param {Function} callback (optional) A callback to be called when
17167      * expanding this node completes (does not wait for deep expand to complete).
17168      * Called with 1 parameter, this node.
17169      */
17170     expand : function(deep, anim, callback){
17171         if(!this.expanded){
17172             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17173                 return;
17174             }
17175             if(!this.childrenRendered){
17176                 this.renderChildren();
17177             }
17178             this.expanded = true;
17179             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17180                 this.ui.animExpand(function(){
17181                     this.fireEvent("expand", this);
17182                     if(typeof callback == "function"){
17183                         callback(this);
17184                     }
17185                     if(deep === true){
17186                         this.expandChildNodes(true);
17187                     }
17188                 }.createDelegate(this));
17189                 return;
17190             }else{
17191                 this.ui.expand();
17192                 this.fireEvent("expand", this);
17193                 if(typeof callback == "function"){
17194                     callback(this);
17195                 }
17196             }
17197         }else{
17198            if(typeof callback == "function"){
17199                callback(this);
17200            }
17201         }
17202         if(deep === true){
17203             this.expandChildNodes(true);
17204         }
17205     },
17206
17207     isHiddenRoot : function(){
17208         return this.isRoot && !this.getOwnerTree().rootVisible;
17209     },
17210
17211     /**
17212      * Collapse this node.
17213      * @param {Boolean} deep (optional) True to collapse all children as well
17214      * @param {Boolean} anim (optional) false to cancel the default animation
17215      */
17216     collapse : function(deep, anim){
17217         if(this.expanded && !this.isHiddenRoot()){
17218             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17219                 return;
17220             }
17221             this.expanded = false;
17222             if((this.getOwnerTree().animate && anim !== false) || anim){
17223                 this.ui.animCollapse(function(){
17224                     this.fireEvent("collapse", this);
17225                     if(deep === true){
17226                         this.collapseChildNodes(true);
17227                     }
17228                 }.createDelegate(this));
17229                 return;
17230             }else{
17231                 this.ui.collapse();
17232                 this.fireEvent("collapse", this);
17233             }
17234         }
17235         if(deep === true){
17236             var cs = this.childNodes;
17237             for(var i = 0, len = cs.length; i < len; i++) {
17238                 cs[i].collapse(true, false);
17239             }
17240         }
17241     },
17242
17243     // private
17244     delayedExpand : function(delay){
17245         if(!this.expandProcId){
17246             this.expandProcId = this.expand.defer(delay, this);
17247         }
17248     },
17249
17250     // private
17251     cancelExpand : function(){
17252         if(this.expandProcId){
17253             clearTimeout(this.expandProcId);
17254         }
17255         this.expandProcId = false;
17256     },
17257
17258     /**
17259      * Toggles expanded/collapsed state of the node
17260      */
17261     toggle : function(){
17262         if(this.expanded){
17263             this.collapse();
17264         }else{
17265             this.expand();
17266         }
17267     },
17268
17269     /**
17270      * Ensures all parent nodes are expanded
17271      */
17272     ensureVisible : function(callback){
17273         var tree = this.getOwnerTree();
17274         tree.expandPath(this.parentNode.getPath(), false, function(){
17275             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17276             Roo.callback(callback);
17277         }.createDelegate(this));
17278     },
17279
17280     /**
17281      * Expand all child nodes
17282      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17283      */
17284     expandChildNodes : function(deep){
17285         var cs = this.childNodes;
17286         for(var i = 0, len = cs.length; i < len; i++) {
17287                 cs[i].expand(deep);
17288         }
17289     },
17290
17291     /**
17292      * Collapse all child nodes
17293      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17294      */
17295     collapseChildNodes : function(deep){
17296         var cs = this.childNodes;
17297         for(var i = 0, len = cs.length; i < len; i++) {
17298                 cs[i].collapse(deep);
17299         }
17300     },
17301
17302     /**
17303      * Disables this node
17304      */
17305     disable : function(){
17306         this.disabled = true;
17307         this.unselect();
17308         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17309             this.ui.onDisableChange(this, true);
17310         }
17311         this.fireEvent("disabledchange", this, true);
17312     },
17313
17314     /**
17315      * Enables this node
17316      */
17317     enable : function(){
17318         this.disabled = false;
17319         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17320             this.ui.onDisableChange(this, false);
17321         }
17322         this.fireEvent("disabledchange", this, false);
17323     },
17324
17325     // private
17326     renderChildren : function(suppressEvent){
17327         if(suppressEvent !== false){
17328             this.fireEvent("beforechildrenrendered", this);
17329         }
17330         var cs = this.childNodes;
17331         for(var i = 0, len = cs.length; i < len; i++){
17332             cs[i].render(true);
17333         }
17334         this.childrenRendered = true;
17335     },
17336
17337     // private
17338     sort : function(fn, scope){
17339         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17340         if(this.childrenRendered){
17341             var cs = this.childNodes;
17342             for(var i = 0, len = cs.length; i < len; i++){
17343                 cs[i].render(true);
17344             }
17345         }
17346     },
17347
17348     // private
17349     render : function(bulkRender){
17350         this.ui.render(bulkRender);
17351         if(!this.rendered){
17352             this.rendered = true;
17353             if(this.expanded){
17354                 this.expanded = false;
17355                 this.expand(false, false);
17356             }
17357         }
17358     },
17359
17360     // private
17361     renderIndent : function(deep, refresh){
17362         if(refresh){
17363             this.ui.childIndent = null;
17364         }
17365         this.ui.renderIndent();
17366         if(deep === true && this.childrenRendered){
17367             var cs = this.childNodes;
17368             for(var i = 0, len = cs.length; i < len; i++){
17369                 cs[i].renderIndent(true, refresh);
17370             }
17371         }
17372     }
17373 });/*
17374  * Based on:
17375  * Ext JS Library 1.1.1
17376  * Copyright(c) 2006-2007, Ext JS, LLC.
17377  *
17378  * Originally Released Under LGPL - original licence link has changed is not relivant.
17379  *
17380  * Fork - LGPL
17381  * <script type="text/javascript">
17382  */
17383  
17384 /**
17385  * @class Roo.tree.AsyncTreeNode
17386  * @extends Roo.tree.TreeNode
17387  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17388  * @constructor
17389  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17390  */
17391  Roo.tree.AsyncTreeNode = function(config){
17392     this.loaded = false;
17393     this.loading = false;
17394     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17395     /**
17396     * @event beforeload
17397     * Fires before this node is loaded, return false to cancel
17398     * @param {Node} this This node
17399     */
17400     this.addEvents({'beforeload':true, 'load': true});
17401     /**
17402     * @event load
17403     * Fires when this node is loaded
17404     * @param {Node} this This node
17405     */
17406     /**
17407      * The loader used by this node (defaults to using the tree's defined loader)
17408      * @type TreeLoader
17409      * @property loader
17410      */
17411 };
17412 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17413     expand : function(deep, anim, callback){
17414         if(this.loading){ // if an async load is already running, waiting til it's done
17415             var timer;
17416             var f = function(){
17417                 if(!this.loading){ // done loading
17418                     clearInterval(timer);
17419                     this.expand(deep, anim, callback);
17420                 }
17421             }.createDelegate(this);
17422             timer = setInterval(f, 200);
17423             return;
17424         }
17425         if(!this.loaded){
17426             if(this.fireEvent("beforeload", this) === false){
17427                 return;
17428             }
17429             this.loading = true;
17430             this.ui.beforeLoad(this);
17431             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17432             if(loader){
17433                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17434                 return;
17435             }
17436         }
17437         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17438     },
17439     
17440     /**
17441      * Returns true if this node is currently loading
17442      * @return {Boolean}
17443      */
17444     isLoading : function(){
17445         return this.loading;  
17446     },
17447     
17448     loadComplete : function(deep, anim, callback){
17449         this.loading = false;
17450         this.loaded = true;
17451         this.ui.afterLoad(this);
17452         this.fireEvent("load", this);
17453         this.expand(deep, anim, callback);
17454     },
17455     
17456     /**
17457      * Returns true if this node has been loaded
17458      * @return {Boolean}
17459      */
17460     isLoaded : function(){
17461         return this.loaded;
17462     },
17463     
17464     hasChildNodes : function(){
17465         if(!this.isLeaf() && !this.loaded){
17466             return true;
17467         }else{
17468             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17469         }
17470     },
17471
17472     /**
17473      * Trigger a reload for this node
17474      * @param {Function} callback
17475      */
17476     reload : function(callback){
17477         this.collapse(false, false);
17478         while(this.firstChild){
17479             this.removeChild(this.firstChild);
17480         }
17481         this.childrenRendered = false;
17482         this.loaded = false;
17483         if(this.isHiddenRoot()){
17484             this.expanded = false;
17485         }
17486         this.expand(false, false, callback);
17487     }
17488 });/*
17489  * Based on:
17490  * Ext JS Library 1.1.1
17491  * Copyright(c) 2006-2007, Ext JS, LLC.
17492  *
17493  * Originally Released Under LGPL - original licence link has changed is not relivant.
17494  *
17495  * Fork - LGPL
17496  * <script type="text/javascript">
17497  */
17498  
17499 /**
17500  * @class Roo.tree.TreeNodeUI
17501  * @constructor
17502  * @param {Object} node The node to render
17503  * The TreeNode UI implementation is separate from the
17504  * tree implementation. Unless you are customizing the tree UI,
17505  * you should never have to use this directly.
17506  */
17507 Roo.tree.TreeNodeUI = function(node){
17508     this.node = node;
17509     this.rendered = false;
17510     this.animating = false;
17511     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17512 };
17513
17514 Roo.tree.TreeNodeUI.prototype = {
17515     removeChild : function(node){
17516         if(this.rendered){
17517             this.ctNode.removeChild(node.ui.getEl());
17518         }
17519     },
17520
17521     beforeLoad : function(){
17522          this.addClass("x-tree-node-loading");
17523     },
17524
17525     afterLoad : function(){
17526          this.removeClass("x-tree-node-loading");
17527     },
17528
17529     onTextChange : function(node, text, oldText){
17530         if(this.rendered){
17531             this.textNode.innerHTML = text;
17532         }
17533     },
17534
17535     onDisableChange : function(node, state){
17536         this.disabled = state;
17537         if(state){
17538             this.addClass("x-tree-node-disabled");
17539         }else{
17540             this.removeClass("x-tree-node-disabled");
17541         }
17542     },
17543
17544     onSelectedChange : function(state){
17545         if(state){
17546             this.focus();
17547             this.addClass("x-tree-selected");
17548         }else{
17549             //this.blur();
17550             this.removeClass("x-tree-selected");
17551         }
17552     },
17553
17554     onMove : function(tree, node, oldParent, newParent, index, refNode){
17555         this.childIndent = null;
17556         if(this.rendered){
17557             var targetNode = newParent.ui.getContainer();
17558             if(!targetNode){//target not rendered
17559                 this.holder = document.createElement("div");
17560                 this.holder.appendChild(this.wrap);
17561                 return;
17562             }
17563             var insertBefore = refNode ? refNode.ui.getEl() : null;
17564             if(insertBefore){
17565                 targetNode.insertBefore(this.wrap, insertBefore);
17566             }else{
17567                 targetNode.appendChild(this.wrap);
17568             }
17569             this.node.renderIndent(true);
17570         }
17571     },
17572
17573     addClass : function(cls){
17574         if(this.elNode){
17575             Roo.fly(this.elNode).addClass(cls);
17576         }
17577     },
17578
17579     removeClass : function(cls){
17580         if(this.elNode){
17581             Roo.fly(this.elNode).removeClass(cls);
17582         }
17583     },
17584
17585     remove : function(){
17586         if(this.rendered){
17587             this.holder = document.createElement("div");
17588             this.holder.appendChild(this.wrap);
17589         }
17590     },
17591
17592     fireEvent : function(){
17593         return this.node.fireEvent.apply(this.node, arguments);
17594     },
17595
17596     initEvents : function(){
17597         this.node.on("move", this.onMove, this);
17598         var E = Roo.EventManager;
17599         var a = this.anchor;
17600
17601         var el = Roo.fly(a, '_treeui');
17602
17603         if(Roo.isOpera){ // opera render bug ignores the CSS
17604             el.setStyle("text-decoration", "none");
17605         }
17606
17607         el.on("click", this.onClick, this);
17608         el.on("dblclick", this.onDblClick, this);
17609
17610         if(this.checkbox){
17611             Roo.EventManager.on(this.checkbox,
17612                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17613         }
17614
17615         el.on("contextmenu", this.onContextMenu, this);
17616
17617         var icon = Roo.fly(this.iconNode);
17618         icon.on("click", this.onClick, this);
17619         icon.on("dblclick", this.onDblClick, this);
17620         icon.on("contextmenu", this.onContextMenu, this);
17621         E.on(this.ecNode, "click", this.ecClick, this, true);
17622
17623         if(this.node.disabled){
17624             this.addClass("x-tree-node-disabled");
17625         }
17626         if(this.node.hidden){
17627             this.addClass("x-tree-node-disabled");
17628         }
17629         var ot = this.node.getOwnerTree();
17630         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17631         if(dd && (!this.node.isRoot || ot.rootVisible)){
17632             Roo.dd.Registry.register(this.elNode, {
17633                 node: this.node,
17634                 handles: this.getDDHandles(),
17635                 isHandle: false
17636             });
17637         }
17638     },
17639
17640     getDDHandles : function(){
17641         return [this.iconNode, this.textNode];
17642     },
17643
17644     hide : function(){
17645         if(this.rendered){
17646             this.wrap.style.display = "none";
17647         }
17648     },
17649
17650     show : function(){
17651         if(this.rendered){
17652             this.wrap.style.display = "";
17653         }
17654     },
17655
17656     onContextMenu : function(e){
17657         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17658             e.preventDefault();
17659             this.focus();
17660             this.fireEvent("contextmenu", this.node, e);
17661         }
17662     },
17663
17664     onClick : function(e){
17665         if(this.dropping){
17666             e.stopEvent();
17667             return;
17668         }
17669         if(this.fireEvent("beforeclick", this.node, e) !== false){
17670             if(!this.disabled && this.node.attributes.href){
17671                 this.fireEvent("click", this.node, e);
17672                 return;
17673             }
17674             e.preventDefault();
17675             if(this.disabled){
17676                 return;
17677             }
17678
17679             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17680                 this.node.toggle();
17681             }
17682
17683             this.fireEvent("click", this.node, e);
17684         }else{
17685             e.stopEvent();
17686         }
17687     },
17688
17689     onDblClick : function(e){
17690         e.preventDefault();
17691         if(this.disabled){
17692             return;
17693         }
17694         if(this.checkbox){
17695             this.toggleCheck();
17696         }
17697         if(!this.animating && this.node.hasChildNodes()){
17698             this.node.toggle();
17699         }
17700         this.fireEvent("dblclick", this.node, e);
17701     },
17702
17703     onCheckChange : function(){
17704         var checked = this.checkbox.checked;
17705         this.node.attributes.checked = checked;
17706         this.fireEvent('checkchange', this.node, checked);
17707     },
17708
17709     ecClick : function(e){
17710         if(!this.animating && this.node.hasChildNodes()){
17711             this.node.toggle();
17712         }
17713     },
17714
17715     startDrop : function(){
17716         this.dropping = true;
17717     },
17718
17719     // delayed drop so the click event doesn't get fired on a drop
17720     endDrop : function(){
17721        setTimeout(function(){
17722            this.dropping = false;
17723        }.createDelegate(this), 50);
17724     },
17725
17726     expand : function(){
17727         this.updateExpandIcon();
17728         this.ctNode.style.display = "";
17729     },
17730
17731     focus : function(){
17732         if(!this.node.preventHScroll){
17733             try{this.anchor.focus();
17734             }catch(e){}
17735         }else if(!Roo.isIE){
17736             try{
17737                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17738                 var l = noscroll.scrollLeft;
17739                 this.anchor.focus();
17740                 noscroll.scrollLeft = l;
17741             }catch(e){}
17742         }
17743     },
17744
17745     toggleCheck : function(value){
17746         var cb = this.checkbox;
17747         if(cb){
17748             cb.checked = (value === undefined ? !cb.checked : value);
17749         }
17750     },
17751
17752     blur : function(){
17753         try{
17754             this.anchor.blur();
17755         }catch(e){}
17756     },
17757
17758     animExpand : function(callback){
17759         var ct = Roo.get(this.ctNode);
17760         ct.stopFx();
17761         if(!this.node.hasChildNodes()){
17762             this.updateExpandIcon();
17763             this.ctNode.style.display = "";
17764             Roo.callback(callback);
17765             return;
17766         }
17767         this.animating = true;
17768         this.updateExpandIcon();
17769
17770         ct.slideIn('t', {
17771            callback : function(){
17772                this.animating = false;
17773                Roo.callback(callback);
17774             },
17775             scope: this,
17776             duration: this.node.ownerTree.duration || .25
17777         });
17778     },
17779
17780     highlight : function(){
17781         var tree = this.node.getOwnerTree();
17782         Roo.fly(this.wrap).highlight(
17783             tree.hlColor || "C3DAF9",
17784             {endColor: tree.hlBaseColor}
17785         );
17786     },
17787
17788     collapse : function(){
17789         this.updateExpandIcon();
17790         this.ctNode.style.display = "none";
17791     },
17792
17793     animCollapse : function(callback){
17794         var ct = Roo.get(this.ctNode);
17795         ct.enableDisplayMode('block');
17796         ct.stopFx();
17797
17798         this.animating = true;
17799         this.updateExpandIcon();
17800
17801         ct.slideOut('t', {
17802             callback : function(){
17803                this.animating = false;
17804                Roo.callback(callback);
17805             },
17806             scope: this,
17807             duration: this.node.ownerTree.duration || .25
17808         });
17809     },
17810
17811     getContainer : function(){
17812         return this.ctNode;
17813     },
17814
17815     getEl : function(){
17816         return this.wrap;
17817     },
17818
17819     appendDDGhost : function(ghostNode){
17820         ghostNode.appendChild(this.elNode.cloneNode(true));
17821     },
17822
17823     getDDRepairXY : function(){
17824         return Roo.lib.Dom.getXY(this.iconNode);
17825     },
17826
17827     onRender : function(){
17828         this.render();
17829     },
17830
17831     render : function(bulkRender){
17832         var n = this.node, a = n.attributes;
17833         var targetNode = n.parentNode ?
17834               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17835
17836         if(!this.rendered){
17837             this.rendered = true;
17838
17839             this.renderElements(n, a, targetNode, bulkRender);
17840
17841             if(a.qtip){
17842                if(this.textNode.setAttributeNS){
17843                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17844                    if(a.qtipTitle){
17845                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17846                    }
17847                }else{
17848                    this.textNode.setAttribute("ext:qtip", a.qtip);
17849                    if(a.qtipTitle){
17850                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17851                    }
17852                }
17853             }else if(a.qtipCfg){
17854                 a.qtipCfg.target = Roo.id(this.textNode);
17855                 Roo.QuickTips.register(a.qtipCfg);
17856             }
17857             this.initEvents();
17858             if(!this.node.expanded){
17859                 this.updateExpandIcon();
17860             }
17861         }else{
17862             if(bulkRender === true) {
17863                 targetNode.appendChild(this.wrap);
17864             }
17865         }
17866     },
17867
17868     renderElements : function(n, a, targetNode, bulkRender){
17869         // add some indent caching, this helps performance when rendering a large tree
17870         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17871         var t = n.getOwnerTree();
17872         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17873         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17874         var cb = typeof a.checked == 'boolean';
17875         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17876         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17877             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17878             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17879             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17880             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17881             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17882              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17883                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17884             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17885             "</li>"];
17886
17887         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17888             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17889                                 n.nextSibling.ui.getEl(), buf.join(""));
17890         }else{
17891             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17892         }
17893
17894         this.elNode = this.wrap.childNodes[0];
17895         this.ctNode = this.wrap.childNodes[1];
17896         var cs = this.elNode.childNodes;
17897         this.indentNode = cs[0];
17898         this.ecNode = cs[1];
17899         this.iconNode = cs[2];
17900         var index = 3;
17901         if(cb){
17902             this.checkbox = cs[3];
17903             index++;
17904         }
17905         this.anchor = cs[index];
17906         this.textNode = cs[index].firstChild;
17907     },
17908
17909     getAnchor : function(){
17910         return this.anchor;
17911     },
17912
17913     getTextEl : function(){
17914         return this.textNode;
17915     },
17916
17917     getIconEl : function(){
17918         return this.iconNode;
17919     },
17920
17921     isChecked : function(){
17922         return this.checkbox ? this.checkbox.checked : false;
17923     },
17924
17925     updateExpandIcon : function(){
17926         if(this.rendered){
17927             var n = this.node, c1, c2;
17928             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17929             var hasChild = n.hasChildNodes();
17930             if(hasChild){
17931                 if(n.expanded){
17932                     cls += "-minus";
17933                     c1 = "x-tree-node-collapsed";
17934                     c2 = "x-tree-node-expanded";
17935                 }else{
17936                     cls += "-plus";
17937                     c1 = "x-tree-node-expanded";
17938                     c2 = "x-tree-node-collapsed";
17939                 }
17940                 if(this.wasLeaf){
17941                     this.removeClass("x-tree-node-leaf");
17942                     this.wasLeaf = false;
17943                 }
17944                 if(this.c1 != c1 || this.c2 != c2){
17945                     Roo.fly(this.elNode).replaceClass(c1, c2);
17946                     this.c1 = c1; this.c2 = c2;
17947                 }
17948             }else{
17949                 if(!this.wasLeaf){
17950                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
17951                     delete this.c1;
17952                     delete this.c2;
17953                     this.wasLeaf = true;
17954                 }
17955             }
17956             var ecc = "x-tree-ec-icon "+cls;
17957             if(this.ecc != ecc){
17958                 this.ecNode.className = ecc;
17959                 this.ecc = ecc;
17960             }
17961         }
17962     },
17963
17964     getChildIndent : function(){
17965         if(!this.childIndent){
17966             var buf = [];
17967             var p = this.node;
17968             while(p){
17969                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
17970                     if(!p.isLast()) {
17971                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
17972                     } else {
17973                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
17974                     }
17975                 }
17976                 p = p.parentNode;
17977             }
17978             this.childIndent = buf.join("");
17979         }
17980         return this.childIndent;
17981     },
17982
17983     renderIndent : function(){
17984         if(this.rendered){
17985             var indent = "";
17986             var p = this.node.parentNode;
17987             if(p){
17988                 indent = p.ui.getChildIndent();
17989             }
17990             if(this.indentMarkup != indent){ // don't rerender if not required
17991                 this.indentNode.innerHTML = indent;
17992                 this.indentMarkup = indent;
17993             }
17994             this.updateExpandIcon();
17995         }
17996     }
17997 };
17998
17999 Roo.tree.RootTreeNodeUI = function(){
18000     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18001 };
18002 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18003     render : function(){
18004         if(!this.rendered){
18005             var targetNode = this.node.ownerTree.innerCt.dom;
18006             this.node.expanded = true;
18007             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18008             this.wrap = this.ctNode = targetNode.firstChild;
18009         }
18010     },
18011     collapse : function(){
18012     },
18013     expand : function(){
18014     }
18015 });/*
18016  * Based on:
18017  * Ext JS Library 1.1.1
18018  * Copyright(c) 2006-2007, Ext JS, LLC.
18019  *
18020  * Originally Released Under LGPL - original licence link has changed is not relivant.
18021  *
18022  * Fork - LGPL
18023  * <script type="text/javascript">
18024  */
18025 /**
18026  * @class Roo.tree.TreeLoader
18027  * @extends Roo.util.Observable
18028  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18029  * nodes from a specified URL. The response must be a javascript Array definition
18030  * who's elements are node definition objects. eg:
18031  * <pre><code>
18032    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18033     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18034 </code></pre>
18035  * <br><br>
18036  * A server request is sent, and child nodes are loaded only when a node is expanded.
18037  * The loading node's id is passed to the server under the parameter name "node" to
18038  * enable the server to produce the correct child nodes.
18039  * <br><br>
18040  * To pass extra parameters, an event handler may be attached to the "beforeload"
18041  * event, and the parameters specified in the TreeLoader's baseParams property:
18042  * <pre><code>
18043     myTreeLoader.on("beforeload", function(treeLoader, node) {
18044         this.baseParams.category = node.attributes.category;
18045     }, this);
18046 </code></pre><
18047  * This would pass an HTTP parameter called "category" to the server containing
18048  * the value of the Node's "category" attribute.
18049  * @constructor
18050  * Creates a new Treeloader.
18051  * @param {Object} config A config object containing config properties.
18052  */
18053 Roo.tree.TreeLoader = function(config){
18054     this.baseParams = {};
18055     this.requestMethod = "POST";
18056     Roo.apply(this, config);
18057
18058     this.addEvents({
18059     
18060         /**
18061          * @event beforeload
18062          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18063          * @param {Object} This TreeLoader object.
18064          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18065          * @param {Object} callback The callback function specified in the {@link #load} call.
18066          */
18067         beforeload : true,
18068         /**
18069          * @event load
18070          * Fires when the node has been successfuly loaded.
18071          * @param {Object} This TreeLoader object.
18072          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18073          * @param {Object} response The response object containing the data from the server.
18074          */
18075         load : true,
18076         /**
18077          * @event loadexception
18078          * Fires if the network request failed.
18079          * @param {Object} This TreeLoader object.
18080          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18081          * @param {Object} response The response object containing the data from the server.
18082          */
18083         loadexception : true,
18084         /**
18085          * @event create
18086          * Fires before a node is created, enabling you to return custom Node types 
18087          * @param {Object} This TreeLoader object.
18088          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18089          */
18090         create : true
18091     });
18092
18093     Roo.tree.TreeLoader.superclass.constructor.call(this);
18094 };
18095
18096 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18097     /**
18098     * @cfg {String} dataUrl The URL from which to request a Json string which
18099     * specifies an array of node definition object representing the child nodes
18100     * to be loaded.
18101     */
18102     /**
18103     * @cfg {Object} baseParams (optional) An object containing properties which
18104     * specify HTTP parameters to be passed to each request for child nodes.
18105     */
18106     /**
18107     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18108     * created by this loader. If the attributes sent by the server have an attribute in this object,
18109     * they take priority.
18110     */
18111     /**
18112     * @cfg {Object} uiProviders (optional) An object containing properties which
18113     * 
18114     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18115     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18116     * <i>uiProvider</i> attribute of a returned child node is a string rather
18117     * than a reference to a TreeNodeUI implementation, this that string value
18118     * is used as a property name in the uiProviders object. You can define the provider named
18119     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18120     */
18121     uiProviders : {},
18122
18123     /**
18124     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18125     * child nodes before loading.
18126     */
18127     clearOnLoad : true,
18128
18129     /**
18130     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18131     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18132     * Grid query { data : [ .....] }
18133     */
18134     
18135     root : false,
18136      /**
18137     * @cfg {String} queryParam (optional) 
18138     * Name of the query as it will be passed on the querystring (defaults to 'node')
18139     * eg. the request will be ?node=[id]
18140     */
18141     
18142     
18143     queryParam: false,
18144     
18145     /**
18146      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18147      * This is called automatically when a node is expanded, but may be used to reload
18148      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18149      * @param {Roo.tree.TreeNode} node
18150      * @param {Function} callback
18151      */
18152     load : function(node, callback){
18153         if(this.clearOnLoad){
18154             while(node.firstChild){
18155                 node.removeChild(node.firstChild);
18156             }
18157         }
18158         if(node.attributes.children){ // preloaded json children
18159             var cs = node.attributes.children;
18160             for(var i = 0, len = cs.length; i < len; i++){
18161                 node.appendChild(this.createNode(cs[i]));
18162             }
18163             if(typeof callback == "function"){
18164                 callback();
18165             }
18166         }else if(this.dataUrl){
18167             this.requestData(node, callback);
18168         }
18169     },
18170
18171     getParams: function(node){
18172         var buf = [], bp = this.baseParams;
18173         for(var key in bp){
18174             if(typeof bp[key] != "function"){
18175                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18176             }
18177         }
18178         var n = this.queryParam === false ? 'node' : this.queryParam;
18179         buf.push(n + "=", encodeURIComponent(node.id));
18180         return buf.join("");
18181     },
18182
18183     requestData : function(node, callback){
18184         if(this.fireEvent("beforeload", this, node, callback) !== false){
18185             this.transId = Roo.Ajax.request({
18186                 method:this.requestMethod,
18187                 url: this.dataUrl||this.url,
18188                 success: this.handleResponse,
18189                 failure: this.handleFailure,
18190                 scope: this,
18191                 argument: {callback: callback, node: node},
18192                 params: this.getParams(node)
18193             });
18194         }else{
18195             // if the load is cancelled, make sure we notify
18196             // the node that we are done
18197             if(typeof callback == "function"){
18198                 callback();
18199             }
18200         }
18201     },
18202
18203     isLoading : function(){
18204         return this.transId ? true : false;
18205     },
18206
18207     abort : function(){
18208         if(this.isLoading()){
18209             Roo.Ajax.abort(this.transId);
18210         }
18211     },
18212
18213     // private
18214     createNode : function(attr){
18215         // apply baseAttrs, nice idea Corey!
18216         if(this.baseAttrs){
18217             Roo.applyIf(attr, this.baseAttrs);
18218         }
18219         if(this.applyLoader !== false){
18220             attr.loader = this;
18221         }
18222         // uiProvider = depreciated..
18223         
18224         if(typeof(attr.uiProvider) == 'string'){
18225            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18226                 /**  eval:var:attr */ eval(attr.uiProvider);
18227         }
18228         if(typeof(this.uiProviders['default']) != 'undefined') {
18229             attr.uiProvider = this.uiProviders['default'];
18230         }
18231         
18232         this.fireEvent('create', this, attr);
18233         
18234         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18235         return(attr.leaf ?
18236                         new Roo.tree.TreeNode(attr) :
18237                         new Roo.tree.AsyncTreeNode(attr));
18238     },
18239
18240     processResponse : function(response, node, callback){
18241         var json = response.responseText;
18242         try {
18243             
18244             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18245             if (this.root !== false) {
18246                 o = o[this.root];
18247             }
18248             
18249             for(var i = 0, len = o.length; i < len; i++){
18250                 var n = this.createNode(o[i]);
18251                 if(n){
18252                     node.appendChild(n);
18253                 }
18254             }
18255             if(typeof callback == "function"){
18256                 callback(this, node);
18257             }
18258         }catch(e){
18259             this.handleFailure(response);
18260         }
18261     },
18262
18263     handleResponse : function(response){
18264         this.transId = false;
18265         var a = response.argument;
18266         this.processResponse(response, a.node, a.callback);
18267         this.fireEvent("load", this, a.node, response);
18268     },
18269
18270     handleFailure : function(response){
18271         this.transId = false;
18272         var a = response.argument;
18273         this.fireEvent("loadexception", this, a.node, response);
18274         if(typeof a.callback == "function"){
18275             a.callback(this, a.node);
18276         }
18277     }
18278 });/*
18279  * Based on:
18280  * Ext JS Library 1.1.1
18281  * Copyright(c) 2006-2007, Ext JS, LLC.
18282  *
18283  * Originally Released Under LGPL - original licence link has changed is not relivant.
18284  *
18285  * Fork - LGPL
18286  * <script type="text/javascript">
18287  */
18288
18289 /**
18290 * @class Roo.tree.TreeFilter
18291 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18292 * @param {TreePanel} tree
18293 * @param {Object} config (optional)
18294  */
18295 Roo.tree.TreeFilter = function(tree, config){
18296     this.tree = tree;
18297     this.filtered = {};
18298     Roo.apply(this, config);
18299 };
18300
18301 Roo.tree.TreeFilter.prototype = {
18302     clearBlank:false,
18303     reverse:false,
18304     autoClear:false,
18305     remove:false,
18306
18307      /**
18308      * Filter the data by a specific attribute.
18309      * @param {String/RegExp} value Either string that the attribute value
18310      * should start with or a RegExp to test against the attribute
18311      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18312      * @param {TreeNode} startNode (optional) The node to start the filter at.
18313      */
18314     filter : function(value, attr, startNode){
18315         attr = attr || "text";
18316         var f;
18317         if(typeof value == "string"){
18318             var vlen = value.length;
18319             // auto clear empty filter
18320             if(vlen == 0 && this.clearBlank){
18321                 this.clear();
18322                 return;
18323             }
18324             value = value.toLowerCase();
18325             f = function(n){
18326                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18327             };
18328         }else if(value.exec){ // regex?
18329             f = function(n){
18330                 return value.test(n.attributes[attr]);
18331             };
18332         }else{
18333             throw 'Illegal filter type, must be string or regex';
18334         }
18335         this.filterBy(f, null, startNode);
18336         },
18337
18338     /**
18339      * Filter by a function. The passed function will be called with each
18340      * node in the tree (or from the startNode). If the function returns true, the node is kept
18341      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18342      * @param {Function} fn The filter function
18343      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18344      */
18345     filterBy : function(fn, scope, startNode){
18346         startNode = startNode || this.tree.root;
18347         if(this.autoClear){
18348             this.clear();
18349         }
18350         var af = this.filtered, rv = this.reverse;
18351         var f = function(n){
18352             if(n == startNode){
18353                 return true;
18354             }
18355             if(af[n.id]){
18356                 return false;
18357             }
18358             var m = fn.call(scope || n, n);
18359             if(!m || rv){
18360                 af[n.id] = n;
18361                 n.ui.hide();
18362                 return false;
18363             }
18364             return true;
18365         };
18366         startNode.cascade(f);
18367         if(this.remove){
18368            for(var id in af){
18369                if(typeof id != "function"){
18370                    var n = af[id];
18371                    if(n && n.parentNode){
18372                        n.parentNode.removeChild(n);
18373                    }
18374                }
18375            }
18376         }
18377     },
18378
18379     /**
18380      * Clears the current filter. Note: with the "remove" option
18381      * set a filter cannot be cleared.
18382      */
18383     clear : function(){
18384         var t = this.tree;
18385         var af = this.filtered;
18386         for(var id in af){
18387             if(typeof id != "function"){
18388                 var n = af[id];
18389                 if(n){
18390                     n.ui.show();
18391                 }
18392             }
18393         }
18394         this.filtered = {};
18395     }
18396 };
18397 /*
18398  * Based on:
18399  * Ext JS Library 1.1.1
18400  * Copyright(c) 2006-2007, Ext JS, LLC.
18401  *
18402  * Originally Released Under LGPL - original licence link has changed is not relivant.
18403  *
18404  * Fork - LGPL
18405  * <script type="text/javascript">
18406  */
18407  
18408
18409 /**
18410  * @class Roo.tree.TreeSorter
18411  * Provides sorting of nodes in a TreePanel
18412  * 
18413  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18414  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18415  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18416  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18417  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18418  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18419  * @constructor
18420  * @param {TreePanel} tree
18421  * @param {Object} config
18422  */
18423 Roo.tree.TreeSorter = function(tree, config){
18424     Roo.apply(this, config);
18425     tree.on("beforechildrenrendered", this.doSort, this);
18426     tree.on("append", this.updateSort, this);
18427     tree.on("insert", this.updateSort, this);
18428     
18429     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18430     var p = this.property || "text";
18431     var sortType = this.sortType;
18432     var fs = this.folderSort;
18433     var cs = this.caseSensitive === true;
18434     var leafAttr = this.leafAttr || 'leaf';
18435
18436     this.sortFn = function(n1, n2){
18437         if(fs){
18438             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18439                 return 1;
18440             }
18441             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18442                 return -1;
18443             }
18444         }
18445         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18446         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18447         if(v1 < v2){
18448                         return dsc ? +1 : -1;
18449                 }else if(v1 > v2){
18450                         return dsc ? -1 : +1;
18451         }else{
18452                 return 0;
18453         }
18454     };
18455 };
18456
18457 Roo.tree.TreeSorter.prototype = {
18458     doSort : function(node){
18459         node.sort(this.sortFn);
18460     },
18461     
18462     compareNodes : function(n1, n2){
18463         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18464     },
18465     
18466     updateSort : function(tree, node){
18467         if(node.childrenRendered){
18468             this.doSort.defer(1, this, [node]);
18469         }
18470     }
18471 };/*
18472  * Based on:
18473  * Ext JS Library 1.1.1
18474  * Copyright(c) 2006-2007, Ext JS, LLC.
18475  *
18476  * Originally Released Under LGPL - original licence link has changed is not relivant.
18477  *
18478  * Fork - LGPL
18479  * <script type="text/javascript">
18480  */
18481
18482 if(Roo.dd.DropZone){
18483     
18484 Roo.tree.TreeDropZone = function(tree, config){
18485     this.allowParentInsert = false;
18486     this.allowContainerDrop = false;
18487     this.appendOnly = false;
18488     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18489     this.tree = tree;
18490     this.lastInsertClass = "x-tree-no-status";
18491     this.dragOverData = {};
18492 };
18493
18494 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18495     ddGroup : "TreeDD",
18496     
18497     expandDelay : 1000,
18498     
18499     expandNode : function(node){
18500         if(node.hasChildNodes() && !node.isExpanded()){
18501             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18502         }
18503     },
18504     
18505     queueExpand : function(node){
18506         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18507     },
18508     
18509     cancelExpand : function(){
18510         if(this.expandProcId){
18511             clearTimeout(this.expandProcId);
18512             this.expandProcId = false;
18513         }
18514     },
18515     
18516     isValidDropPoint : function(n, pt, dd, e, data){
18517         if(!n || !data){ return false; }
18518         var targetNode = n.node;
18519         var dropNode = data.node;
18520         // default drop rules
18521         if(!(targetNode && targetNode.isTarget && pt)){
18522             return false;
18523         }
18524         if(pt == "append" && targetNode.allowChildren === false){
18525             return false;
18526         }
18527         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18528             return false;
18529         }
18530         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18531             return false;
18532         }
18533         // reuse the object
18534         var overEvent = this.dragOverData;
18535         overEvent.tree = this.tree;
18536         overEvent.target = targetNode;
18537         overEvent.data = data;
18538         overEvent.point = pt;
18539         overEvent.source = dd;
18540         overEvent.rawEvent = e;
18541         overEvent.dropNode = dropNode;
18542         overEvent.cancel = false;  
18543         var result = this.tree.fireEvent("nodedragover", overEvent);
18544         return overEvent.cancel === false && result !== false;
18545     },
18546     
18547     getDropPoint : function(e, n, dd){
18548         var tn = n.node;
18549         if(tn.isRoot){
18550             return tn.allowChildren !== false ? "append" : false; // always append for root
18551         }
18552         var dragEl = n.ddel;
18553         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18554         var y = Roo.lib.Event.getPageY(e);
18555         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18556         
18557         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18558         var noAppend = tn.allowChildren === false;
18559         if(this.appendOnly || tn.parentNode.allowChildren === false){
18560             return noAppend ? false : "append";
18561         }
18562         var noBelow = false;
18563         if(!this.allowParentInsert){
18564             noBelow = tn.hasChildNodes() && tn.isExpanded();
18565         }
18566         var q = (b - t) / (noAppend ? 2 : 3);
18567         if(y >= t && y < (t + q)){
18568             return "above";
18569         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18570             return "below";
18571         }else{
18572             return "append";
18573         }
18574     },
18575     
18576     onNodeEnter : function(n, dd, e, data){
18577         this.cancelExpand();
18578     },
18579     
18580     onNodeOver : function(n, dd, e, data){
18581         var pt = this.getDropPoint(e, n, dd);
18582         var node = n.node;
18583         
18584         // auto node expand check
18585         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18586             this.queueExpand(node);
18587         }else if(pt != "append"){
18588             this.cancelExpand();
18589         }
18590         
18591         // set the insert point style on the target node
18592         var returnCls = this.dropNotAllowed;
18593         if(this.isValidDropPoint(n, pt, dd, e, data)){
18594            if(pt){
18595                var el = n.ddel;
18596                var cls;
18597                if(pt == "above"){
18598                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18599                    cls = "x-tree-drag-insert-above";
18600                }else if(pt == "below"){
18601                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18602                    cls = "x-tree-drag-insert-below";
18603                }else{
18604                    returnCls = "x-tree-drop-ok-append";
18605                    cls = "x-tree-drag-append";
18606                }
18607                if(this.lastInsertClass != cls){
18608                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18609                    this.lastInsertClass = cls;
18610                }
18611            }
18612        }
18613        return returnCls;
18614     },
18615     
18616     onNodeOut : function(n, dd, e, data){
18617         this.cancelExpand();
18618         this.removeDropIndicators(n);
18619     },
18620     
18621     onNodeDrop : function(n, dd, e, data){
18622         var point = this.getDropPoint(e, n, dd);
18623         var targetNode = n.node;
18624         targetNode.ui.startDrop();
18625         if(!this.isValidDropPoint(n, point, dd, e, data)){
18626             targetNode.ui.endDrop();
18627             return false;
18628         }
18629         // first try to find the drop node
18630         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18631         var dropEvent = {
18632             tree : this.tree,
18633             target: targetNode,
18634             data: data,
18635             point: point,
18636             source: dd,
18637             rawEvent: e,
18638             dropNode: dropNode,
18639             cancel: !dropNode   
18640         };
18641         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18642         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18643             targetNode.ui.endDrop();
18644             return false;
18645         }
18646         // allow target changing
18647         targetNode = dropEvent.target;
18648         if(point == "append" && !targetNode.isExpanded()){
18649             targetNode.expand(false, null, function(){
18650                 this.completeDrop(dropEvent);
18651             }.createDelegate(this));
18652         }else{
18653             this.completeDrop(dropEvent);
18654         }
18655         return true;
18656     },
18657     
18658     completeDrop : function(de){
18659         var ns = de.dropNode, p = de.point, t = de.target;
18660         if(!(ns instanceof Array)){
18661             ns = [ns];
18662         }
18663         var n;
18664         for(var i = 0, len = ns.length; i < len; i++){
18665             n = ns[i];
18666             if(p == "above"){
18667                 t.parentNode.insertBefore(n, t);
18668             }else if(p == "below"){
18669                 t.parentNode.insertBefore(n, t.nextSibling);
18670             }else{
18671                 t.appendChild(n);
18672             }
18673         }
18674         n.ui.focus();
18675         if(this.tree.hlDrop){
18676             n.ui.highlight();
18677         }
18678         t.ui.endDrop();
18679         this.tree.fireEvent("nodedrop", de);
18680     },
18681     
18682     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18683         if(this.tree.hlDrop){
18684             dropNode.ui.focus();
18685             dropNode.ui.highlight();
18686         }
18687         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18688     },
18689     
18690     getTree : function(){
18691         return this.tree;
18692     },
18693     
18694     removeDropIndicators : function(n){
18695         if(n && n.ddel){
18696             var el = n.ddel;
18697             Roo.fly(el).removeClass([
18698                     "x-tree-drag-insert-above",
18699                     "x-tree-drag-insert-below",
18700                     "x-tree-drag-append"]);
18701             this.lastInsertClass = "_noclass";
18702         }
18703     },
18704     
18705     beforeDragDrop : function(target, e, id){
18706         this.cancelExpand();
18707         return true;
18708     },
18709     
18710     afterRepair : function(data){
18711         if(data && Roo.enableFx){
18712             data.node.ui.highlight();
18713         }
18714         this.hideProxy();
18715     }    
18716 });
18717
18718 }
18719 /*
18720  * Based on:
18721  * Ext JS Library 1.1.1
18722  * Copyright(c) 2006-2007, Ext JS, LLC.
18723  *
18724  * Originally Released Under LGPL - original licence link has changed is not relivant.
18725  *
18726  * Fork - LGPL
18727  * <script type="text/javascript">
18728  */
18729  
18730
18731 if(Roo.dd.DragZone){
18732 Roo.tree.TreeDragZone = function(tree, config){
18733     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18734     this.tree = tree;
18735 };
18736
18737 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18738     ddGroup : "TreeDD",
18739     
18740     onBeforeDrag : function(data, e){
18741         var n = data.node;
18742         return n && n.draggable && !n.disabled;
18743     },
18744     
18745     onInitDrag : function(e){
18746         var data = this.dragData;
18747         this.tree.getSelectionModel().select(data.node);
18748         this.proxy.update("");
18749         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18750         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18751     },
18752     
18753     getRepairXY : function(e, data){
18754         return data.node.ui.getDDRepairXY();
18755     },
18756     
18757     onEndDrag : function(data, e){
18758         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18759     },
18760     
18761     onValidDrop : function(dd, e, id){
18762         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18763         this.hideProxy();
18764     },
18765     
18766     beforeInvalidDrop : function(e, id){
18767         // this scrolls the original position back into view
18768         var sm = this.tree.getSelectionModel();
18769         sm.clearSelections();
18770         sm.select(this.dragData.node);
18771     }
18772 });
18773 }/*
18774  * Based on:
18775  * Ext JS Library 1.1.1
18776  * Copyright(c) 2006-2007, Ext JS, LLC.
18777  *
18778  * Originally Released Under LGPL - original licence link has changed is not relivant.
18779  *
18780  * Fork - LGPL
18781  * <script type="text/javascript">
18782  */
18783 /**
18784  * @class Roo.tree.TreeEditor
18785  * @extends Roo.Editor
18786  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18787  * as the editor field.
18788  * @constructor
18789  * @param {TreePanel} tree
18790  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18791  */
18792 Roo.tree.TreeEditor = function(tree, config){
18793     config = config || {};
18794     var field = config.events ? config : new Roo.form.TextField(config);
18795     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18796
18797     this.tree = tree;
18798
18799     tree.on('beforeclick', this.beforeNodeClick, this);
18800     tree.getTreeEl().on('mousedown', this.hide, this);
18801     this.on('complete', this.updateNode, this);
18802     this.on('beforestartedit', this.fitToTree, this);
18803     this.on('startedit', this.bindScroll, this, {delay:10});
18804     this.on('specialkey', this.onSpecialKey, this);
18805 };
18806
18807 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18808     /**
18809      * @cfg {String} alignment
18810      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18811      */
18812     alignment: "l-l",
18813     // inherit
18814     autoSize: false,
18815     /**
18816      * @cfg {Boolean} hideEl
18817      * True to hide the bound element while the editor is displayed (defaults to false)
18818      */
18819     hideEl : false,
18820     /**
18821      * @cfg {String} cls
18822      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18823      */
18824     cls: "x-small-editor x-tree-editor",
18825     /**
18826      * @cfg {Boolean} shim
18827      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18828      */
18829     shim:false,
18830     // inherit
18831     shadow:"frame",
18832     /**
18833      * @cfg {Number} maxWidth
18834      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18835      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18836      * scroll and client offsets into account prior to each edit.
18837      */
18838     maxWidth: 250,
18839
18840     editDelay : 350,
18841
18842     // private
18843     fitToTree : function(ed, el){
18844         var td = this.tree.getTreeEl().dom, nd = el.dom;
18845         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18846             td.scrollLeft = nd.offsetLeft;
18847         }
18848         var w = Math.min(
18849                 this.maxWidth,
18850                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18851         this.setSize(w, '');
18852     },
18853
18854     // private
18855     triggerEdit : function(node){
18856         this.completeEdit();
18857         this.editNode = node;
18858         this.startEdit(node.ui.textNode, node.text);
18859     },
18860
18861     // private
18862     bindScroll : function(){
18863         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18864     },
18865
18866     // private
18867     beforeNodeClick : function(node, e){
18868         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18869         this.lastClick = new Date();
18870         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18871             e.stopEvent();
18872             this.triggerEdit(node);
18873             return false;
18874         }
18875     },
18876
18877     // private
18878     updateNode : function(ed, value){
18879         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18880         this.editNode.setText(value);
18881     },
18882
18883     // private
18884     onHide : function(){
18885         Roo.tree.TreeEditor.superclass.onHide.call(this);
18886         if(this.editNode){
18887             this.editNode.ui.focus();
18888         }
18889     },
18890
18891     // private
18892     onSpecialKey : function(field, e){
18893         var k = e.getKey();
18894         if(k == e.ESC){
18895             e.stopEvent();
18896             this.cancelEdit();
18897         }else if(k == e.ENTER && !e.hasModifier()){
18898             e.stopEvent();
18899             this.completeEdit();
18900         }
18901     }
18902 });//<Script type="text/javascript">
18903 /*
18904  * Based on:
18905  * Ext JS Library 1.1.1
18906  * Copyright(c) 2006-2007, Ext JS, LLC.
18907  *
18908  * Originally Released Under LGPL - original licence link has changed is not relivant.
18909  *
18910  * Fork - LGPL
18911  * <script type="text/javascript">
18912  */
18913  
18914 /**
18915  * Not documented??? - probably should be...
18916  */
18917
18918 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18919     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18920     
18921     renderElements : function(n, a, targetNode, bulkRender){
18922         //consel.log("renderElements?");
18923         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18924
18925         var t = n.getOwnerTree();
18926         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18927         
18928         var cols = t.columns;
18929         var bw = t.borderWidth;
18930         var c = cols[0];
18931         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18932          var cb = typeof a.checked == "boolean";
18933         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18934         var colcls = 'x-t-' + tid + '-c0';
18935         var buf = [
18936             '<li class="x-tree-node">',
18937             
18938                 
18939                 '<div class="x-tree-node-el ', a.cls,'">',
18940                     // extran...
18941                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
18942                 
18943                 
18944                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
18945                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
18946                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
18947                            (a.icon ? ' x-tree-node-inline-icon' : ''),
18948                            (a.iconCls ? ' '+a.iconCls : ''),
18949                            '" unselectable="on" />',
18950                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
18951                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
18952                              
18953                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18954                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
18955                             '<span unselectable="on" qtip="' + tx + '">',
18956                              tx,
18957                              '</span></a>' ,
18958                     '</div>',
18959                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
18960                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
18961                  ];
18962         for(var i = 1, len = cols.length; i < len; i++){
18963             c = cols[i];
18964             colcls = 'x-t-' + tid + '-c' +i;
18965             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18966             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
18967                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
18968                       "</div>");
18969          }
18970          
18971          buf.push(
18972             '</a>',
18973             '<div class="x-clear"></div></div>',
18974             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18975             "</li>");
18976         
18977         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18978             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18979                                 n.nextSibling.ui.getEl(), buf.join(""));
18980         }else{
18981             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18982         }
18983         var el = this.wrap.firstChild;
18984         this.elRow = el;
18985         this.elNode = el.firstChild;
18986         this.ranchor = el.childNodes[1];
18987         this.ctNode = this.wrap.childNodes[1];
18988         var cs = el.firstChild.childNodes;
18989         this.indentNode = cs[0];
18990         this.ecNode = cs[1];
18991         this.iconNode = cs[2];
18992         var index = 3;
18993         if(cb){
18994             this.checkbox = cs[3];
18995             index++;
18996         }
18997         this.anchor = cs[index];
18998         
18999         this.textNode = cs[index].firstChild;
19000         
19001         //el.on("click", this.onClick, this);
19002         //el.on("dblclick", this.onDblClick, this);
19003         
19004         
19005        // console.log(this);
19006     },
19007     initEvents : function(){
19008         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19009         
19010             
19011         var a = this.ranchor;
19012
19013         var el = Roo.get(a);
19014
19015         if(Roo.isOpera){ // opera render bug ignores the CSS
19016             el.setStyle("text-decoration", "none");
19017         }
19018
19019         el.on("click", this.onClick, this);
19020         el.on("dblclick", this.onDblClick, this);
19021         el.on("contextmenu", this.onContextMenu, this);
19022         
19023     },
19024     
19025     /*onSelectedChange : function(state){
19026         if(state){
19027             this.focus();
19028             this.addClass("x-tree-selected");
19029         }else{
19030             //this.blur();
19031             this.removeClass("x-tree-selected");
19032         }
19033     },*/
19034     addClass : function(cls){
19035         if(this.elRow){
19036             Roo.fly(this.elRow).addClass(cls);
19037         }
19038         
19039     },
19040     
19041     
19042     removeClass : function(cls){
19043         if(this.elRow){
19044             Roo.fly(this.elRow).removeClass(cls);
19045         }
19046     }
19047
19048     
19049     
19050 });//<Script type="text/javascript">
19051
19052 /*
19053  * Based on:
19054  * Ext JS Library 1.1.1
19055  * Copyright(c) 2006-2007, Ext JS, LLC.
19056  *
19057  * Originally Released Under LGPL - original licence link has changed is not relivant.
19058  *
19059  * Fork - LGPL
19060  * <script type="text/javascript">
19061  */
19062  
19063
19064 /**
19065  * @class Roo.tree.ColumnTree
19066  * @extends Roo.data.TreePanel
19067  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19068  * @cfg {int} borderWidth  compined right/left border allowance
19069  * @constructor
19070  * @param {String/HTMLElement/Element} el The container element
19071  * @param {Object} config
19072  */
19073 Roo.tree.ColumnTree =  function(el, config)
19074 {
19075    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19076    this.addEvents({
19077         /**
19078         * @event resize
19079         * Fire this event on a container when it resizes
19080         * @param {int} w Width
19081         * @param {int} h Height
19082         */
19083        "resize" : true
19084     });
19085     this.on('resize', this.onResize, this);
19086 };
19087
19088 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19089     //lines:false,
19090     
19091     
19092     borderWidth: Roo.isBorderBox ? 0 : 2, 
19093     headEls : false,
19094     
19095     render : function(){
19096         // add the header.....
19097        
19098         Roo.tree.ColumnTree.superclass.render.apply(this);
19099         
19100         this.el.addClass('x-column-tree');
19101         
19102         this.headers = this.el.createChild(
19103             {cls:'x-tree-headers'},this.innerCt.dom);
19104    
19105         var cols = this.columns, c;
19106         var totalWidth = 0;
19107         this.headEls = [];
19108         var  len = cols.length;
19109         for(var i = 0; i < len; i++){
19110              c = cols[i];
19111              totalWidth += c.width;
19112             this.headEls.push(this.headers.createChild({
19113                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19114                  cn: {
19115                      cls:'x-tree-hd-text',
19116                      html: c.header
19117                  },
19118                  style:'width:'+(c.width-this.borderWidth)+'px;'
19119              }));
19120         }
19121         this.headers.createChild({cls:'x-clear'});
19122         // prevent floats from wrapping when clipped
19123         this.headers.setWidth(totalWidth);
19124         //this.innerCt.setWidth(totalWidth);
19125         this.innerCt.setStyle({ overflow: 'auto' });
19126         this.onResize(this.width, this.height);
19127              
19128         
19129     },
19130     onResize : function(w,h)
19131     {
19132         this.height = h;
19133         this.width = w;
19134         // resize cols..
19135         this.innerCt.setWidth(this.width);
19136         this.innerCt.setHeight(this.height-20);
19137         
19138         // headers...
19139         var cols = this.columns, c;
19140         var totalWidth = 0;
19141         var expEl = false;
19142         var len = cols.length;
19143         for(var i = 0; i < len; i++){
19144             c = cols[i];
19145             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19146                 // it's the expander..
19147                 expEl  = this.headEls[i];
19148                 continue;
19149             }
19150             totalWidth += c.width;
19151             
19152         }
19153         if (expEl) {
19154             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19155         }
19156         this.headers.setWidth(w-20);
19157
19158         
19159         
19160         
19161     }
19162 });
19163 /*
19164  * Based on:
19165  * Ext JS Library 1.1.1
19166  * Copyright(c) 2006-2007, Ext JS, LLC.
19167  *
19168  * Originally Released Under LGPL - original licence link has changed is not relivant.
19169  *
19170  * Fork - LGPL
19171  * <script type="text/javascript">
19172  */
19173  
19174 /**
19175  * @class Roo.menu.Menu
19176  * @extends Roo.util.Observable
19177  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19178  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19179  * @constructor
19180  * Creates a new Menu
19181  * @param {Object} config Configuration options
19182  */
19183 Roo.menu.Menu = function(config){
19184     Roo.apply(this, config);
19185     this.id = this.id || Roo.id();
19186     this.addEvents({
19187         /**
19188          * @event beforeshow
19189          * Fires before this menu is displayed
19190          * @param {Roo.menu.Menu} this
19191          */
19192         beforeshow : true,
19193         /**
19194          * @event beforehide
19195          * Fires before this menu is hidden
19196          * @param {Roo.menu.Menu} this
19197          */
19198         beforehide : true,
19199         /**
19200          * @event show
19201          * Fires after this menu is displayed
19202          * @param {Roo.menu.Menu} this
19203          */
19204         show : true,
19205         /**
19206          * @event hide
19207          * Fires after this menu is hidden
19208          * @param {Roo.menu.Menu} this
19209          */
19210         hide : true,
19211         /**
19212          * @event click
19213          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19214          * @param {Roo.menu.Menu} this
19215          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19216          * @param {Roo.EventObject} e
19217          */
19218         click : true,
19219         /**
19220          * @event mouseover
19221          * Fires when the mouse is hovering over this menu
19222          * @param {Roo.menu.Menu} this
19223          * @param {Roo.EventObject} e
19224          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19225          */
19226         mouseover : true,
19227         /**
19228          * @event mouseout
19229          * Fires when the mouse exits this menu
19230          * @param {Roo.menu.Menu} this
19231          * @param {Roo.EventObject} e
19232          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19233          */
19234         mouseout : true,
19235         /**
19236          * @event itemclick
19237          * Fires when a menu item contained in this menu is clicked
19238          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19239          * @param {Roo.EventObject} e
19240          */
19241         itemclick: true
19242     });
19243     if (this.registerMenu) {
19244         Roo.menu.MenuMgr.register(this);
19245     }
19246     
19247     var mis = this.items;
19248     this.items = new Roo.util.MixedCollection();
19249     if(mis){
19250         this.add.apply(this, mis);
19251     }
19252 };
19253
19254 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19255     /**
19256      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19257      */
19258     minWidth : 120,
19259     /**
19260      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19261      * for bottom-right shadow (defaults to "sides")
19262      */
19263     shadow : "sides",
19264     /**
19265      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19266      * this menu (defaults to "tl-tr?")
19267      */
19268     subMenuAlign : "tl-tr?",
19269     /**
19270      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19271      * relative to its element of origin (defaults to "tl-bl?")
19272      */
19273     defaultAlign : "tl-bl?",
19274     /**
19275      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19276      */
19277     allowOtherMenus : false,
19278     /**
19279      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19280      */
19281     registerMenu : true,
19282
19283     hidden:true,
19284
19285     // private
19286     render : function(){
19287         if(this.el){
19288             return;
19289         }
19290         var el = this.el = new Roo.Layer({
19291             cls: "x-menu",
19292             shadow:this.shadow,
19293             constrain: false,
19294             parentEl: this.parentEl || document.body,
19295             zindex:15000
19296         });
19297
19298         this.keyNav = new Roo.menu.MenuNav(this);
19299
19300         if(this.plain){
19301             el.addClass("x-menu-plain");
19302         }
19303         if(this.cls){
19304             el.addClass(this.cls);
19305         }
19306         // generic focus element
19307         this.focusEl = el.createChild({
19308             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19309         });
19310         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19311         ul.on("click", this.onClick, this);
19312         ul.on("mouseover", this.onMouseOver, this);
19313         ul.on("mouseout", this.onMouseOut, this);
19314         this.items.each(function(item){
19315             var li = document.createElement("li");
19316             li.className = "x-menu-list-item";
19317             ul.dom.appendChild(li);
19318             item.render(li, this);
19319         }, this);
19320         this.ul = ul;
19321         this.autoWidth();
19322     },
19323
19324     // private
19325     autoWidth : function(){
19326         var el = this.el, ul = this.ul;
19327         if(!el){
19328             return;
19329         }
19330         var w = this.width;
19331         if(w){
19332             el.setWidth(w);
19333         }else if(Roo.isIE){
19334             el.setWidth(this.minWidth);
19335             var t = el.dom.offsetWidth; // force recalc
19336             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19337         }
19338     },
19339
19340     // private
19341     delayAutoWidth : function(){
19342         if(this.rendered){
19343             if(!this.awTask){
19344                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19345             }
19346             this.awTask.delay(20);
19347         }
19348     },
19349
19350     // private
19351     findTargetItem : function(e){
19352         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19353         if(t && t.menuItemId){
19354             return this.items.get(t.menuItemId);
19355         }
19356     },
19357
19358     // private
19359     onClick : function(e){
19360         var t;
19361         if(t = this.findTargetItem(e)){
19362             t.onClick(e);
19363             this.fireEvent("click", this, t, e);
19364         }
19365     },
19366
19367     // private
19368     setActiveItem : function(item, autoExpand){
19369         if(item != this.activeItem){
19370             if(this.activeItem){
19371                 this.activeItem.deactivate();
19372             }
19373             this.activeItem = item;
19374             item.activate(autoExpand);
19375         }else if(autoExpand){
19376             item.expandMenu();
19377         }
19378     },
19379
19380     // private
19381     tryActivate : function(start, step){
19382         var items = this.items;
19383         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19384             var item = items.get(i);
19385             if(!item.disabled && item.canActivate){
19386                 this.setActiveItem(item, false);
19387                 return item;
19388             }
19389         }
19390         return false;
19391     },
19392
19393     // private
19394     onMouseOver : function(e){
19395         var t;
19396         if(t = this.findTargetItem(e)){
19397             if(t.canActivate && !t.disabled){
19398                 this.setActiveItem(t, true);
19399             }
19400         }
19401         this.fireEvent("mouseover", this, e, t);
19402     },
19403
19404     // private
19405     onMouseOut : function(e){
19406         var t;
19407         if(t = this.findTargetItem(e)){
19408             if(t == this.activeItem && t.shouldDeactivate(e)){
19409                 this.activeItem.deactivate();
19410                 delete this.activeItem;
19411             }
19412         }
19413         this.fireEvent("mouseout", this, e, t);
19414     },
19415
19416     /**
19417      * Read-only.  Returns true if the menu is currently displayed, else false.
19418      * @type Boolean
19419      */
19420     isVisible : function(){
19421         return this.el && !this.hidden;
19422     },
19423
19424     /**
19425      * Displays this menu relative to another element
19426      * @param {String/HTMLElement/Roo.Element} element The element to align to
19427      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19428      * the element (defaults to this.defaultAlign)
19429      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19430      */
19431     show : function(el, pos, parentMenu){
19432         this.parentMenu = parentMenu;
19433         if(!this.el){
19434             this.render();
19435         }
19436         this.fireEvent("beforeshow", this);
19437         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19438     },
19439
19440     /**
19441      * Displays this menu at a specific xy position
19442      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19443      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19444      */
19445     showAt : function(xy, parentMenu, /* private: */_e){
19446         this.parentMenu = parentMenu;
19447         if(!this.el){
19448             this.render();
19449         }
19450         if(_e !== false){
19451             this.fireEvent("beforeshow", this);
19452             xy = this.el.adjustForConstraints(xy);
19453         }
19454         this.el.setXY(xy);
19455         this.el.show();
19456         this.hidden = false;
19457         this.focus();
19458         this.fireEvent("show", this);
19459     },
19460
19461     focus : function(){
19462         if(!this.hidden){
19463             this.doFocus.defer(50, this);
19464         }
19465     },
19466
19467     doFocus : function(){
19468         if(!this.hidden){
19469             this.focusEl.focus();
19470         }
19471     },
19472
19473     /**
19474      * Hides this menu and optionally all parent menus
19475      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19476      */
19477     hide : function(deep){
19478         if(this.el && this.isVisible()){
19479             this.fireEvent("beforehide", this);
19480             if(this.activeItem){
19481                 this.activeItem.deactivate();
19482                 this.activeItem = null;
19483             }
19484             this.el.hide();
19485             this.hidden = true;
19486             this.fireEvent("hide", this);
19487         }
19488         if(deep === true && this.parentMenu){
19489             this.parentMenu.hide(true);
19490         }
19491     },
19492
19493     /**
19494      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19495      * Any of the following are valid:
19496      * <ul>
19497      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19498      * <li>An HTMLElement object which will be converted to a menu item</li>
19499      * <li>A menu item config object that will be created as a new menu item</li>
19500      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19501      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19502      * </ul>
19503      * Usage:
19504      * <pre><code>
19505 // Create the menu
19506 var menu = new Roo.menu.Menu();
19507
19508 // Create a menu item to add by reference
19509 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19510
19511 // Add a bunch of items at once using different methods.
19512 // Only the last item added will be returned.
19513 var item = menu.add(
19514     menuItem,                // add existing item by ref
19515     'Dynamic Item',          // new TextItem
19516     '-',                     // new separator
19517     { text: 'Config Item' }  // new item by config
19518 );
19519 </code></pre>
19520      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19521      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19522      */
19523     add : function(){
19524         var a = arguments, l = a.length, item;
19525         for(var i = 0; i < l; i++){
19526             var el = a[i];
19527             if ((typeof(el) == "object") && el.xtype && el.xns) {
19528                 el = Roo.factory(el, Roo.menu);
19529             }
19530             
19531             if(el.render){ // some kind of Item
19532                 item = this.addItem(el);
19533             }else if(typeof el == "string"){ // string
19534                 if(el == "separator" || el == "-"){
19535                     item = this.addSeparator();
19536                 }else{
19537                     item = this.addText(el);
19538                 }
19539             }else if(el.tagName || el.el){ // element
19540                 item = this.addElement(el);
19541             }else if(typeof el == "object"){ // must be menu item config?
19542                 item = this.addMenuItem(el);
19543             }
19544         }
19545         return item;
19546     },
19547
19548     /**
19549      * Returns this menu's underlying {@link Roo.Element} object
19550      * @return {Roo.Element} The element
19551      */
19552     getEl : function(){
19553         if(!this.el){
19554             this.render();
19555         }
19556         return this.el;
19557     },
19558
19559     /**
19560      * Adds a separator bar to the menu
19561      * @return {Roo.menu.Item} The menu item that was added
19562      */
19563     addSeparator : function(){
19564         return this.addItem(new Roo.menu.Separator());
19565     },
19566
19567     /**
19568      * Adds an {@link Roo.Element} object to the menu
19569      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19570      * @return {Roo.menu.Item} The menu item that was added
19571      */
19572     addElement : function(el){
19573         return this.addItem(new Roo.menu.BaseItem(el));
19574     },
19575
19576     /**
19577      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19578      * @param {Roo.menu.Item} item The menu item to add
19579      * @return {Roo.menu.Item} The menu item that was added
19580      */
19581     addItem : function(item){
19582         this.items.add(item);
19583         if(this.ul){
19584             var li = document.createElement("li");
19585             li.className = "x-menu-list-item";
19586             this.ul.dom.appendChild(li);
19587             item.render(li, this);
19588             this.delayAutoWidth();
19589         }
19590         return item;
19591     },
19592
19593     /**
19594      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19595      * @param {Object} config A MenuItem config object
19596      * @return {Roo.menu.Item} The menu item that was added
19597      */
19598     addMenuItem : function(config){
19599         if(!(config instanceof Roo.menu.Item)){
19600             if(typeof config.checked == "boolean"){ // must be check menu item config?
19601                 config = new Roo.menu.CheckItem(config);
19602             }else{
19603                 config = new Roo.menu.Item(config);
19604             }
19605         }
19606         return this.addItem(config);
19607     },
19608
19609     /**
19610      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19611      * @param {String} text The text to display in the menu item
19612      * @return {Roo.menu.Item} The menu item that was added
19613      */
19614     addText : function(text){
19615         return this.addItem(new Roo.menu.TextItem({ text : text }));
19616     },
19617
19618     /**
19619      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19620      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19621      * @param {Roo.menu.Item} item The menu item to add
19622      * @return {Roo.menu.Item} The menu item that was added
19623      */
19624     insert : function(index, item){
19625         this.items.insert(index, item);
19626         if(this.ul){
19627             var li = document.createElement("li");
19628             li.className = "x-menu-list-item";
19629             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19630             item.render(li, this);
19631             this.delayAutoWidth();
19632         }
19633         return item;
19634     },
19635
19636     /**
19637      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19638      * @param {Roo.menu.Item} item The menu item to remove
19639      */
19640     remove : function(item){
19641         this.items.removeKey(item.id);
19642         item.destroy();
19643     },
19644
19645     /**
19646      * Removes and destroys all items in the menu
19647      */
19648     removeAll : function(){
19649         var f;
19650         while(f = this.items.first()){
19651             this.remove(f);
19652         }
19653     }
19654 });
19655
19656 // MenuNav is a private utility class used internally by the Menu
19657 Roo.menu.MenuNav = function(menu){
19658     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19659     this.scope = this.menu = menu;
19660 };
19661
19662 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19663     doRelay : function(e, h){
19664         var k = e.getKey();
19665         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19666             this.menu.tryActivate(0, 1);
19667             return false;
19668         }
19669         return h.call(this.scope || this, e, this.menu);
19670     },
19671
19672     up : function(e, m){
19673         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19674             m.tryActivate(m.items.length-1, -1);
19675         }
19676     },
19677
19678     down : function(e, m){
19679         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19680             m.tryActivate(0, 1);
19681         }
19682     },
19683
19684     right : function(e, m){
19685         if(m.activeItem){
19686             m.activeItem.expandMenu(true);
19687         }
19688     },
19689
19690     left : function(e, m){
19691         m.hide();
19692         if(m.parentMenu && m.parentMenu.activeItem){
19693             m.parentMenu.activeItem.activate();
19694         }
19695     },
19696
19697     enter : function(e, m){
19698         if(m.activeItem){
19699             e.stopPropagation();
19700             m.activeItem.onClick(e);
19701             m.fireEvent("click", this, m.activeItem);
19702             return true;
19703         }
19704     }
19705 });/*
19706  * Based on:
19707  * Ext JS Library 1.1.1
19708  * Copyright(c) 2006-2007, Ext JS, LLC.
19709  *
19710  * Originally Released Under LGPL - original licence link has changed is not relivant.
19711  *
19712  * Fork - LGPL
19713  * <script type="text/javascript">
19714  */
19715  
19716 /**
19717  * @class Roo.menu.MenuMgr
19718  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19719  * @singleton
19720  */
19721 Roo.menu.MenuMgr = function(){
19722    var menus, active, groups = {}, attached = false, lastShow = new Date();
19723
19724    // private - called when first menu is created
19725    function init(){
19726        menus = {};
19727        active = new Roo.util.MixedCollection();
19728        Roo.get(document).addKeyListener(27, function(){
19729            if(active.length > 0){
19730                hideAll();
19731            }
19732        });
19733    }
19734
19735    // private
19736    function hideAll(){
19737        if(active && active.length > 0){
19738            var c = active.clone();
19739            c.each(function(m){
19740                m.hide();
19741            });
19742        }
19743    }
19744
19745    // private
19746    function onHide(m){
19747        active.remove(m);
19748        if(active.length < 1){
19749            Roo.get(document).un("mousedown", onMouseDown);
19750            attached = false;
19751        }
19752    }
19753
19754    // private
19755    function onShow(m){
19756        var last = active.last();
19757        lastShow = new Date();
19758        active.add(m);
19759        if(!attached){
19760            Roo.get(document).on("mousedown", onMouseDown);
19761            attached = true;
19762        }
19763        if(m.parentMenu){
19764           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19765           m.parentMenu.activeChild = m;
19766        }else if(last && last.isVisible()){
19767           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19768        }
19769    }
19770
19771    // private
19772    function onBeforeHide(m){
19773        if(m.activeChild){
19774            m.activeChild.hide();
19775        }
19776        if(m.autoHideTimer){
19777            clearTimeout(m.autoHideTimer);
19778            delete m.autoHideTimer;
19779        }
19780    }
19781
19782    // private
19783    function onBeforeShow(m){
19784        var pm = m.parentMenu;
19785        if(!pm && !m.allowOtherMenus){
19786            hideAll();
19787        }else if(pm && pm.activeChild && active != m){
19788            pm.activeChild.hide();
19789        }
19790    }
19791
19792    // private
19793    function onMouseDown(e){
19794        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19795            hideAll();
19796        }
19797    }
19798
19799    // private
19800    function onBeforeCheck(mi, state){
19801        if(state){
19802            var g = groups[mi.group];
19803            for(var i = 0, l = g.length; i < l; i++){
19804                if(g[i] != mi){
19805                    g[i].setChecked(false);
19806                }
19807            }
19808        }
19809    }
19810
19811    return {
19812
19813        /**
19814         * Hides all menus that are currently visible
19815         */
19816        hideAll : function(){
19817             hideAll();  
19818        },
19819
19820        // private
19821        register : function(menu){
19822            if(!menus){
19823                init();
19824            }
19825            menus[menu.id] = menu;
19826            menu.on("beforehide", onBeforeHide);
19827            menu.on("hide", onHide);
19828            menu.on("beforeshow", onBeforeShow);
19829            menu.on("show", onShow);
19830            var g = menu.group;
19831            if(g && menu.events["checkchange"]){
19832                if(!groups[g]){
19833                    groups[g] = [];
19834                }
19835                groups[g].push(menu);
19836                menu.on("checkchange", onCheck);
19837            }
19838        },
19839
19840         /**
19841          * Returns a {@link Roo.menu.Menu} object
19842          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19843          * be used to generate and return a new Menu instance.
19844          */
19845        get : function(menu){
19846            if(typeof menu == "string"){ // menu id
19847                return menus[menu];
19848            }else if(menu.events){  // menu instance
19849                return menu;
19850            }else if(typeof menu.length == 'number'){ // array of menu items?
19851                return new Roo.menu.Menu({items:menu});
19852            }else{ // otherwise, must be a config
19853                return new Roo.menu.Menu(menu);
19854            }
19855        },
19856
19857        // private
19858        unregister : function(menu){
19859            delete menus[menu.id];
19860            menu.un("beforehide", onBeforeHide);
19861            menu.un("hide", onHide);
19862            menu.un("beforeshow", onBeforeShow);
19863            menu.un("show", onShow);
19864            var g = menu.group;
19865            if(g && menu.events["checkchange"]){
19866                groups[g].remove(menu);
19867                menu.un("checkchange", onCheck);
19868            }
19869        },
19870
19871        // private
19872        registerCheckable : function(menuItem){
19873            var g = menuItem.group;
19874            if(g){
19875                if(!groups[g]){
19876                    groups[g] = [];
19877                }
19878                groups[g].push(menuItem);
19879                menuItem.on("beforecheckchange", onBeforeCheck);
19880            }
19881        },
19882
19883        // private
19884        unregisterCheckable : function(menuItem){
19885            var g = menuItem.group;
19886            if(g){
19887                groups[g].remove(menuItem);
19888                menuItem.un("beforecheckchange", onBeforeCheck);
19889            }
19890        }
19891    };
19892 }();/*
19893  * Based on:
19894  * Ext JS Library 1.1.1
19895  * Copyright(c) 2006-2007, Ext JS, LLC.
19896  *
19897  * Originally Released Under LGPL - original licence link has changed is not relivant.
19898  *
19899  * Fork - LGPL
19900  * <script type="text/javascript">
19901  */
19902  
19903
19904 /**
19905  * @class Roo.menu.BaseItem
19906  * @extends Roo.Component
19907  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19908  * management and base configuration options shared by all menu components.
19909  * @constructor
19910  * Creates a new BaseItem
19911  * @param {Object} config Configuration options
19912  */
19913 Roo.menu.BaseItem = function(config){
19914     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19915
19916     this.addEvents({
19917         /**
19918          * @event click
19919          * Fires when this item is clicked
19920          * @param {Roo.menu.BaseItem} this
19921          * @param {Roo.EventObject} e
19922          */
19923         click: true,
19924         /**
19925          * @event activate
19926          * Fires when this item is activated
19927          * @param {Roo.menu.BaseItem} this
19928          */
19929         activate : true,
19930         /**
19931          * @event deactivate
19932          * Fires when this item is deactivated
19933          * @param {Roo.menu.BaseItem} this
19934          */
19935         deactivate : true
19936     });
19937
19938     if(this.handler){
19939         this.on("click", this.handler, this.scope, true);
19940     }
19941 };
19942
19943 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
19944     /**
19945      * @cfg {Function} handler
19946      * A function that will handle the click event of this menu item (defaults to undefined)
19947      */
19948     /**
19949      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
19950      */
19951     canActivate : false,
19952     /**
19953      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
19954      */
19955     activeClass : "x-menu-item-active",
19956     /**
19957      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
19958      */
19959     hideOnClick : true,
19960     /**
19961      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
19962      */
19963     hideDelay : 100,
19964
19965     // private
19966     ctype: "Roo.menu.BaseItem",
19967
19968     // private
19969     actionMode : "container",
19970
19971     // private
19972     render : function(container, parentMenu){
19973         this.parentMenu = parentMenu;
19974         Roo.menu.BaseItem.superclass.render.call(this, container);
19975         this.container.menuItemId = this.id;
19976     },
19977
19978     // private
19979     onRender : function(container, position){
19980         this.el = Roo.get(this.el);
19981         container.dom.appendChild(this.el.dom);
19982     },
19983
19984     // private
19985     onClick : function(e){
19986         if(!this.disabled && this.fireEvent("click", this, e) !== false
19987                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
19988             this.handleClick(e);
19989         }else{
19990             e.stopEvent();
19991         }
19992     },
19993
19994     // private
19995     activate : function(){
19996         if(this.disabled){
19997             return false;
19998         }
19999         var li = this.container;
20000         li.addClass(this.activeClass);
20001         this.region = li.getRegion().adjust(2, 2, -2, -2);
20002         this.fireEvent("activate", this);
20003         return true;
20004     },
20005
20006     // private
20007     deactivate : function(){
20008         this.container.removeClass(this.activeClass);
20009         this.fireEvent("deactivate", this);
20010     },
20011
20012     // private
20013     shouldDeactivate : function(e){
20014         return !this.region || !this.region.contains(e.getPoint());
20015     },
20016
20017     // private
20018     handleClick : function(e){
20019         if(this.hideOnClick){
20020             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20021         }
20022     },
20023
20024     // private
20025     expandMenu : function(autoActivate){
20026         // do nothing
20027     },
20028
20029     // private
20030     hideMenu : function(){
20031         // do nothing
20032     }
20033 });/*
20034  * Based on:
20035  * Ext JS Library 1.1.1
20036  * Copyright(c) 2006-2007, Ext JS, LLC.
20037  *
20038  * Originally Released Under LGPL - original licence link has changed is not relivant.
20039  *
20040  * Fork - LGPL
20041  * <script type="text/javascript">
20042  */
20043  
20044 /**
20045  * @class Roo.menu.Adapter
20046  * @extends Roo.menu.BaseItem
20047  * 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.
20048  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20049  * @constructor
20050  * Creates a new Adapter
20051  * @param {Object} config Configuration options
20052  */
20053 Roo.menu.Adapter = function(component, config){
20054     Roo.menu.Adapter.superclass.constructor.call(this, config);
20055     this.component = component;
20056 };
20057 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20058     // private
20059     canActivate : true,
20060
20061     // private
20062     onRender : function(container, position){
20063         this.component.render(container);
20064         this.el = this.component.getEl();
20065     },
20066
20067     // private
20068     activate : function(){
20069         if(this.disabled){
20070             return false;
20071         }
20072         this.component.focus();
20073         this.fireEvent("activate", this);
20074         return true;
20075     },
20076
20077     // private
20078     deactivate : function(){
20079         this.fireEvent("deactivate", this);
20080     },
20081
20082     // private
20083     disable : function(){
20084         this.component.disable();
20085         Roo.menu.Adapter.superclass.disable.call(this);
20086     },
20087
20088     // private
20089     enable : function(){
20090         this.component.enable();
20091         Roo.menu.Adapter.superclass.enable.call(this);
20092     }
20093 });/*
20094  * Based on:
20095  * Ext JS Library 1.1.1
20096  * Copyright(c) 2006-2007, Ext JS, LLC.
20097  *
20098  * Originally Released Under LGPL - original licence link has changed is not relivant.
20099  *
20100  * Fork - LGPL
20101  * <script type="text/javascript">
20102  */
20103
20104 /**
20105  * @class Roo.menu.TextItem
20106  * @extends Roo.menu.BaseItem
20107  * Adds a static text string to a menu, usually used as either a heading or group separator.
20108  * Note: old style constructor with text is still supported.
20109  * 
20110  * @constructor
20111  * Creates a new TextItem
20112  * @param {Object} cfg Configuration
20113  */
20114 Roo.menu.TextItem = function(cfg){
20115     if (typeof(cfg) == 'string') {
20116         this.text = cfg;
20117     } else {
20118         Roo.apply(this,cfg);
20119     }
20120     
20121     Roo.menu.TextItem.superclass.constructor.call(this);
20122 };
20123
20124 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20125     /**
20126      * @cfg {Boolean} text Text to show on item.
20127      */
20128     text : '',
20129     
20130     /**
20131      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20132      */
20133     hideOnClick : false,
20134     /**
20135      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20136      */
20137     itemCls : "x-menu-text",
20138
20139     // private
20140     onRender : function(){
20141         var s = document.createElement("span");
20142         s.className = this.itemCls;
20143         s.innerHTML = this.text;
20144         this.el = s;
20145         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20146     }
20147 });/*
20148  * Based on:
20149  * Ext JS Library 1.1.1
20150  * Copyright(c) 2006-2007, Ext JS, LLC.
20151  *
20152  * Originally Released Under LGPL - original licence link has changed is not relivant.
20153  *
20154  * Fork - LGPL
20155  * <script type="text/javascript">
20156  */
20157
20158 /**
20159  * @class Roo.menu.Separator
20160  * @extends Roo.menu.BaseItem
20161  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20162  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20163  * @constructor
20164  * @param {Object} config Configuration options
20165  */
20166 Roo.menu.Separator = function(config){
20167     Roo.menu.Separator.superclass.constructor.call(this, config);
20168 };
20169
20170 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20171     /**
20172      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20173      */
20174     itemCls : "x-menu-sep",
20175     /**
20176      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20177      */
20178     hideOnClick : false,
20179
20180     // private
20181     onRender : function(li){
20182         var s = document.createElement("span");
20183         s.className = this.itemCls;
20184         s.innerHTML = "&#160;";
20185         this.el = s;
20186         li.addClass("x-menu-sep-li");
20187         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20188     }
20189 });/*
20190  * Based on:
20191  * Ext JS Library 1.1.1
20192  * Copyright(c) 2006-2007, Ext JS, LLC.
20193  *
20194  * Originally Released Under LGPL - original licence link has changed is not relivant.
20195  *
20196  * Fork - LGPL
20197  * <script type="text/javascript">
20198  */
20199 /**
20200  * @class Roo.menu.Item
20201  * @extends Roo.menu.BaseItem
20202  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20203  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20204  * activation and click handling.
20205  * @constructor
20206  * Creates a new Item
20207  * @param {Object} config Configuration options
20208  */
20209 Roo.menu.Item = function(config){
20210     Roo.menu.Item.superclass.constructor.call(this, config);
20211     if(this.menu){
20212         this.menu = Roo.menu.MenuMgr.get(this.menu);
20213     }
20214 };
20215 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20216     
20217     /**
20218      * @cfg {String} text
20219      * The text to show on the menu item.
20220      */
20221     text: '',
20222     /**
20223      * @cfg {String} icon
20224      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20225      */
20226     icon: undefined,
20227     /**
20228      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20229      */
20230     itemCls : "x-menu-item",
20231     /**
20232      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20233      */
20234     canActivate : true,
20235     /**
20236      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20237      */
20238     showDelay: 200,
20239     // doc'd in BaseItem
20240     hideDelay: 200,
20241
20242     // private
20243     ctype: "Roo.menu.Item",
20244     
20245     // private
20246     onRender : function(container, position){
20247         var el = document.createElement("a");
20248         el.hideFocus = true;
20249         el.unselectable = "on";
20250         el.href = this.href || "#";
20251         if(this.hrefTarget){
20252             el.target = this.hrefTarget;
20253         }
20254         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20255         el.innerHTML = String.format(
20256                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
20257                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
20258         this.el = el;
20259         Roo.menu.Item.superclass.onRender.call(this, container, position);
20260     },
20261
20262     /**
20263      * Sets the text to display in this menu item
20264      * @param {String} text The text to display
20265      */
20266     setText : function(text){
20267         this.text = text;
20268         if(this.rendered){
20269             this.el.update(String.format(
20270                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
20271                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20272             this.parentMenu.autoWidth();
20273         }
20274     },
20275
20276     // private
20277     handleClick : function(e){
20278         if(!this.href){ // if no link defined, stop the event automatically
20279             e.stopEvent();
20280         }
20281         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20282     },
20283
20284     // private
20285     activate : function(autoExpand){
20286         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20287             this.focus();
20288             if(autoExpand){
20289                 this.expandMenu();
20290             }
20291         }
20292         return true;
20293     },
20294
20295     // private
20296     shouldDeactivate : function(e){
20297         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20298             if(this.menu && this.menu.isVisible()){
20299                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20300             }
20301             return true;
20302         }
20303         return false;
20304     },
20305
20306     // private
20307     deactivate : function(){
20308         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20309         this.hideMenu();
20310     },
20311
20312     // private
20313     expandMenu : function(autoActivate){
20314         if(!this.disabled && this.menu){
20315             clearTimeout(this.hideTimer);
20316             delete this.hideTimer;
20317             if(!this.menu.isVisible() && !this.showTimer){
20318                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20319             }else if (this.menu.isVisible() && autoActivate){
20320                 this.menu.tryActivate(0, 1);
20321             }
20322         }
20323     },
20324
20325     // private
20326     deferExpand : function(autoActivate){
20327         delete this.showTimer;
20328         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20329         if(autoActivate){
20330             this.menu.tryActivate(0, 1);
20331         }
20332     },
20333
20334     // private
20335     hideMenu : function(){
20336         clearTimeout(this.showTimer);
20337         delete this.showTimer;
20338         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20339             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20340         }
20341     },
20342
20343     // private
20344     deferHide : function(){
20345         delete this.hideTimer;
20346         this.menu.hide();
20347     }
20348 });/*
20349  * Based on:
20350  * Ext JS Library 1.1.1
20351  * Copyright(c) 2006-2007, Ext JS, LLC.
20352  *
20353  * Originally Released Under LGPL - original licence link has changed is not relivant.
20354  *
20355  * Fork - LGPL
20356  * <script type="text/javascript">
20357  */
20358  
20359 /**
20360  * @class Roo.menu.CheckItem
20361  * @extends Roo.menu.Item
20362  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20363  * @constructor
20364  * Creates a new CheckItem
20365  * @param {Object} config Configuration options
20366  */
20367 Roo.menu.CheckItem = function(config){
20368     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20369     this.addEvents({
20370         /**
20371          * @event beforecheckchange
20372          * Fires before the checked value is set, providing an opportunity to cancel if needed
20373          * @param {Roo.menu.CheckItem} this
20374          * @param {Boolean} checked The new checked value that will be set
20375          */
20376         "beforecheckchange" : true,
20377         /**
20378          * @event checkchange
20379          * Fires after the checked value has been set
20380          * @param {Roo.menu.CheckItem} this
20381          * @param {Boolean} checked The checked value that was set
20382          */
20383         "checkchange" : true
20384     });
20385     if(this.checkHandler){
20386         this.on('checkchange', this.checkHandler, this.scope);
20387     }
20388 };
20389 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20390     /**
20391      * @cfg {String} group
20392      * All check items with the same group name will automatically be grouped into a single-select
20393      * radio button group (defaults to '')
20394      */
20395     /**
20396      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20397      */
20398     itemCls : "x-menu-item x-menu-check-item",
20399     /**
20400      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20401      */
20402     groupClass : "x-menu-group-item",
20403
20404     /**
20405      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20406      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20407      * initialized with checked = true will be rendered as checked.
20408      */
20409     checked: false,
20410
20411     // private
20412     ctype: "Roo.menu.CheckItem",
20413
20414     // private
20415     onRender : function(c){
20416         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20417         if(this.group){
20418             this.el.addClass(this.groupClass);
20419         }
20420         Roo.menu.MenuMgr.registerCheckable(this);
20421         if(this.checked){
20422             this.checked = false;
20423             this.setChecked(true, true);
20424         }
20425     },
20426
20427     // private
20428     destroy : function(){
20429         if(this.rendered){
20430             Roo.menu.MenuMgr.unregisterCheckable(this);
20431         }
20432         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20433     },
20434
20435     /**
20436      * Set the checked state of this item
20437      * @param {Boolean} checked The new checked value
20438      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20439      */
20440     setChecked : function(state, suppressEvent){
20441         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20442             if(this.container){
20443                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20444             }
20445             this.checked = state;
20446             if(suppressEvent !== true){
20447                 this.fireEvent("checkchange", this, state);
20448             }
20449         }
20450     },
20451
20452     // private
20453     handleClick : function(e){
20454        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20455            this.setChecked(!this.checked);
20456        }
20457        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20458     }
20459 });/*
20460  * Based on:
20461  * Ext JS Library 1.1.1
20462  * Copyright(c) 2006-2007, Ext JS, LLC.
20463  *
20464  * Originally Released Under LGPL - original licence link has changed is not relivant.
20465  *
20466  * Fork - LGPL
20467  * <script type="text/javascript">
20468  */
20469  
20470 /**
20471  * @class Roo.menu.DateItem
20472  * @extends Roo.menu.Adapter
20473  * A menu item that wraps the {@link Roo.DatPicker} component.
20474  * @constructor
20475  * Creates a new DateItem
20476  * @param {Object} config Configuration options
20477  */
20478 Roo.menu.DateItem = function(config){
20479     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20480     /** The Roo.DatePicker object @type Roo.DatePicker */
20481     this.picker = this.component;
20482     this.addEvents({select: true});
20483     
20484     this.picker.on("render", function(picker){
20485         picker.getEl().swallowEvent("click");
20486         picker.container.addClass("x-menu-date-item");
20487     });
20488
20489     this.picker.on("select", this.onSelect, this);
20490 };
20491
20492 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20493     // private
20494     onSelect : function(picker, date){
20495         this.fireEvent("select", this, date, picker);
20496         Roo.menu.DateItem.superclass.handleClick.call(this);
20497     }
20498 });/*
20499  * Based on:
20500  * Ext JS Library 1.1.1
20501  * Copyright(c) 2006-2007, Ext JS, LLC.
20502  *
20503  * Originally Released Under LGPL - original licence link has changed is not relivant.
20504  *
20505  * Fork - LGPL
20506  * <script type="text/javascript">
20507  */
20508  
20509 /**
20510  * @class Roo.menu.ColorItem
20511  * @extends Roo.menu.Adapter
20512  * A menu item that wraps the {@link Roo.ColorPalette} component.
20513  * @constructor
20514  * Creates a new ColorItem
20515  * @param {Object} config Configuration options
20516  */
20517 Roo.menu.ColorItem = function(config){
20518     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20519     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20520     this.palette = this.component;
20521     this.relayEvents(this.palette, ["select"]);
20522     if(this.selectHandler){
20523         this.on('select', this.selectHandler, this.scope);
20524     }
20525 };
20526 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20527  * Based on:
20528  * Ext JS Library 1.1.1
20529  * Copyright(c) 2006-2007, Ext JS, LLC.
20530  *
20531  * Originally Released Under LGPL - original licence link has changed is not relivant.
20532  *
20533  * Fork - LGPL
20534  * <script type="text/javascript">
20535  */
20536  
20537
20538 /**
20539  * @class Roo.menu.DateMenu
20540  * @extends Roo.menu.Menu
20541  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20542  * @constructor
20543  * Creates a new DateMenu
20544  * @param {Object} config Configuration options
20545  */
20546 Roo.menu.DateMenu = function(config){
20547     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20548     this.plain = true;
20549     var di = new Roo.menu.DateItem(config);
20550     this.add(di);
20551     /**
20552      * The {@link Roo.DatePicker} instance for this DateMenu
20553      * @type DatePicker
20554      */
20555     this.picker = di.picker;
20556     /**
20557      * @event select
20558      * @param {DatePicker} picker
20559      * @param {Date} date
20560      */
20561     this.relayEvents(di, ["select"]);
20562
20563     this.on('beforeshow', function(){
20564         if(this.picker){
20565             this.picker.hideMonthPicker(true);
20566         }
20567     }, this);
20568 };
20569 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20570     cls:'x-date-menu'
20571 });/*
20572  * Based on:
20573  * Ext JS Library 1.1.1
20574  * Copyright(c) 2006-2007, Ext JS, LLC.
20575  *
20576  * Originally Released Under LGPL - original licence link has changed is not relivant.
20577  *
20578  * Fork - LGPL
20579  * <script type="text/javascript">
20580  */
20581  
20582
20583 /**
20584  * @class Roo.menu.ColorMenu
20585  * @extends Roo.menu.Menu
20586  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20587  * @constructor
20588  * Creates a new ColorMenu
20589  * @param {Object} config Configuration options
20590  */
20591 Roo.menu.ColorMenu = function(config){
20592     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20593     this.plain = true;
20594     var ci = new Roo.menu.ColorItem(config);
20595     this.add(ci);
20596     /**
20597      * The {@link Roo.ColorPalette} instance for this ColorMenu
20598      * @type ColorPalette
20599      */
20600     this.palette = ci.palette;
20601     /**
20602      * @event select
20603      * @param {ColorPalette} palette
20604      * @param {String} color
20605      */
20606     this.relayEvents(ci, ["select"]);
20607 };
20608 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20609  * Based on:
20610  * Ext JS Library 1.1.1
20611  * Copyright(c) 2006-2007, Ext JS, LLC.
20612  *
20613  * Originally Released Under LGPL - original licence link has changed is not relivant.
20614  *
20615  * Fork - LGPL
20616  * <script type="text/javascript">
20617  */
20618  
20619 /**
20620  * @class Roo.form.Field
20621  * @extends Roo.BoxComponent
20622  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20623  * @constructor
20624  * Creates a new Field
20625  * @param {Object} config Configuration options
20626  */
20627 Roo.form.Field = function(config){
20628     Roo.form.Field.superclass.constructor.call(this, config);
20629 };
20630
20631 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20632     /**
20633      * @cfg {String} fieldLabel Label to use when rendering a form.
20634      */
20635        /**
20636      * @cfg {String} qtip Mouse over tip
20637      */
20638      
20639     /**
20640      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20641      */
20642     invalidClass : "x-form-invalid",
20643     /**
20644      * @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")
20645      */
20646     invalidText : "The value in this field is invalid",
20647     /**
20648      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20649      */
20650     focusClass : "x-form-focus",
20651     /**
20652      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20653       automatic validation (defaults to "keyup").
20654      */
20655     validationEvent : "keyup",
20656     /**
20657      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20658      */
20659     validateOnBlur : true,
20660     /**
20661      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20662      */
20663     validationDelay : 250,
20664     /**
20665      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20666      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20667      */
20668     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20669     /**
20670      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20671      */
20672     fieldClass : "x-form-field",
20673     /**
20674      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20675      *<pre>
20676 Value         Description
20677 -----------   ----------------------------------------------------------------------
20678 qtip          Display a quick tip when the user hovers over the field
20679 title         Display a default browser title attribute popup
20680 under         Add a block div beneath the field containing the error text
20681 side          Add an error icon to the right of the field with a popup on hover
20682 [element id]  Add the error text directly to the innerHTML of the specified element
20683 </pre>
20684      */
20685     msgTarget : 'qtip',
20686     /**
20687      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20688      */
20689     msgFx : 'normal',
20690
20691     /**
20692      * @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.
20693      */
20694     readOnly : false,
20695
20696     /**
20697      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20698      */
20699     disabled : false,
20700
20701     /**
20702      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20703      */
20704     inputType : undefined,
20705     
20706     /**
20707      * @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).
20708          */
20709         tabIndex : undefined,
20710         
20711     // private
20712     isFormField : true,
20713
20714     // private
20715     hasFocus : false,
20716     /**
20717      * @property {Roo.Element} fieldEl
20718      * Element Containing the rendered Field (with label etc.)
20719      */
20720     /**
20721      * @cfg {Mixed} value A value to initialize this field with.
20722      */
20723     value : undefined,
20724
20725     /**
20726      * @cfg {String} name The field's HTML name attribute.
20727      */
20728     /**
20729      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20730      */
20731
20732         // private ??
20733         initComponent : function(){
20734         Roo.form.Field.superclass.initComponent.call(this);
20735         this.addEvents({
20736             /**
20737              * @event focus
20738              * Fires when this field receives input focus.
20739              * @param {Roo.form.Field} this
20740              */
20741             focus : true,
20742             /**
20743              * @event blur
20744              * Fires when this field loses input focus.
20745              * @param {Roo.form.Field} this
20746              */
20747             blur : true,
20748             /**
20749              * @event specialkey
20750              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20751              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20752              * @param {Roo.form.Field} this
20753              * @param {Roo.EventObject} e The event object
20754              */
20755             specialkey : true,
20756             /**
20757              * @event change
20758              * Fires just before the field blurs if the field value has changed.
20759              * @param {Roo.form.Field} this
20760              * @param {Mixed} newValue The new value
20761              * @param {Mixed} oldValue The original value
20762              */
20763             change : true,
20764             /**
20765              * @event invalid
20766              * Fires after the field has been marked as invalid.
20767              * @param {Roo.form.Field} this
20768              * @param {String} msg The validation message
20769              */
20770             invalid : true,
20771             /**
20772              * @event valid
20773              * Fires after the field has been validated with no errors.
20774              * @param {Roo.form.Field} this
20775              */
20776             valid : true
20777         });
20778     },
20779
20780     /**
20781      * Returns the name attribute of the field if available
20782      * @return {String} name The field name
20783      */
20784     getName: function(){
20785          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20786     },
20787
20788     // private
20789     onRender : function(ct, position){
20790         Roo.form.Field.superclass.onRender.call(this, ct, position);
20791         if(!this.el){
20792             var cfg = this.getAutoCreate();
20793             if(!cfg.name){
20794                 cfg.name = this.name || this.id;
20795             }
20796             if(this.inputType){
20797                 cfg.type = this.inputType;
20798             }
20799             this.el = ct.createChild(cfg, position);
20800         }
20801         var type = this.el.dom.type;
20802         if(type){
20803             if(type == 'password'){
20804                 type = 'text';
20805             }
20806             this.el.addClass('x-form-'+type);
20807         }
20808         if(this.readOnly){
20809             this.el.dom.readOnly = true;
20810         }
20811         if(this.tabIndex !== undefined){
20812             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20813         }
20814
20815         this.el.addClass([this.fieldClass, this.cls]);
20816         this.initValue();
20817     },
20818
20819     /**
20820      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20821      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20822      * @return {Roo.form.Field} this
20823      */
20824     applyTo : function(target){
20825         this.allowDomMove = false;
20826         this.el = Roo.get(target);
20827         this.render(this.el.dom.parentNode);
20828         return this;
20829     },
20830
20831     // private
20832     initValue : function(){
20833         if(this.value !== undefined){
20834             this.setValue(this.value);
20835         }else if(this.el.dom.value.length > 0){
20836             this.setValue(this.el.dom.value);
20837         }
20838     },
20839
20840     /**
20841      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20842      */
20843     isDirty : function() {
20844         if(this.disabled) {
20845             return false;
20846         }
20847         return String(this.getValue()) !== String(this.originalValue);
20848     },
20849
20850     // private
20851     afterRender : function(){
20852         Roo.form.Field.superclass.afterRender.call(this);
20853         this.initEvents();
20854     },
20855
20856     // private
20857     fireKey : function(e){
20858         if(e.isNavKeyPress()){
20859             this.fireEvent("specialkey", this, e);
20860         }
20861     },
20862
20863     /**
20864      * Resets the current field value to the originally loaded value and clears any validation messages
20865      */
20866     reset : function(){
20867         this.setValue(this.originalValue);
20868         this.clearInvalid();
20869     },
20870
20871     // private
20872     initEvents : function(){
20873         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
20874         this.el.on("focus", this.onFocus,  this);
20875         this.el.on("blur", this.onBlur,  this);
20876
20877         // reference to original value for reset
20878         this.originalValue = this.getValue();
20879     },
20880
20881     // private
20882     onFocus : function(){
20883         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20884             this.el.addClass(this.focusClass);
20885         }
20886         if(!this.hasFocus){
20887             this.hasFocus = true;
20888             this.startValue = this.getValue();
20889             this.fireEvent("focus", this);
20890         }
20891     },
20892
20893     beforeBlur : Roo.emptyFn,
20894
20895     // private
20896     onBlur : function(){
20897         this.beforeBlur();
20898         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20899             this.el.removeClass(this.focusClass);
20900         }
20901         this.hasFocus = false;
20902         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20903             this.validate();
20904         }
20905         var v = this.getValue();
20906         if(String(v) !== String(this.startValue)){
20907             this.fireEvent('change', this, v, this.startValue);
20908         }
20909         this.fireEvent("blur", this);
20910     },
20911
20912     /**
20913      * Returns whether or not the field value is currently valid
20914      * @param {Boolean} preventMark True to disable marking the field invalid
20915      * @return {Boolean} True if the value is valid, else false
20916      */
20917     isValid : function(preventMark){
20918         if(this.disabled){
20919             return true;
20920         }
20921         var restore = this.preventMark;
20922         this.preventMark = preventMark === true;
20923         var v = this.validateValue(this.processValue(this.getRawValue()));
20924         this.preventMark = restore;
20925         return v;
20926     },
20927
20928     /**
20929      * Validates the field value
20930      * @return {Boolean} True if the value is valid, else false
20931      */
20932     validate : function(){
20933         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
20934             this.clearInvalid();
20935             return true;
20936         }
20937         return false;
20938     },
20939
20940     processValue : function(value){
20941         return value;
20942     },
20943
20944     // private
20945     // Subclasses should provide the validation implementation by overriding this
20946     validateValue : function(value){
20947         return true;
20948     },
20949
20950     /**
20951      * Mark this field as invalid
20952      * @param {String} msg The validation message
20953      */
20954     markInvalid : function(msg){
20955         if(!this.rendered || this.preventMark){ // not rendered
20956             return;
20957         }
20958         this.el.addClass(this.invalidClass);
20959         msg = msg || this.invalidText;
20960         switch(this.msgTarget){
20961             case 'qtip':
20962                 this.el.dom.qtip = msg;
20963                 this.el.dom.qclass = 'x-form-invalid-tip';
20964                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
20965                     Roo.QuickTips.enable();
20966                 }
20967                 break;
20968             case 'title':
20969                 this.el.dom.title = msg;
20970                 break;
20971             case 'under':
20972                 if(!this.errorEl){
20973                     var elp = this.el.findParent('.x-form-element', 5, true);
20974                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
20975                     this.errorEl.setWidth(elp.getWidth(true)-20);
20976                 }
20977                 this.errorEl.update(msg);
20978                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
20979                 break;
20980             case 'side':
20981                 if(!this.errorIcon){
20982                     var elp = this.el.findParent('.x-form-element', 5, true);
20983                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
20984                 }
20985                 this.alignErrorIcon();
20986                 this.errorIcon.dom.qtip = msg;
20987                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
20988                 this.errorIcon.show();
20989                 this.on('resize', this.alignErrorIcon, this);
20990                 break;
20991             default:
20992                 var t = Roo.getDom(this.msgTarget);
20993                 t.innerHTML = msg;
20994                 t.style.display = this.msgDisplay;
20995                 break;
20996         }
20997         this.fireEvent('invalid', this, msg);
20998     },
20999
21000     // private
21001     alignErrorIcon : function(){
21002         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21003     },
21004
21005     /**
21006      * Clear any invalid styles/messages for this field
21007      */
21008     clearInvalid : function(){
21009         if(!this.rendered || this.preventMark){ // not rendered
21010             return;
21011         }
21012         this.el.removeClass(this.invalidClass);
21013         switch(this.msgTarget){
21014             case 'qtip':
21015                 this.el.dom.qtip = '';
21016                 break;
21017             case 'title':
21018                 this.el.dom.title = '';
21019                 break;
21020             case 'under':
21021                 if(this.errorEl){
21022                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21023                 }
21024                 break;
21025             case 'side':
21026                 if(this.errorIcon){
21027                     this.errorIcon.dom.qtip = '';
21028                     this.errorIcon.hide();
21029                     this.un('resize', this.alignErrorIcon, this);
21030                 }
21031                 break;
21032             default:
21033                 var t = Roo.getDom(this.msgTarget);
21034                 t.innerHTML = '';
21035                 t.style.display = 'none';
21036                 break;
21037         }
21038         this.fireEvent('valid', this);
21039     },
21040
21041     /**
21042      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21043      * @return {Mixed} value The field value
21044      */
21045     getRawValue : function(){
21046         var v = this.el.getValue();
21047         if(v === this.emptyText){
21048             v = '';
21049         }
21050         return v;
21051     },
21052
21053     /**
21054      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21055      * @return {Mixed} value The field value
21056      */
21057     getValue : function(){
21058         var v = this.el.getValue();
21059         if(v === this.emptyText || v === undefined){
21060             v = '';
21061         }
21062         return v;
21063     },
21064
21065     /**
21066      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21067      * @param {Mixed} value The value to set
21068      */
21069     setRawValue : function(v){
21070         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21071     },
21072
21073     /**
21074      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21075      * @param {Mixed} value The value to set
21076      */
21077     setValue : function(v){
21078         this.value = v;
21079         if(this.rendered){
21080             this.el.dom.value = (v === null || v === undefined ? '' : v);
21081             this.validate();
21082         }
21083     },
21084
21085     adjustSize : function(w, h){
21086         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21087         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21088         return s;
21089     },
21090
21091     adjustWidth : function(tag, w){
21092         tag = tag.toLowerCase();
21093         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21094             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21095                 if(tag == 'input'){
21096                     return w + 2;
21097                 }
21098                 if(tag = 'textarea'){
21099                     return w-2;
21100                 }
21101             }else if(Roo.isOpera){
21102                 if(tag == 'input'){
21103                     return w + 2;
21104                 }
21105                 if(tag = 'textarea'){
21106                     return w-2;
21107                 }
21108             }
21109         }
21110         return w;
21111     }
21112 });
21113
21114
21115 // anything other than normal should be considered experimental
21116 Roo.form.Field.msgFx = {
21117     normal : {
21118         show: function(msgEl, f){
21119             msgEl.setDisplayed('block');
21120         },
21121
21122         hide : function(msgEl, f){
21123             msgEl.setDisplayed(false).update('');
21124         }
21125     },
21126
21127     slide : {
21128         show: function(msgEl, f){
21129             msgEl.slideIn('t', {stopFx:true});
21130         },
21131
21132         hide : function(msgEl, f){
21133             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21134         }
21135     },
21136
21137     slideRight : {
21138         show: function(msgEl, f){
21139             msgEl.fixDisplay();
21140             msgEl.alignTo(f.el, 'tl-tr');
21141             msgEl.slideIn('l', {stopFx:true});
21142         },
21143
21144         hide : function(msgEl, f){
21145             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21146         }
21147     }
21148 };/*
21149  * Based on:
21150  * Ext JS Library 1.1.1
21151  * Copyright(c) 2006-2007, Ext JS, LLC.
21152  *
21153  * Originally Released Under LGPL - original licence link has changed is not relivant.
21154  *
21155  * Fork - LGPL
21156  * <script type="text/javascript">
21157  */
21158  
21159
21160 /**
21161  * @class Roo.form.TextField
21162  * @extends Roo.form.Field
21163  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21164  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21165  * @constructor
21166  * Creates a new TextField
21167  * @param {Object} config Configuration options
21168  */
21169 Roo.form.TextField = function(config){
21170     Roo.form.TextField.superclass.constructor.call(this, config);
21171     this.addEvents({
21172         /**
21173          * @event autosize
21174          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21175          * according to the default logic, but this event provides a hook for the developer to apply additional
21176          * logic at runtime to resize the field if needed.
21177              * @param {Roo.form.Field} this This text field
21178              * @param {Number} width The new field width
21179              */
21180         autosize : true
21181     });
21182 };
21183
21184 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21185     /**
21186      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21187      */
21188     grow : false,
21189     /**
21190      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21191      */
21192     growMin : 30,
21193     /**
21194      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21195      */
21196     growMax : 800,
21197     /**
21198      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21199      */
21200     vtype : null,
21201     /**
21202      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21203      */
21204     maskRe : null,
21205     /**
21206      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21207      */
21208     disableKeyFilter : false,
21209     /**
21210      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21211      */
21212     allowBlank : true,
21213     /**
21214      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21215      */
21216     minLength : 0,
21217     /**
21218      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21219      */
21220     maxLength : Number.MAX_VALUE,
21221     /**
21222      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21223      */
21224     minLengthText : "The minimum length for this field is {0}",
21225     /**
21226      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21227      */
21228     maxLengthText : "The maximum length for this field is {0}",
21229     /**
21230      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21231      */
21232     selectOnFocus : false,
21233     /**
21234      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21235      */
21236     blankText : "This field is required",
21237     /**
21238      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21239      * If available, this function will be called only after the basic validators all return true, and will be passed the
21240      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21241      */
21242     validator : null,
21243     /**
21244      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21245      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21246      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21247      */
21248     regex : null,
21249     /**
21250      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21251      */
21252     regexText : "",
21253     /**
21254      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21255      */
21256     emptyText : null,
21257     /**
21258      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21259      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21260      */
21261     emptyClass : 'x-form-empty-field',
21262
21263     // private
21264     initEvents : function(){
21265         Roo.form.TextField.superclass.initEvents.call(this);
21266         if(this.validationEvent == 'keyup'){
21267             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21268             this.el.on('keyup', this.filterValidation, this);
21269         }
21270         else if(this.validationEvent !== false){
21271             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21272         }
21273         if(this.selectOnFocus || this.emptyText){
21274             this.on("focus", this.preFocus, this);
21275             if(this.emptyText){
21276                 this.on('blur', this.postBlur, this);
21277                 this.applyEmptyText();
21278             }
21279         }
21280         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21281             this.el.on("keypress", this.filterKeys, this);
21282         }
21283         if(this.grow){
21284             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21285             this.el.on("click", this.autoSize,  this);
21286         }
21287     },
21288
21289     processValue : function(value){
21290         if(this.stripCharsRe){
21291             var newValue = value.replace(this.stripCharsRe, '');
21292             if(newValue !== value){
21293                 this.setRawValue(newValue);
21294                 return newValue;
21295             }
21296         }
21297         return value;
21298     },
21299
21300     filterValidation : function(e){
21301         if(!e.isNavKeyPress()){
21302             this.validationTask.delay(this.validationDelay);
21303         }
21304     },
21305
21306     // private
21307     onKeyUp : function(e){
21308         if(!e.isNavKeyPress()){
21309             this.autoSize();
21310         }
21311     },
21312
21313     /**
21314      * Resets the current field value to the originally-loaded value and clears any validation messages.
21315      * Also adds emptyText and emptyClass if the original value was blank.
21316      */
21317     reset : function(){
21318         Roo.form.TextField.superclass.reset.call(this);
21319         this.applyEmptyText();
21320     },
21321
21322     applyEmptyText : function(){
21323         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21324             this.setRawValue(this.emptyText);
21325             this.el.addClass(this.emptyClass);
21326         }
21327     },
21328
21329     // private
21330     preFocus : function(){
21331         if(this.emptyText){
21332             if(this.el.dom.value == this.emptyText){
21333                 this.setRawValue('');
21334             }
21335             this.el.removeClass(this.emptyClass);
21336         }
21337         if(this.selectOnFocus){
21338             this.el.dom.select();
21339         }
21340     },
21341
21342     // private
21343     postBlur : function(){
21344         this.applyEmptyText();
21345     },
21346
21347     // private
21348     filterKeys : function(e){
21349         var k = e.getKey();
21350         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21351             return;
21352         }
21353         var c = e.getCharCode(), cc = String.fromCharCode(c);
21354         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21355             return;
21356         }
21357         if(!this.maskRe.test(cc)){
21358             e.stopEvent();
21359         }
21360     },
21361
21362     setValue : function(v){
21363         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21364             this.el.removeClass(this.emptyClass);
21365         }
21366         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21367         this.applyEmptyText();
21368         this.autoSize();
21369     },
21370
21371     /**
21372      * Validates a value according to the field's validation rules and marks the field as invalid
21373      * if the validation fails
21374      * @param {Mixed} value The value to validate
21375      * @return {Boolean} True if the value is valid, else false
21376      */
21377     validateValue : function(value){
21378         if(value.length < 1 || value === this.emptyText){ // if it's blank
21379              if(this.allowBlank){
21380                 this.clearInvalid();
21381                 return true;
21382              }else{
21383                 this.markInvalid(this.blankText);
21384                 return false;
21385              }
21386         }
21387         if(value.length < this.minLength){
21388             this.markInvalid(String.format(this.minLengthText, this.minLength));
21389             return false;
21390         }
21391         if(value.length > this.maxLength){
21392             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21393             return false;
21394         }
21395         if(this.vtype){
21396             var vt = Roo.form.VTypes;
21397             if(!vt[this.vtype](value, this)){
21398                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21399                 return false;
21400             }
21401         }
21402         if(typeof this.validator == "function"){
21403             var msg = this.validator(value);
21404             if(msg !== true){
21405                 this.markInvalid(msg);
21406                 return false;
21407             }
21408         }
21409         if(this.regex && !this.regex.test(value)){
21410             this.markInvalid(this.regexText);
21411             return false;
21412         }
21413         return true;
21414     },
21415
21416     /**
21417      * Selects text in this field
21418      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21419      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21420      */
21421     selectText : function(start, end){
21422         var v = this.getRawValue();
21423         if(v.length > 0){
21424             start = start === undefined ? 0 : start;
21425             end = end === undefined ? v.length : end;
21426             var d = this.el.dom;
21427             if(d.setSelectionRange){
21428                 d.setSelectionRange(start, end);
21429             }else if(d.createTextRange){
21430                 var range = d.createTextRange();
21431                 range.moveStart("character", start);
21432                 range.moveEnd("character", v.length-end);
21433                 range.select();
21434             }
21435         }
21436     },
21437
21438     /**
21439      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21440      * This only takes effect if grow = true, and fires the autosize event.
21441      */
21442     autoSize : function(){
21443         if(!this.grow || !this.rendered){
21444             return;
21445         }
21446         if(!this.metrics){
21447             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21448         }
21449         var el = this.el;
21450         var v = el.dom.value;
21451         var d = document.createElement('div');
21452         d.appendChild(document.createTextNode(v));
21453         v = d.innerHTML;
21454         d = null;
21455         v += "&#160;";
21456         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21457         this.el.setWidth(w);
21458         this.fireEvent("autosize", this, w);
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.Hidden
21473  * @extends Roo.form.TextField
21474  * Simple Hidden element used on forms 
21475  * 
21476  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21477  * 
21478  * @constructor
21479  * Creates a new Hidden form element.
21480  * @param {Object} config Configuration options
21481  */
21482
21483
21484
21485 // easy hidden field...
21486 Roo.form.Hidden = function(config){
21487     Roo.form.Hidden.superclass.constructor.call(this, config);
21488 };
21489   
21490 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21491     fieldLabel:      '',
21492     inputType:      'hidden',
21493     width:          50,
21494     allowBlank:     true,
21495     labelSeparator: '',
21496     hidden:         true,
21497     itemCls :       'x-form-item-display-none'
21498
21499
21500 });
21501
21502
21503 /*
21504  * Based on:
21505  * Ext JS Library 1.1.1
21506  * Copyright(c) 2006-2007, Ext JS, LLC.
21507  *
21508  * Originally Released Under LGPL - original licence link has changed is not relivant.
21509  *
21510  * Fork - LGPL
21511  * <script type="text/javascript">
21512  */
21513  
21514 /**
21515  * @class Roo.form.TriggerField
21516  * @extends Roo.form.TextField
21517  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21518  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21519  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21520  * for which you can provide a custom implementation.  For example:
21521  * <pre><code>
21522 var trigger = new Roo.form.TriggerField();
21523 trigger.onTriggerClick = myTriggerFn;
21524 trigger.applyTo('my-field');
21525 </code></pre>
21526  *
21527  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21528  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21529  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21530  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21531  * @constructor
21532  * Create a new TriggerField.
21533  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21534  * to the base TextField)
21535  */
21536 Roo.form.TriggerField = function(config){
21537     this.mimicing = false;
21538     Roo.form.TriggerField.superclass.constructor.call(this, config);
21539 };
21540
21541 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21542     /**
21543      * @cfg {String} triggerClass A CSS class to apply to the trigger
21544      */
21545     /**
21546      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21547      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21548      */
21549     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21550     /**
21551      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21552      */
21553     hideTrigger:false,
21554
21555     /** @cfg {Boolean} grow @hide */
21556     /** @cfg {Number} growMin @hide */
21557     /** @cfg {Number} growMax @hide */
21558
21559     /**
21560      * @hide 
21561      * @method
21562      */
21563     autoSize: Roo.emptyFn,
21564     // private
21565     monitorTab : true,
21566     // private
21567     deferHeight : true,
21568
21569     
21570     actionMode : 'wrap',
21571     // private
21572     onResize : function(w, h){
21573         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21574         if(typeof w == 'number'){
21575             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
21576         }
21577     },
21578
21579     // private
21580     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21581
21582     // private
21583     getResizeEl : function(){
21584         return this.wrap;
21585     },
21586
21587     // private
21588     getPositionEl : function(){
21589         return this.wrap;
21590     },
21591
21592     // private
21593     alignErrorIcon : function(){
21594         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21595     },
21596
21597     // private
21598     onRender : function(ct, position){
21599         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21600         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21601         this.trigger = this.wrap.createChild(this.triggerConfig ||
21602                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21603         if(this.hideTrigger){
21604             this.trigger.setDisplayed(false);
21605         }
21606         this.initTrigger();
21607         if(!this.width){
21608             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21609         }
21610     },
21611
21612     // private
21613     initTrigger : function(){
21614         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21615         this.trigger.addClassOnOver('x-form-trigger-over');
21616         this.trigger.addClassOnClick('x-form-trigger-click');
21617     },
21618
21619     // private
21620     onDestroy : function(){
21621         if(this.trigger){
21622             this.trigger.removeAllListeners();
21623             this.trigger.remove();
21624         }
21625         if(this.wrap){
21626             this.wrap.remove();
21627         }
21628         Roo.form.TriggerField.superclass.onDestroy.call(this);
21629     },
21630
21631     // private
21632     onFocus : function(){
21633         Roo.form.TriggerField.superclass.onFocus.call(this);
21634         if(!this.mimicing){
21635             this.wrap.addClass('x-trigger-wrap-focus');
21636             this.mimicing = true;
21637             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21638             if(this.monitorTab){
21639                 this.el.on("keydown", this.checkTab, this);
21640             }
21641         }
21642     },
21643
21644     // private
21645     checkTab : function(e){
21646         if(e.getKey() == e.TAB){
21647             this.triggerBlur();
21648         }
21649     },
21650
21651     // private
21652     onBlur : function(){
21653         // do nothing
21654     },
21655
21656     // private
21657     mimicBlur : function(e, t){
21658         if(!this.wrap.contains(t) && this.validateBlur()){
21659             this.triggerBlur();
21660         }
21661     },
21662
21663     // private
21664     triggerBlur : function(){
21665         this.mimicing = false;
21666         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21667         if(this.monitorTab){
21668             this.el.un("keydown", this.checkTab, this);
21669         }
21670         this.wrap.removeClass('x-trigger-wrap-focus');
21671         Roo.form.TriggerField.superclass.onBlur.call(this);
21672     },
21673
21674     // private
21675     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21676     validateBlur : function(e, t){
21677         return true;
21678     },
21679
21680     // private
21681     onDisable : function(){
21682         Roo.form.TriggerField.superclass.onDisable.call(this);
21683         if(this.wrap){
21684             this.wrap.addClass('x-item-disabled');
21685         }
21686     },
21687
21688     // private
21689     onEnable : function(){
21690         Roo.form.TriggerField.superclass.onEnable.call(this);
21691         if(this.wrap){
21692             this.wrap.removeClass('x-item-disabled');
21693         }
21694     },
21695
21696     // private
21697     onShow : function(){
21698         var ae = this.getActionEl();
21699         
21700         if(ae){
21701             ae.dom.style.display = '';
21702             ae.dom.style.visibility = 'visible';
21703         }
21704     },
21705
21706     // private
21707     
21708     onHide : function(){
21709         var ae = this.getActionEl();
21710         ae.dom.style.display = 'none';
21711     },
21712
21713     /**
21714      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21715      * by an implementing function.
21716      * @method
21717      * @param {EventObject} e
21718      */
21719     onTriggerClick : Roo.emptyFn
21720 });
21721
21722 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21723 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21724 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21725 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21726     initComponent : function(){
21727         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21728
21729         this.triggerConfig = {
21730             tag:'span', cls:'x-form-twin-triggers', cn:[
21731             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21732             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21733         ]};
21734     },
21735
21736     getTrigger : function(index){
21737         return this.triggers[index];
21738     },
21739
21740     initTrigger : function(){
21741         var ts = this.trigger.select('.x-form-trigger', true);
21742         this.wrap.setStyle('overflow', 'hidden');
21743         var triggerField = this;
21744         ts.each(function(t, all, index){
21745             t.hide = function(){
21746                 var w = triggerField.wrap.getWidth();
21747                 this.dom.style.display = 'none';
21748                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21749             };
21750             t.show = function(){
21751                 var w = triggerField.wrap.getWidth();
21752                 this.dom.style.display = '';
21753                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21754             };
21755             var triggerIndex = 'Trigger'+(index+1);
21756
21757             if(this['hide'+triggerIndex]){
21758                 t.dom.style.display = 'none';
21759             }
21760             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21761             t.addClassOnOver('x-form-trigger-over');
21762             t.addClassOnClick('x-form-trigger-click');
21763         }, this);
21764         this.triggers = ts.elements;
21765     },
21766
21767     onTrigger1Click : Roo.emptyFn,
21768     onTrigger2Click : Roo.emptyFn
21769 });/*
21770  * Based on:
21771  * Ext JS Library 1.1.1
21772  * Copyright(c) 2006-2007, Ext JS, LLC.
21773  *
21774  * Originally Released Under LGPL - original licence link has changed is not relivant.
21775  *
21776  * Fork - LGPL
21777  * <script type="text/javascript">
21778  */
21779  
21780 /**
21781  * @class Roo.form.TextArea
21782  * @extends Roo.form.TextField
21783  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21784  * support for auto-sizing.
21785  * @constructor
21786  * Creates a new TextArea
21787  * @param {Object} config Configuration options
21788  */
21789 Roo.form.TextArea = function(config){
21790     Roo.form.TextArea.superclass.constructor.call(this, config);
21791     // these are provided exchanges for backwards compat
21792     // minHeight/maxHeight were replaced by growMin/growMax to be
21793     // compatible with TextField growing config values
21794     if(this.minHeight !== undefined){
21795         this.growMin = this.minHeight;
21796     }
21797     if(this.maxHeight !== undefined){
21798         this.growMax = this.maxHeight;
21799     }
21800 };
21801
21802 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21803     /**
21804      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21805      */
21806     growMin : 60,
21807     /**
21808      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21809      */
21810     growMax: 1000,
21811     /**
21812      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21813      * in the field (equivalent to setting overflow: hidden, defaults to false)
21814      */
21815     preventScrollbars: false,
21816     /**
21817      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21818      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21819      */
21820
21821     // private
21822     onRender : function(ct, position){
21823         if(!this.el){
21824             this.defaultAutoCreate = {
21825                 tag: "textarea",
21826                 style:"width:300px;height:60px;",
21827                 autocomplete: "off"
21828             };
21829         }
21830         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21831         if(this.grow){
21832             this.textSizeEl = Roo.DomHelper.append(document.body, {
21833                 tag: "pre", cls: "x-form-grow-sizer"
21834             });
21835             if(this.preventScrollbars){
21836                 this.el.setStyle("overflow", "hidden");
21837             }
21838             this.el.setHeight(this.growMin);
21839         }
21840     },
21841
21842     onDestroy : function(){
21843         if(this.textSizeEl){
21844             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21845         }
21846         Roo.form.TextArea.superclass.onDestroy.call(this);
21847     },
21848
21849     // private
21850     onKeyUp : function(e){
21851         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21852             this.autoSize();
21853         }
21854     },
21855
21856     /**
21857      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21858      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21859      */
21860     autoSize : function(){
21861         if(!this.grow || !this.textSizeEl){
21862             return;
21863         }
21864         var el = this.el;
21865         var v = el.dom.value;
21866         var ts = this.textSizeEl;
21867
21868         ts.innerHTML = '';
21869         ts.appendChild(document.createTextNode(v));
21870         v = ts.innerHTML;
21871
21872         Roo.fly(ts).setWidth(this.el.getWidth());
21873         if(v.length < 1){
21874             v = "&#160;&#160;";
21875         }else{
21876             if(Roo.isIE){
21877                 v = v.replace(/\n/g, '<p>&#160;</p>');
21878             }
21879             v += "&#160;\n&#160;";
21880         }
21881         ts.innerHTML = v;
21882         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21883         if(h != this.lastHeight){
21884             this.lastHeight = h;
21885             this.el.setHeight(h);
21886             this.fireEvent("autosize", this, h);
21887         }
21888     }
21889 });/*
21890  * Based on:
21891  * Ext JS Library 1.1.1
21892  * Copyright(c) 2006-2007, Ext JS, LLC.
21893  *
21894  * Originally Released Under LGPL - original licence link has changed is not relivant.
21895  *
21896  * Fork - LGPL
21897  * <script type="text/javascript">
21898  */
21899  
21900
21901 /**
21902  * @class Roo.form.NumberField
21903  * @extends Roo.form.TextField
21904  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21905  * @constructor
21906  * Creates a new NumberField
21907  * @param {Object} config Configuration options
21908  */
21909 Roo.form.NumberField = function(config){
21910     Roo.form.NumberField.superclass.constructor.call(this, config);
21911 };
21912
21913 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
21914     /**
21915      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
21916      */
21917     fieldClass: "x-form-field x-form-num-field",
21918     /**
21919      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
21920      */
21921     allowDecimals : true,
21922     /**
21923      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
21924      */
21925     decimalSeparator : ".",
21926     /**
21927      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
21928      */
21929     decimalPrecision : 2,
21930     /**
21931      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
21932      */
21933     allowNegative : true,
21934     /**
21935      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
21936      */
21937     minValue : Number.NEGATIVE_INFINITY,
21938     /**
21939      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
21940      */
21941     maxValue : Number.MAX_VALUE,
21942     /**
21943      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
21944      */
21945     minText : "The minimum value for this field is {0}",
21946     /**
21947      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
21948      */
21949     maxText : "The maximum value for this field is {0}",
21950     /**
21951      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
21952      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
21953      */
21954     nanText : "{0} is not a valid number",
21955
21956     // private
21957     initEvents : function(){
21958         Roo.form.NumberField.superclass.initEvents.call(this);
21959         var allowed = "0123456789";
21960         if(this.allowDecimals){
21961             allowed += this.decimalSeparator;
21962         }
21963         if(this.allowNegative){
21964             allowed += "-";
21965         }
21966         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
21967         var keyPress = function(e){
21968             var k = e.getKey();
21969             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
21970                 return;
21971             }
21972             var c = e.getCharCode();
21973             if(allowed.indexOf(String.fromCharCode(c)) === -1){
21974                 e.stopEvent();
21975             }
21976         };
21977         this.el.on("keypress", keyPress, this);
21978     },
21979
21980     // private
21981     validateValue : function(value){
21982         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
21983             return false;
21984         }
21985         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
21986              return true;
21987         }
21988         var num = this.parseValue(value);
21989         if(isNaN(num)){
21990             this.markInvalid(String.format(this.nanText, value));
21991             return false;
21992         }
21993         if(num < this.minValue){
21994             this.markInvalid(String.format(this.minText, this.minValue));
21995             return false;
21996         }
21997         if(num > this.maxValue){
21998             this.markInvalid(String.format(this.maxText, this.maxValue));
21999             return false;
22000         }
22001         return true;
22002     },
22003
22004     getValue : function(){
22005         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22006     },
22007
22008     // private
22009     parseValue : function(value){
22010         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22011         return isNaN(value) ? '' : value;
22012     },
22013
22014     // private
22015     fixPrecision : function(value){
22016         var nan = isNaN(value);
22017         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22018             return nan ? '' : value;
22019         }
22020         return parseFloat(value).toFixed(this.decimalPrecision);
22021     },
22022
22023     setValue : function(v){
22024         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22025     },
22026
22027     // private
22028     decimalPrecisionFcn : function(v){
22029         return Math.floor(v);
22030     },
22031
22032     beforeBlur : function(){
22033         var v = this.parseValue(this.getRawValue());
22034         if(v){
22035             this.setValue(this.fixPrecision(v));
22036         }
22037     }
22038 });/*
22039  * Based on:
22040  * Ext JS Library 1.1.1
22041  * Copyright(c) 2006-2007, Ext JS, LLC.
22042  *
22043  * Originally Released Under LGPL - original licence link has changed is not relivant.
22044  *
22045  * Fork - LGPL
22046  * <script type="text/javascript">
22047  */
22048  
22049 /**
22050  * @class Roo.form.DateField
22051  * @extends Roo.form.TriggerField
22052  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22053 * @constructor
22054 * Create a new DateField
22055 * @param {Object} config
22056  */
22057 Roo.form.DateField = function(config){
22058     Roo.form.DateField.superclass.constructor.call(this, config);
22059     
22060       this.addEvents({
22061          
22062         /**
22063          * @event select
22064          * Fires when a date is selected
22065              * @param {Roo.form.DateField} combo This combo box
22066              * @param {Date} date The date selected
22067              */
22068         'select' : true
22069          
22070     });
22071     
22072     
22073     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22074     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22075     this.ddMatch = null;
22076     if(this.disabledDates){
22077         var dd = this.disabledDates;
22078         var re = "(?:";
22079         for(var i = 0; i < dd.length; i++){
22080             re += dd[i];
22081             if(i != dd.length-1) re += "|";
22082         }
22083         this.ddMatch = new RegExp(re + ")");
22084     }
22085 };
22086
22087 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22088     /**
22089      * @cfg {String} format
22090      * The default date format string which can be overriden for localization support.  The format must be
22091      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22092      */
22093     format : "m/d/y",
22094     /**
22095      * @cfg {String} altFormats
22096      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22097      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22098      */
22099     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22100     /**
22101      * @cfg {Array} disabledDays
22102      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22103      */
22104     disabledDays : null,
22105     /**
22106      * @cfg {String} disabledDaysText
22107      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22108      */
22109     disabledDaysText : "Disabled",
22110     /**
22111      * @cfg {Array} disabledDates
22112      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22113      * expression so they are very powerful. Some examples:
22114      * <ul>
22115      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22116      * <li>["03/08", "09/16"] would disable those days for every year</li>
22117      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22118      * <li>["03/../2006"] would disable every day in March 2006</li>
22119      * <li>["^03"] would disable every day in every March</li>
22120      * </ul>
22121      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22122      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22123      */
22124     disabledDates : null,
22125     /**
22126      * @cfg {String} disabledDatesText
22127      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22128      */
22129     disabledDatesText : "Disabled",
22130     /**
22131      * @cfg {Date/String} minValue
22132      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22133      * valid format (defaults to null).
22134      */
22135     minValue : null,
22136     /**
22137      * @cfg {Date/String} maxValue
22138      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22139      * valid format (defaults to null).
22140      */
22141     maxValue : null,
22142     /**
22143      * @cfg {String} minText
22144      * The error text to display when the date in the cell is before minValue (defaults to
22145      * 'The date in this field must be after {minValue}').
22146      */
22147     minText : "The date in this field must be equal to or after {0}",
22148     /**
22149      * @cfg {String} maxText
22150      * The error text to display when the date in the cell is after maxValue (defaults to
22151      * 'The date in this field must be before {maxValue}').
22152      */
22153     maxText : "The date in this field must be equal to or before {0}",
22154     /**
22155      * @cfg {String} invalidText
22156      * The error text to display when the date in the field is invalid (defaults to
22157      * '{value} is not a valid date - it must be in the format {format}').
22158      */
22159     invalidText : "{0} is not a valid date - it must be in the format {1}",
22160     /**
22161      * @cfg {String} triggerClass
22162      * An additional CSS class used to style the trigger button.  The trigger will always get the
22163      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22164      * which displays a calendar icon).
22165      */
22166     triggerClass : 'x-form-date-trigger',
22167     
22168
22169     /**
22170      * @cfg {bool} useIso
22171      * if enabled, then the date field will use a hidden field to store the 
22172      * real value as iso formated date. default (false)
22173      */ 
22174     useIso : false,
22175     /**
22176      * @cfg {String/Object} autoCreate
22177      * A DomHelper element spec, or true for a default element spec (defaults to
22178      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22179      */ 
22180     // private
22181     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22182     
22183     // private
22184     hiddenField: false,
22185     
22186     onRender : function(ct, position)
22187     {
22188         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22189         if (this.useIso) {
22190             this.el.dom.removeAttribute('name'); 
22191             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22192                     'before', true);
22193             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22194             // prevent input submission
22195             this.hiddenName = this.name;
22196         }
22197             
22198             
22199     },
22200     
22201     // private
22202     validateValue : function(value)
22203     {
22204         value = this.formatDate(value);
22205         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22206             return false;
22207         }
22208         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22209              return true;
22210         }
22211         var svalue = value;
22212         value = this.parseDate(value);
22213         if(!value){
22214             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22215             return false;
22216         }
22217         var time = value.getTime();
22218         if(this.minValue && time < this.minValue.getTime()){
22219             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22220             return false;
22221         }
22222         if(this.maxValue && time > this.maxValue.getTime()){
22223             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22224             return false;
22225         }
22226         if(this.disabledDays){
22227             var day = value.getDay();
22228             for(var i = 0; i < this.disabledDays.length; i++) {
22229                 if(day === this.disabledDays[i]){
22230                     this.markInvalid(this.disabledDaysText);
22231                     return false;
22232                 }
22233             }
22234         }
22235         var fvalue = this.formatDate(value);
22236         if(this.ddMatch && this.ddMatch.test(fvalue)){
22237             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22238             return false;
22239         }
22240         return true;
22241     },
22242
22243     // private
22244     // Provides logic to override the default TriggerField.validateBlur which just returns true
22245     validateBlur : function(){
22246         return !this.menu || !this.menu.isVisible();
22247     },
22248
22249     /**
22250      * Returns the current date value of the date field.
22251      * @return {Date} The date value
22252      */
22253     getValue : function(){
22254         
22255         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22256     },
22257
22258     /**
22259      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22260      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22261      * (the default format used is "m/d/y").
22262      * <br />Usage:
22263      * <pre><code>
22264 //All of these calls set the same date value (May 4, 2006)
22265
22266 //Pass a date object:
22267 var dt = new Date('5/4/06');
22268 dateField.setValue(dt);
22269
22270 //Pass a date string (default format):
22271 dateField.setValue('5/4/06');
22272
22273 //Pass a date string (custom format):
22274 dateField.format = 'Y-m-d';
22275 dateField.setValue('2006-5-4');
22276 </code></pre>
22277      * @param {String/Date} date The date or valid date string
22278      */
22279     setValue : function(date){
22280         if (this.hiddenField) {
22281             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22282         }
22283         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22284     },
22285
22286     // private
22287     parseDate : function(value){
22288         if(!value || value instanceof Date){
22289             return value;
22290         }
22291         var v = Date.parseDate(value, this.format);
22292         if(!v && this.altFormats){
22293             if(!this.altFormatsArray){
22294                 this.altFormatsArray = this.altFormats.split("|");
22295             }
22296             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22297                 v = Date.parseDate(value, this.altFormatsArray[i]);
22298             }
22299         }
22300         return v;
22301     },
22302
22303     // private
22304     formatDate : function(date, fmt){
22305         return (!date || !(date instanceof Date)) ?
22306                date : date.dateFormat(fmt || this.format);
22307     },
22308
22309     // private
22310     menuListeners : {
22311         select: function(m, d){
22312             this.setValue(d);
22313             this.fireEvent('select', this, d);
22314         },
22315         show : function(){ // retain focus styling
22316             this.onFocus();
22317         },
22318         hide : function(){
22319             this.focus.defer(10, this);
22320             var ml = this.menuListeners;
22321             this.menu.un("select", ml.select,  this);
22322             this.menu.un("show", ml.show,  this);
22323             this.menu.un("hide", ml.hide,  this);
22324         }
22325     },
22326
22327     // private
22328     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22329     onTriggerClick : function(){
22330         if(this.disabled){
22331             return;
22332         }
22333         if(this.menu == null){
22334             this.menu = new Roo.menu.DateMenu();
22335         }
22336         Roo.apply(this.menu.picker,  {
22337             showClear: this.allowBlank,
22338             minDate : this.minValue,
22339             maxDate : this.maxValue,
22340             disabledDatesRE : this.ddMatch,
22341             disabledDatesText : this.disabledDatesText,
22342             disabledDays : this.disabledDays,
22343             disabledDaysText : this.disabledDaysText,
22344             format : this.format,
22345             minText : String.format(this.minText, this.formatDate(this.minValue)),
22346             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22347         });
22348         this.menu.on(Roo.apply({}, this.menuListeners, {
22349             scope:this
22350         }));
22351         this.menu.picker.setValue(this.getValue() || new Date());
22352         this.menu.show(this.el, "tl-bl?");
22353     },
22354
22355     beforeBlur : function(){
22356         var v = this.parseDate(this.getRawValue());
22357         if(v){
22358             this.setValue(v);
22359         }
22360     }
22361
22362     /** @cfg {Boolean} grow @hide */
22363     /** @cfg {Number} growMin @hide */
22364     /** @cfg {Number} growMax @hide */
22365     /**
22366      * @hide
22367      * @method autoSize
22368      */
22369 });/*
22370  * Based on:
22371  * Ext JS Library 1.1.1
22372  * Copyright(c) 2006-2007, Ext JS, LLC.
22373  *
22374  * Originally Released Under LGPL - original licence link has changed is not relivant.
22375  *
22376  * Fork - LGPL
22377  * <script type="text/javascript">
22378  */
22379  
22380
22381 /**
22382  * @class Roo.form.ComboBox
22383  * @extends Roo.form.TriggerField
22384  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22385  * @constructor
22386  * Create a new ComboBox.
22387  * @param {Object} config Configuration options
22388  */
22389 Roo.form.ComboBox = function(config){
22390     Roo.form.ComboBox.superclass.constructor.call(this, config);
22391     this.addEvents({
22392         /**
22393          * @event expand
22394          * Fires when the dropdown list is expanded
22395              * @param {Roo.form.ComboBox} combo This combo box
22396              */
22397         'expand' : true,
22398         /**
22399          * @event collapse
22400          * Fires when the dropdown list is collapsed
22401              * @param {Roo.form.ComboBox} combo This combo box
22402              */
22403         'collapse' : true,
22404         /**
22405          * @event beforeselect
22406          * Fires before a list item is selected. Return false to cancel the selection.
22407              * @param {Roo.form.ComboBox} combo This combo box
22408              * @param {Roo.data.Record} record The data record returned from the underlying store
22409              * @param {Number} index The index of the selected item in the dropdown list
22410              */
22411         'beforeselect' : true,
22412         /**
22413          * @event select
22414          * Fires when a list item is selected
22415              * @param {Roo.form.ComboBox} combo This combo box
22416              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22417              * @param {Number} index The index of the selected item in the dropdown list
22418              */
22419         'select' : true,
22420         /**
22421          * @event beforequery
22422          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22423          * The event object passed has these properties:
22424              * @param {Roo.form.ComboBox} combo This combo box
22425              * @param {String} query The query
22426              * @param {Boolean} forceAll true to force "all" query
22427              * @param {Boolean} cancel true to cancel the query
22428              * @param {Object} e The query event object
22429              */
22430         'beforequery': true
22431     });
22432     if(this.transform){
22433         this.allowDomMove = false;
22434         var s = Roo.getDom(this.transform);
22435         if(!this.hiddenName){
22436             this.hiddenName = s.name;
22437         }
22438         if(!this.store){
22439             this.mode = 'local';
22440             var d = [], opts = s.options;
22441             for(var i = 0, len = opts.length;i < len; i++){
22442                 var o = opts[i];
22443                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22444                 if(o.selected) {
22445                     this.value = value;
22446                 }
22447                 d.push([value, o.text]);
22448             }
22449             this.store = new Roo.data.SimpleStore({
22450                 'id': 0,
22451                 fields: ['value', 'text'],
22452                 data : d
22453             });
22454             this.valueField = 'value';
22455             this.displayField = 'text';
22456         }
22457         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22458         if(!this.lazyRender){
22459             this.target = true;
22460             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22461             s.parentNode.removeChild(s); // remove it
22462             this.render(this.el.parentNode);
22463         }else{
22464             s.parentNode.removeChild(s); // remove it
22465         }
22466
22467     }
22468     if (this.store) {
22469         this.store = Roo.factory(this.store, Roo.data);
22470     }
22471     
22472     this.selectedIndex = -1;
22473     if(this.mode == 'local'){
22474         if(config.queryDelay === undefined){
22475             this.queryDelay = 10;
22476         }
22477         if(config.minChars === undefined){
22478             this.minChars = 0;
22479         }
22480     }
22481 };
22482
22483 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22484     /**
22485      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22486      */
22487     /**
22488      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22489      * rendering into an Roo.Editor, defaults to false)
22490      */
22491     /**
22492      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22493      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22494      */
22495     /**
22496      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22497      */
22498     /**
22499      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22500      * the dropdown list (defaults to undefined, with no header element)
22501      */
22502
22503      /**
22504      * @cfg {String/Roo.Template} tpl The template to use to render the output
22505      */
22506      
22507     // private
22508     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22509     /**
22510      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22511      */
22512     listWidth: undefined,
22513     /**
22514      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22515      * mode = 'remote' or 'text' if mode = 'local')
22516      */
22517     displayField: undefined,
22518     /**
22519      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22520      * mode = 'remote' or 'value' if mode = 'local'). 
22521      * Note: use of a valueField requires the user make a selection
22522      * in order for a value to be mapped.
22523      */
22524     valueField: undefined,
22525     /**
22526      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22527      * field's data value (defaults to the underlying DOM element's name)
22528      */
22529     hiddenName: undefined,
22530     /**
22531      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22532      */
22533     listClass: '',
22534     /**
22535      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22536      */
22537     selectedClass: 'x-combo-selected',
22538     /**
22539      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22540      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22541      * which displays a downward arrow icon).
22542      */
22543     triggerClass : 'x-form-arrow-trigger',
22544     /**
22545      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22546      */
22547     shadow:'sides',
22548     /**
22549      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22550      * anchor positions (defaults to 'tl-bl')
22551      */
22552     listAlign: 'tl-bl?',
22553     /**
22554      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22555      */
22556     maxHeight: 300,
22557     /**
22558      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22559      * query specified by the allQuery config option (defaults to 'query')
22560      */
22561     triggerAction: 'query',
22562     /**
22563      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22564      * (defaults to 4, does not apply if editable = false)
22565      */
22566     minChars : 4,
22567     /**
22568      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22569      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22570      */
22571     typeAhead: false,
22572     /**
22573      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22574      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22575      */
22576     queryDelay: 500,
22577     /**
22578      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22579      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22580      */
22581     pageSize: 0,
22582     /**
22583      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22584      * when editable = true (defaults to false)
22585      */
22586     selectOnFocus:false,
22587     /**
22588      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22589      */
22590     queryParam: 'query',
22591     /**
22592      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22593      * when mode = 'remote' (defaults to 'Loading...')
22594      */
22595     loadingText: 'Loading...',
22596     /**
22597      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22598      */
22599     resizable: false,
22600     /**
22601      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22602      */
22603     handleHeight : 8,
22604     /**
22605      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22606      * traditional select (defaults to true)
22607      */
22608     editable: true,
22609     /**
22610      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22611      */
22612     allQuery: '',
22613     /**
22614      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22615      */
22616     mode: 'remote',
22617     /**
22618      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22619      * listWidth has a higher value)
22620      */
22621     minListWidth : 70,
22622     /**
22623      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22624      * allow the user to set arbitrary text into the field (defaults to false)
22625      */
22626     forceSelection:false,
22627     /**
22628      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22629      * if typeAhead = true (defaults to 250)
22630      */
22631     typeAheadDelay : 250,
22632     /**
22633      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22634      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22635      */
22636     valueNotFoundText : undefined,
22637     /**
22638      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22639      */
22640     blockFocus : false,
22641     
22642     /**
22643      * @cfg {bool} disableClear Disable showing of clear button.
22644      */
22645     disableClear : false,
22646     
22647     // private
22648     onRender : function(ct, position){
22649         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22650         if(this.hiddenName){
22651             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22652                     'before', true);
22653             this.hiddenField.value =
22654                 this.hiddenValue !== undefined ? this.hiddenValue :
22655                 this.value !== undefined ? this.value : '';
22656
22657             // prevent input submission
22658             this.el.dom.removeAttribute('name');
22659         }
22660         if(Roo.isGecko){
22661             this.el.dom.setAttribute('autocomplete', 'off');
22662         }
22663
22664         var cls = 'x-combo-list';
22665
22666         this.list = new Roo.Layer({
22667             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22668         });
22669
22670         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22671         this.list.setWidth(lw);
22672         this.list.swallowEvent('mousewheel');
22673         this.assetHeight = 0;
22674
22675         if(this.title){
22676             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22677             this.assetHeight += this.header.getHeight();
22678         }
22679
22680         this.innerList = this.list.createChild({cls:cls+'-inner'});
22681         this.innerList.on('mouseover', this.onViewOver, this);
22682         this.innerList.on('mousemove', this.onViewMove, this);
22683         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22684         
22685         if(this.allowBlank && !this.pageSize && !this.disableClear){
22686             this.footer = this.list.createChild({cls:cls+'-ft'});
22687             this.pageTb = new Roo.Toolbar(this.footer);
22688            
22689         }
22690         if(this.pageSize){
22691             this.footer = this.list.createChild({cls:cls+'-ft'});
22692             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22693                     {pageSize: this.pageSize});
22694             
22695         }
22696         
22697         if (this.pageTb && this.allowBlank && !this.disableClear) {
22698             var _this = this;
22699             this.pageTb.add(new Roo.Toolbar.Fill(), {
22700                 cls: 'x-btn-icon x-btn-clear',
22701                 text: '&#160;',
22702                 handler: function()
22703                 {
22704                     _this.collapse();
22705                     _this.clearValue();
22706                     _this.onSelect(false, -1);
22707                 }
22708             });
22709         }
22710         if (this.footer) {
22711             this.assetHeight += this.footer.getHeight();
22712         }
22713         
22714
22715         if(!this.tpl){
22716             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22717         }
22718
22719         this.view = new Roo.View(this.innerList, this.tpl, {
22720             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22721         });
22722
22723         this.view.on('click', this.onViewClick, this);
22724
22725         this.store.on('beforeload', this.onBeforeLoad, this);
22726         this.store.on('load', this.onLoad, this);
22727         this.store.on('loadexception', this.collapse, this);
22728
22729         if(this.resizable){
22730             this.resizer = new Roo.Resizable(this.list,  {
22731                pinned:true, handles:'se'
22732             });
22733             this.resizer.on('resize', function(r, w, h){
22734                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22735                 this.listWidth = w;
22736                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22737                 this.restrictHeight();
22738             }, this);
22739             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22740         }
22741         if(!this.editable){
22742             this.editable = true;
22743             this.setEditable(false);
22744         }
22745     },
22746
22747     // private
22748     initEvents : function(){
22749         Roo.form.ComboBox.superclass.initEvents.call(this);
22750
22751         this.keyNav = new Roo.KeyNav(this.el, {
22752             "up" : function(e){
22753                 this.inKeyMode = true;
22754                 this.selectPrev();
22755             },
22756
22757             "down" : function(e){
22758                 if(!this.isExpanded()){
22759                     this.onTriggerClick();
22760                 }else{
22761                     this.inKeyMode = true;
22762                     this.selectNext();
22763                 }
22764             },
22765
22766             "enter" : function(e){
22767                 this.onViewClick();
22768                 //return true;
22769             },
22770
22771             "esc" : function(e){
22772                 this.collapse();
22773             },
22774
22775             "tab" : function(e){
22776                 this.onViewClick(false);
22777                 return true;
22778             },
22779
22780             scope : this,
22781
22782             doRelay : function(foo, bar, hname){
22783                 if(hname == 'down' || this.scope.isExpanded()){
22784                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22785                 }
22786                 return true;
22787             },
22788
22789             forceKeyDown: true
22790         });
22791         this.queryDelay = Math.max(this.queryDelay || 10,
22792                 this.mode == 'local' ? 10 : 250);
22793         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22794         if(this.typeAhead){
22795             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22796         }
22797         if(this.editable !== false){
22798             this.el.on("keyup", this.onKeyUp, this);
22799         }
22800         if(this.forceSelection){
22801             this.on('blur', this.doForce, this);
22802         }
22803     },
22804
22805     onDestroy : function(){
22806         if(this.view){
22807             this.view.setStore(null);
22808             this.view.el.removeAllListeners();
22809             this.view.el.remove();
22810             this.view.purgeListeners();
22811         }
22812         if(this.list){
22813             this.list.destroy();
22814         }
22815         if(this.store){
22816             this.store.un('beforeload', this.onBeforeLoad, this);
22817             this.store.un('load', this.onLoad, this);
22818             this.store.un('loadexception', this.collapse, this);
22819         }
22820         Roo.form.ComboBox.superclass.onDestroy.call(this);
22821     },
22822
22823     // private
22824     fireKey : function(e){
22825         if(e.isNavKeyPress() && !this.list.isVisible()){
22826             this.fireEvent("specialkey", this, e);
22827         }
22828     },
22829
22830     // private
22831     onResize: function(w, h){
22832         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22833         if(this.list && this.listWidth === undefined){
22834             var lw = Math.max(w, this.minListWidth);
22835             this.list.setWidth(lw);
22836             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22837         }
22838     },
22839
22840     /**
22841      * Allow or prevent the user from directly editing the field text.  If false is passed,
22842      * the user will only be able to select from the items defined in the dropdown list.  This method
22843      * is the runtime equivalent of setting the 'editable' config option at config time.
22844      * @param {Boolean} value True to allow the user to directly edit the field text
22845      */
22846     setEditable : function(value){
22847         if(value == this.editable){
22848             return;
22849         }
22850         this.editable = value;
22851         if(!value){
22852             this.el.dom.setAttribute('readOnly', true);
22853             this.el.on('mousedown', this.onTriggerClick,  this);
22854             this.el.addClass('x-combo-noedit');
22855         }else{
22856             this.el.dom.setAttribute('readOnly', false);
22857             this.el.un('mousedown', this.onTriggerClick,  this);
22858             this.el.removeClass('x-combo-noedit');
22859         }
22860     },
22861
22862     // private
22863     onBeforeLoad : function(){
22864         if(!this.hasFocus){
22865             return;
22866         }
22867         this.innerList.update(this.loadingText ?
22868                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
22869         this.restrictHeight();
22870         this.selectedIndex = -1;
22871     },
22872
22873     // private
22874     onLoad : function(){
22875         if(!this.hasFocus){
22876             return;
22877         }
22878         if(this.store.getCount() > 0){
22879             this.expand();
22880             this.restrictHeight();
22881             if(this.lastQuery == this.allQuery){
22882                 if(this.editable){
22883                     this.el.dom.select();
22884                 }
22885                 if(!this.selectByValue(this.value, true)){
22886                     this.select(0, true);
22887                 }
22888             }else{
22889                 this.selectNext();
22890                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
22891                     this.taTask.delay(this.typeAheadDelay);
22892                 }
22893             }
22894         }else{
22895             this.onEmptyResults();
22896         }
22897         //this.el.focus();
22898     },
22899
22900     // private
22901     onTypeAhead : function(){
22902         if(this.store.getCount() > 0){
22903             var r = this.store.getAt(0);
22904             var newValue = r.data[this.displayField];
22905             var len = newValue.length;
22906             var selStart = this.getRawValue().length;
22907             if(selStart != len){
22908                 this.setRawValue(newValue);
22909                 this.selectText(selStart, newValue.length);
22910             }
22911         }
22912     },
22913
22914     // private
22915     onSelect : function(record, index){
22916         if(this.fireEvent('beforeselect', this, record, index) !== false){
22917             this.setFromData(index > -1 ? record.data : false);
22918             this.collapse();
22919             this.fireEvent('select', this, record, index);
22920         }
22921     },
22922
22923     /**
22924      * Returns the currently selected field value or empty string if no value is set.
22925      * @return {String} value The selected value
22926      */
22927     getValue : function(){
22928         if(this.valueField){
22929             return typeof this.value != 'undefined' ? this.value : '';
22930         }else{
22931             return Roo.form.ComboBox.superclass.getValue.call(this);
22932         }
22933     },
22934
22935     /**
22936      * Clears any text/value currently set in the field
22937      */
22938     clearValue : function(){
22939         if(this.hiddenField){
22940             this.hiddenField.value = '';
22941         }
22942         this.value = '';
22943         this.setRawValue('');
22944         this.lastSelectionText = '';
22945         this.applyEmptyText();
22946     },
22947
22948     /**
22949      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
22950      * will be displayed in the field.  If the value does not match the data value of an existing item,
22951      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
22952      * Otherwise the field will be blank (although the value will still be set).
22953      * @param {String} value The value to match
22954      */
22955     setValue : function(v){
22956         var text = v;
22957         if(this.valueField){
22958             var r = this.findRecord(this.valueField, v);
22959             if(r){
22960                 text = r.data[this.displayField];
22961             }else if(this.valueNotFoundText !== undefined){
22962                 text = this.valueNotFoundText;
22963             }
22964         }
22965         this.lastSelectionText = text;
22966         if(this.hiddenField){
22967             this.hiddenField.value = v;
22968         }
22969         Roo.form.ComboBox.superclass.setValue.call(this, text);
22970         this.value = v;
22971     },
22972     /**
22973      * @property {Object} the last set data for the element
22974      */
22975     
22976     lastData : false,
22977     /**
22978      * Sets the value of the field based on a object which is related to the record format for the store.
22979      * @param {Object} value the value to set as. or false on reset?
22980      */
22981     setFromData : function(o){
22982         var dv = ''; // display value
22983         var vv = ''; // value value..
22984         this.lastData = o;
22985         if (this.displayField) {
22986             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
22987         } else {
22988             // this is an error condition!!!
22989             console.log('no value field set for '+ this.name);
22990         }
22991         
22992         if(this.valueField){
22993             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
22994         }
22995         if(this.hiddenField){
22996             this.hiddenField.value = vv;
22997             
22998             this.lastSelectionText = dv;
22999             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23000             this.value = vv;
23001             return;
23002         }
23003         // no hidden field.. - we store the value in 'value', but still display
23004         // display field!!!!
23005         this.lastSelectionText = dv;
23006         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23007         this.value = vv;
23008         
23009         
23010     },
23011     // private
23012     reset : function(){
23013         // overridden so that last data is reset..
23014         this.setValue(this.originalValue);
23015         this.clearInvalid();
23016         this.lastData = false;
23017     },
23018     // private
23019     findRecord : function(prop, value){
23020         var record;
23021         if(this.store.getCount() > 0){
23022             this.store.each(function(r){
23023                 if(r.data[prop] == value){
23024                     record = r;
23025                     return false;
23026                 }
23027             });
23028         }
23029         return record;
23030     },
23031
23032     // private
23033     onViewMove : function(e, t){
23034         this.inKeyMode = false;
23035     },
23036
23037     // private
23038     onViewOver : function(e, t){
23039         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23040             return;
23041         }
23042         var item = this.view.findItemFromChild(t);
23043         if(item){
23044             var index = this.view.indexOf(item);
23045             this.select(index, false);
23046         }
23047     },
23048
23049     // private
23050     onViewClick : function(doFocus){
23051         var index = this.view.getSelectedIndexes()[0];
23052         var r = this.store.getAt(index);
23053         if(r){
23054             this.onSelect(r, index);
23055         }
23056         if(doFocus !== false && !this.blockFocus){
23057             this.el.focus();
23058         }
23059     },
23060
23061     // private
23062     restrictHeight : function(){
23063         this.innerList.dom.style.height = '';
23064         var inner = this.innerList.dom;
23065         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23066         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23067         this.list.beginUpdate();
23068         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23069         this.list.alignTo(this.el, this.listAlign);
23070         this.list.endUpdate();
23071     },
23072
23073     // private
23074     onEmptyResults : function(){
23075         this.collapse();
23076     },
23077
23078     /**
23079      * Returns true if the dropdown list is expanded, else false.
23080      */
23081     isExpanded : function(){
23082         return this.list.isVisible();
23083     },
23084
23085     /**
23086      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23087      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23088      * @param {String} value The data value of the item to select
23089      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23090      * selected item if it is not currently in view (defaults to true)
23091      * @return {Boolean} True if the value matched an item in the list, else false
23092      */
23093     selectByValue : function(v, scrollIntoView){
23094         if(v !== undefined && v !== null){
23095             var r = this.findRecord(this.valueField || this.displayField, v);
23096             if(r){
23097                 this.select(this.store.indexOf(r), scrollIntoView);
23098                 return true;
23099             }
23100         }
23101         return false;
23102     },
23103
23104     /**
23105      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23106      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23107      * @param {Number} index The zero-based index of the list item to select
23108      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23109      * selected item if it is not currently in view (defaults to true)
23110      */
23111     select : function(index, scrollIntoView){
23112         this.selectedIndex = index;
23113         this.view.select(index);
23114         if(scrollIntoView !== false){
23115             var el = this.view.getNode(index);
23116             if(el){
23117                 this.innerList.scrollChildIntoView(el, false);
23118             }
23119         }
23120     },
23121
23122     // private
23123     selectNext : function(){
23124         var ct = this.store.getCount();
23125         if(ct > 0){
23126             if(this.selectedIndex == -1){
23127                 this.select(0);
23128             }else if(this.selectedIndex < ct-1){
23129                 this.select(this.selectedIndex+1);
23130             }
23131         }
23132     },
23133
23134     // private
23135     selectPrev : function(){
23136         var ct = this.store.getCount();
23137         if(ct > 0){
23138             if(this.selectedIndex == -1){
23139                 this.select(0);
23140             }else if(this.selectedIndex != 0){
23141                 this.select(this.selectedIndex-1);
23142             }
23143         }
23144     },
23145
23146     // private
23147     onKeyUp : function(e){
23148         if(this.editable !== false && !e.isSpecialKey()){
23149             this.lastKey = e.getKey();
23150             this.dqTask.delay(this.queryDelay);
23151         }
23152     },
23153
23154     // private
23155     validateBlur : function(){
23156         return !this.list || !this.list.isVisible();   
23157     },
23158
23159     // private
23160     initQuery : function(){
23161         this.doQuery(this.getRawValue());
23162     },
23163
23164     // private
23165     doForce : function(){
23166         if(this.el.dom.value.length > 0){
23167             this.el.dom.value =
23168                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23169             this.applyEmptyText();
23170         }
23171     },
23172
23173     /**
23174      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23175      * query allowing the query action to be canceled if needed.
23176      * @param {String} query The SQL query to execute
23177      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23178      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23179      * saved in the current store (defaults to false)
23180      */
23181     doQuery : function(q, forceAll){
23182         if(q === undefined || q === null){
23183             q = '';
23184         }
23185         var qe = {
23186             query: q,
23187             forceAll: forceAll,
23188             combo: this,
23189             cancel:false
23190         };
23191         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23192             return false;
23193         }
23194         q = qe.query;
23195         forceAll = qe.forceAll;
23196         if(forceAll === true || (q.length >= this.minChars)){
23197             if(this.lastQuery != q){
23198                 this.lastQuery = q;
23199                 if(this.mode == 'local'){
23200                     this.selectedIndex = -1;
23201                     if(forceAll){
23202                         this.store.clearFilter();
23203                     }else{
23204                         this.store.filter(this.displayField, q);
23205                     }
23206                     this.onLoad();
23207                 }else{
23208                     this.store.baseParams[this.queryParam] = q;
23209                     this.store.load({
23210                         params: this.getParams(q)
23211                     });
23212                     this.expand();
23213                 }
23214             }else{
23215                 this.selectedIndex = -1;
23216                 this.onLoad();   
23217             }
23218         }
23219     },
23220
23221     // private
23222     getParams : function(q){
23223         var p = {};
23224         //p[this.queryParam] = q;
23225         if(this.pageSize){
23226             p.start = 0;
23227             p.limit = this.pageSize;
23228         }
23229         return p;
23230     },
23231
23232     /**
23233      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23234      */
23235     collapse : function(){
23236         if(!this.isExpanded()){
23237             return;
23238         }
23239         this.list.hide();
23240         Roo.get(document).un('mousedown', this.collapseIf, this);
23241         Roo.get(document).un('mousewheel', this.collapseIf, this);
23242         this.fireEvent('collapse', this);
23243     },
23244
23245     // private
23246     collapseIf : function(e){
23247         if(!e.within(this.wrap) && !e.within(this.list)){
23248             this.collapse();
23249         }
23250     },
23251
23252     /**
23253      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23254      */
23255     expand : function(){
23256         if(this.isExpanded() || !this.hasFocus){
23257             return;
23258         }
23259         this.list.alignTo(this.el, this.listAlign);
23260         this.list.show();
23261         Roo.get(document).on('mousedown', this.collapseIf, this);
23262         Roo.get(document).on('mousewheel', this.collapseIf, this);
23263         this.fireEvent('expand', this);
23264     },
23265
23266     // private
23267     // Implements the default empty TriggerField.onTriggerClick function
23268     onTriggerClick : function(){
23269         if(this.disabled){
23270             return;
23271         }
23272         if(this.isExpanded()){
23273             this.collapse();
23274             if (!this.blockFocus) {
23275                 this.el.focus();
23276             }
23277             
23278         }else {
23279             this.hasFocus = true;
23280             if(this.triggerAction == 'all') {
23281                 this.doQuery(this.allQuery, true);
23282             } else {
23283                 this.doQuery(this.getRawValue());
23284             }
23285             if (!this.blockFocus) {
23286                 this.el.focus();
23287             }
23288         }
23289     }
23290
23291     /** 
23292     * @cfg {Boolean} grow 
23293     * @hide 
23294     */
23295     /** 
23296     * @cfg {Number} growMin 
23297     * @hide 
23298     */
23299     /** 
23300     * @cfg {Number} growMax 
23301     * @hide 
23302     */
23303     /**
23304      * @hide
23305      * @method autoSize
23306      */
23307 });/*
23308  * Based on:
23309  * Ext JS Library 1.1.1
23310  * Copyright(c) 2006-2007, Ext JS, LLC.
23311  *
23312  * Originally Released Under LGPL - original licence link has changed is not relivant.
23313  *
23314  * Fork - LGPL
23315  * <script type="text/javascript">
23316  */
23317 /**
23318  * @class Roo.form.Checkbox
23319  * @extends Roo.form.Field
23320  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23321  * @constructor
23322  * Creates a new Checkbox
23323  * @param {Object} config Configuration options
23324  */
23325 Roo.form.Checkbox = function(config){
23326     Roo.form.Checkbox.superclass.constructor.call(this, config);
23327     this.addEvents({
23328         /**
23329          * @event check
23330          * Fires when the checkbox is checked or unchecked.
23331              * @param {Roo.form.Checkbox} this This checkbox
23332              * @param {Boolean} checked The new checked value
23333              */
23334         check : true
23335     });
23336 };
23337
23338 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23339     /**
23340      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23341      */
23342     focusClass : undefined,
23343     /**
23344      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23345      */
23346     fieldClass: "x-form-field",
23347     /**
23348      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23349      */
23350     checked: false,
23351     /**
23352      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23353      * {tag: "input", type: "checkbox", autocomplete: "off"})
23354      */
23355     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23356     /**
23357      * @cfg {String} boxLabel The text that appears beside the checkbox
23358      */
23359     boxLabel : "",
23360     /**
23361      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23362      */  
23363     inputValue : '1',
23364     /**
23365      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23366      */
23367      valueOff: '0', // value when not checked..
23368
23369     actionMode : 'viewEl', 
23370     //
23371     // private
23372     itemCls : 'x-menu-check-item x-form-item',
23373     groupClass : 'x-menu-group-item',
23374     inputType : 'hidden',
23375     
23376     
23377     inSetChecked: false, // check that we are not calling self...
23378     
23379     inputElement: false, // real input element?
23380     basedOn: false, // ????
23381     
23382     isFormField: true, // not sure where this is needed!!!!
23383
23384     onResize : function(){
23385         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23386         if(!this.boxLabel){
23387             this.el.alignTo(this.wrap, 'c-c');
23388         }
23389     },
23390
23391     initEvents : function(){
23392         Roo.form.Checkbox.superclass.initEvents.call(this);
23393         this.el.on("click", this.onClick,  this);
23394         this.el.on("change", this.onClick,  this);
23395     },
23396
23397
23398     getResizeEl : function(){
23399         return this.wrap;
23400     },
23401
23402     getPositionEl : function(){
23403         return this.wrap;
23404     },
23405
23406     // private
23407     onRender : function(ct, position){
23408         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23409         /*
23410         if(this.inputValue !== undefined){
23411             this.el.dom.value = this.inputValue;
23412         }
23413         */
23414         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23415         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23416         var viewEl = this.wrap.createChild({ 
23417             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23418         this.viewEl = viewEl;   
23419         this.wrap.on('click', this.onClick,  this); 
23420         
23421         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23422         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23423         
23424         
23425         
23426         if(this.boxLabel){
23427             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23428         //    viewEl.on('click', this.onClick,  this); 
23429         }
23430         //if(this.checked){
23431             this.setChecked(this.checked);
23432         //}else{
23433             //this.checked = this.el.dom;
23434         //}
23435
23436     },
23437
23438     // private
23439     initValue : Roo.emptyFn,
23440
23441     /**
23442      * Returns the checked state of the checkbox.
23443      * @return {Boolean} True if checked, else false
23444      */
23445     getValue : function(){
23446         if(this.el){
23447             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23448         }
23449         return this.valueOff;
23450         
23451     },
23452
23453         // private
23454     onClick : function(){ 
23455         this.setChecked(!this.checked);
23456
23457         //if(this.el.dom.checked != this.checked){
23458         //    this.setValue(this.el.dom.checked);
23459        // }
23460     },
23461
23462     /**
23463      * Sets the checked state of the checkbox.
23464      * On is always based on a string comparison between inputValue and the param.
23465      * @param {Boolean/String} value - the value to set 
23466      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23467      */
23468     setValue : function(v,suppressEvent){
23469         
23470         
23471         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23472         //if(this.el && this.el.dom){
23473         //    this.el.dom.checked = this.checked;
23474         //    this.el.dom.defaultChecked = this.checked;
23475         //}
23476         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23477         //this.fireEvent("check", this, this.checked);
23478     },
23479     // private..
23480     setChecked : function(state,suppressEvent)
23481     {
23482         if (this.inSetChecked) {
23483             this.checked = state;
23484             return;
23485         }
23486         
23487     
23488         if(this.wrap){
23489             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23490         }
23491         this.checked = state;
23492         if(suppressEvent !== true){
23493             this.fireEvent('checkchange', this, state);
23494         }
23495         this.inSetChecked = true;
23496         this.el.dom.value = state ? this.inputValue : this.valueOff;
23497         this.inSetChecked = false;
23498         
23499     },
23500     // handle setting of hidden value by some other method!!?!?
23501     setFromHidden: function()
23502     {
23503         if(!this.el){
23504             return;
23505         }
23506         //console.log("SET FROM HIDDEN");
23507         //alert('setFrom hidden');
23508         this.setValue(this.el.dom.value);
23509     },
23510     
23511     onDestroy : function()
23512     {
23513         if(this.viewEl){
23514             Roo.get(this.viewEl).remove();
23515         }
23516          
23517         Roo.form.Checkbox.superclass.onDestroy.call(this);
23518     }
23519
23520 });/*
23521  * Based on:
23522  * Ext JS Library 1.1.1
23523  * Copyright(c) 2006-2007, Ext JS, LLC.
23524  *
23525  * Originally Released Under LGPL - original licence link has changed is not relivant.
23526  *
23527  * Fork - LGPL
23528  * <script type="text/javascript">
23529  */
23530  
23531 /**
23532  * @class Roo.form.Radio
23533  * @extends Roo.form.Checkbox
23534  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23535  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23536  * @constructor
23537  * Creates a new Radio
23538  * @param {Object} config Configuration options
23539  */
23540 Roo.form.Radio = function(){
23541     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23542 };
23543 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23544     inputType: 'radio',
23545
23546     /**
23547      * If this radio is part of a group, it will return the selected value
23548      * @return {String}
23549      */
23550     getGroupValue : function(){
23551         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23552     }
23553 });//<script type="text/javascript">
23554
23555 /*
23556  * Ext JS Library 1.1.1
23557  * Copyright(c) 2006-2007, Ext JS, LLC.
23558  * licensing@extjs.com
23559  * 
23560  * http://www.extjs.com/license
23561  */
23562  
23563  /*
23564   * 
23565   * Known bugs:
23566   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23567   * - IE ? - no idea how much works there.
23568   * 
23569   * 
23570   * 
23571   */
23572  
23573
23574 /**
23575  * @class Ext.form.HtmlEditor
23576  * @extends Ext.form.Field
23577  * Provides a lightweight HTML Editor component.
23578  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23579  * 
23580  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23581  * supported by this editor.</b><br/><br/>
23582  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23583  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23584  */
23585 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23586       /**
23587      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23588      */
23589     toolbars : false,
23590     /**
23591      * @cfg {String} createLinkText The default text for the create link prompt
23592      */
23593     createLinkText : 'Please enter the URL for the link:',
23594     /**
23595      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23596      */
23597     defaultLinkValue : 'http:/'+'/',
23598    
23599     
23600     // id of frame..
23601     frameId: false,
23602     
23603     // private properties
23604     validationEvent : false,
23605     deferHeight: true,
23606     initialized : false,
23607     activated : false,
23608     sourceEditMode : false,
23609     onFocus : Roo.emptyFn,
23610     iframePad:3,
23611     hideMode:'offsets',
23612     defaultAutoCreate : {
23613         tag: "textarea",
23614         style:"width:500px;height:300px;",
23615         autocomplete: "off"
23616     },
23617
23618     // private
23619     initComponent : function(){
23620         this.addEvents({
23621             /**
23622              * @event initialize
23623              * Fires when the editor is fully initialized (including the iframe)
23624              * @param {HtmlEditor} this
23625              */
23626             initialize: true,
23627             /**
23628              * @event activate
23629              * Fires when the editor is first receives the focus. Any insertion must wait
23630              * until after this event.
23631              * @param {HtmlEditor} this
23632              */
23633             activate: true,
23634              /**
23635              * @event beforesync
23636              * Fires before the textarea is updated with content from the editor iframe. Return false
23637              * to cancel the sync.
23638              * @param {HtmlEditor} this
23639              * @param {String} html
23640              */
23641             beforesync: true,
23642              /**
23643              * @event beforepush
23644              * Fires before the iframe editor is updated with content from the textarea. Return false
23645              * to cancel the push.
23646              * @param {HtmlEditor} this
23647              * @param {String} html
23648              */
23649             beforepush: true,
23650              /**
23651              * @event sync
23652              * Fires when the textarea is updated with content from the editor iframe.
23653              * @param {HtmlEditor} this
23654              * @param {String} html
23655              */
23656             sync: true,
23657              /**
23658              * @event push
23659              * Fires when the iframe editor is updated with content from the textarea.
23660              * @param {HtmlEditor} this
23661              * @param {String} html
23662              */
23663             push: true,
23664              /**
23665              * @event editmodechange
23666              * Fires when the editor switches edit modes
23667              * @param {HtmlEditor} this
23668              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23669              */
23670             editmodechange: true,
23671             /**
23672              * @event editorevent
23673              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23674              * @param {HtmlEditor} this
23675              */
23676             editorevent: true
23677         })
23678     },
23679
23680     /**
23681      * Protected method that will not generally be called directly. It
23682      * is called when the editor creates its toolbar. Override this method if you need to
23683      * add custom toolbar buttons.
23684      * @param {HtmlEditor} editor
23685      */
23686     createToolbar : function(editor){
23687         if (!editor.toolbars || !editor.toolbars.length) {
23688             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23689         }
23690         
23691         for (var i =0 ; i < editor.toolbars.length;i++) {
23692             editor.toolbars[i].init(editor);
23693         }
23694          
23695         
23696     },
23697
23698     /**
23699      * Protected method that will not generally be called directly. It
23700      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23701      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23702      */
23703     getDocMarkup : function(){
23704         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23705     },
23706
23707     // private
23708     onRender : function(ct, position){
23709         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23710         this.el.dom.style.border = '0 none';
23711         this.el.dom.setAttribute('tabIndex', -1);
23712         this.el.addClass('x-hidden');
23713         if(Roo.isIE){ // fix IE 1px bogus margin
23714             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23715         }
23716         this.wrap = this.el.wrap({
23717             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23718         });
23719
23720         this.frameId = Roo.id();
23721         this.createToolbar(this);
23722         
23723         
23724         
23725         
23726       
23727         
23728         var iframe = this.wrap.createChild({
23729             tag: 'iframe',
23730             id: this.frameId,
23731             name: this.frameId,
23732             frameBorder : 'no',
23733             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23734         });
23735         
23736        // console.log(iframe);
23737         //this.wrap.dom.appendChild(iframe);
23738
23739         this.iframe = iframe.dom;
23740
23741          this.assignDocWin();
23742         
23743         this.doc.designMode = 'on';
23744        
23745         this.doc.open();
23746         this.doc.write(this.getDocMarkup());
23747         this.doc.close();
23748
23749         
23750         var task = { // must defer to wait for browser to be ready
23751             run : function(){
23752                 //console.log("run task?" + this.doc.readyState);
23753                 this.assignDocWin();
23754                 if(this.doc.body || this.doc.readyState == 'complete'){
23755                     try {
23756                         this.doc.designMode="on";
23757                     } catch (e) {
23758                         return;
23759                     }
23760                     Roo.TaskMgr.stop(task);
23761                     this.initEditor.defer(10, this);
23762                 }
23763             },
23764             interval : 10,
23765             duration:10000,
23766             scope: this
23767         };
23768         Roo.TaskMgr.start(task);
23769
23770         if(!this.width){
23771             this.setSize(this.el.getSize());
23772         }
23773     },
23774
23775     // private
23776     onResize : function(w, h){
23777         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23778         if(this.el && this.iframe){
23779             if(typeof w == 'number'){
23780                 var aw = w - this.wrap.getFrameWidth('lr');
23781                 this.el.setWidth(this.adjustWidth('textarea', aw));
23782                 this.iframe.style.width = aw + 'px';
23783             }
23784             if(typeof h == 'number'){
23785                 var tbh = 0;
23786                 for (var i =0; i < this.toolbars.length;i++) {
23787                     // fixme - ask toolbars for heights?
23788                     tbh += this.toolbars[i].tb.el.getHeight();
23789                 }
23790                 
23791                 
23792                 
23793                 
23794                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23795                 this.el.setHeight(this.adjustWidth('textarea', ah));
23796                 this.iframe.style.height = ah + 'px';
23797                 if(this.doc){
23798                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
23799                 }
23800             }
23801         }
23802     },
23803
23804     /**
23805      * Toggles the editor between standard and source edit mode.
23806      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23807      */
23808     toggleSourceEdit : function(sourceEditMode){
23809         
23810         this.sourceEditMode = sourceEditMode === true;
23811         
23812         if(this.sourceEditMode){
23813           
23814             this.syncValue();
23815             this.iframe.className = 'x-hidden';
23816             this.el.removeClass('x-hidden');
23817             this.el.dom.removeAttribute('tabIndex');
23818             this.el.focus();
23819         }else{
23820              
23821             this.pushValue();
23822             this.iframe.className = '';
23823             this.el.addClass('x-hidden');
23824             this.el.dom.setAttribute('tabIndex', -1);
23825             this.deferFocus();
23826         }
23827         this.setSize(this.wrap.getSize());
23828         this.fireEvent('editmodechange', this, this.sourceEditMode);
23829     },
23830
23831     // private used internally
23832     createLink : function(){
23833         var url = prompt(this.createLinkText, this.defaultLinkValue);
23834         if(url && url != 'http:/'+'/'){
23835             this.relayCmd('createlink', url);
23836         }
23837     },
23838
23839     // private (for BoxComponent)
23840     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23841
23842     // private (for BoxComponent)
23843     getResizeEl : function(){
23844         return this.wrap;
23845     },
23846
23847     // private (for BoxComponent)
23848     getPositionEl : function(){
23849         return this.wrap;
23850     },
23851
23852     // private
23853     initEvents : function(){
23854         this.originalValue = this.getValue();
23855     },
23856
23857     /**
23858      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23859      * @method
23860      */
23861     markInvalid : Roo.emptyFn,
23862     /**
23863      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23864      * @method
23865      */
23866     clearInvalid : Roo.emptyFn,
23867
23868     setValue : function(v){
23869         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23870         this.pushValue();
23871     },
23872
23873     /**
23874      * Protected method that will not generally be called directly. If you need/want
23875      * custom HTML cleanup, this is the method you should override.
23876      * @param {String} html The HTML to be cleaned
23877      * return {String} The cleaned HTML
23878      */
23879     cleanHtml : function(html){
23880         html = String(html);
23881         if(html.length > 5){
23882             if(Roo.isSafari){ // strip safari nonsense
23883                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23884             }
23885         }
23886         if(html == '&nbsp;'){
23887             html = '';
23888         }
23889         return html;
23890     },
23891
23892     /**
23893      * Protected method that will not generally be called directly. Syncs the contents
23894      * of the editor iframe with the textarea.
23895      */
23896     syncValue : function(){
23897         if(this.initialized){
23898             var bd = (this.doc.body || this.doc.documentElement);
23899             var html = bd.innerHTML;
23900             if(Roo.isSafari){
23901                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23902                 var m = bs.match(/text-align:(.*?);/i);
23903                 if(m && m[1]){
23904                     html = '<div style="'+m[0]+'">' + html + '</div>';
23905                 }
23906             }
23907             html = this.cleanHtml(html);
23908             if(this.fireEvent('beforesync', this, html) !== false){
23909                 this.el.dom.value = html;
23910                 this.fireEvent('sync', this, html);
23911             }
23912         }
23913     },
23914
23915     /**
23916      * Protected method that will not generally be called directly. Pushes the value of the textarea
23917      * into the iframe editor.
23918      */
23919     pushValue : function(){
23920         if(this.initialized){
23921             var v = this.el.dom.value;
23922             if(v.length < 1){
23923                 v = '&#160;';
23924             }
23925             if(this.fireEvent('beforepush', this, v) !== false){
23926                 (this.doc.body || this.doc.documentElement).innerHTML = v;
23927                 this.fireEvent('push', this, v);
23928             }
23929         }
23930     },
23931
23932     // private
23933     deferFocus : function(){
23934         this.focus.defer(10, this);
23935     },
23936
23937     // doc'ed in Field
23938     focus : function(){
23939         if(this.win && !this.sourceEditMode){
23940             this.win.focus();
23941         }else{
23942             this.el.focus();
23943         }
23944     },
23945     
23946     assignDocWin: function()
23947     {
23948         var iframe = this.iframe;
23949         
23950          if(Roo.isIE){
23951             this.doc = iframe.contentWindow.document;
23952             this.win = iframe.contentWindow;
23953         } else {
23954             if (!Roo.get(this.frameId)) {
23955                 return;
23956             }
23957             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
23958             this.win = Roo.get(this.frameId).dom.contentWindow;
23959         }
23960     },
23961     
23962     // private
23963     initEditor : function(){
23964         //console.log("INIT EDITOR");
23965         this.assignDocWin();
23966         
23967         
23968         
23969         this.doc.designMode="on";
23970         this.doc.open();
23971         this.doc.write(this.getDocMarkup());
23972         this.doc.close();
23973         
23974         var dbody = (this.doc.body || this.doc.documentElement);
23975         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
23976         // this copies styles from the containing element into thsi one..
23977         // not sure why we need all of this..
23978         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
23979         ss['background-attachment'] = 'fixed'; // w3c
23980         dbody.bgProperties = 'fixed'; // ie
23981         Roo.DomHelper.applyStyles(dbody, ss);
23982         Roo.EventManager.on(this.doc, {
23983             'mousedown': this.onEditorEvent,
23984             'dblclick': this.onEditorEvent,
23985             'click': this.onEditorEvent,
23986             'keyup': this.onEditorEvent,
23987             buffer:100,
23988             scope: this
23989         });
23990         if(Roo.isGecko){
23991             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
23992         }
23993         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
23994             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
23995         }
23996         this.initialized = true;
23997
23998         this.fireEvent('initialize', this);
23999         this.pushValue();
24000     },
24001
24002     // private
24003     onDestroy : function(){
24004         
24005         
24006         
24007         if(this.rendered){
24008             
24009             for (var i =0; i < this.toolbars.length;i++) {
24010                 // fixme - ask toolbars for heights?
24011                 this.toolbars[i].onDestroy();
24012             }
24013             
24014             this.wrap.dom.innerHTML = '';
24015             this.wrap.remove();
24016         }
24017     },
24018
24019     // private
24020     onFirstFocus : function(){
24021         
24022         this.assignDocWin();
24023         
24024         
24025         this.activated = true;
24026         for (var i =0; i < this.toolbars.length;i++) {
24027             this.toolbars[i].onFirstFocus();
24028         }
24029        
24030         if(Roo.isGecko){ // prevent silly gecko errors
24031             this.win.focus();
24032             var s = this.win.getSelection();
24033             if(!s.focusNode || s.focusNode.nodeType != 3){
24034                 var r = s.getRangeAt(0);
24035                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24036                 r.collapse(true);
24037                 this.deferFocus();
24038             }
24039             try{
24040                 this.execCmd('useCSS', true);
24041                 this.execCmd('styleWithCSS', false);
24042             }catch(e){}
24043         }
24044         this.fireEvent('activate', this);
24045     },
24046
24047     // private
24048     adjustFont: function(btn){
24049         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24050         //if(Roo.isSafari){ // safari
24051         //    adjust *= 2;
24052        // }
24053         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24054         if(Roo.isSafari){ // safari
24055             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24056             v =  (v < 10) ? 10 : v;
24057             v =  (v > 48) ? 48 : v;
24058             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24059             
24060         }
24061         
24062         
24063         v = Math.max(1, v+adjust);
24064         
24065         this.execCmd('FontSize', v  );
24066     },
24067
24068     onEditorEvent : function(e){
24069         this.fireEvent('editorevent', this, e);
24070       //  this.updateToolbar();
24071         this.syncValue();
24072     },
24073
24074     insertTag : function(tg)
24075     {
24076         // could be a bit smarter... -> wrap the current selected tRoo..
24077         
24078         this.execCmd("formatblock",   tg);
24079         
24080     },
24081     
24082     insertText : function(txt)
24083     {
24084         
24085         
24086         range = this.createRange();
24087         range.deleteContents();
24088                //alert(Sender.getAttribute('label'));
24089                
24090         range.insertNode(this.doc.createTextNode(txt));
24091     } ,
24092     
24093     // private
24094     relayBtnCmd : function(btn){
24095         this.relayCmd(btn.cmd);
24096     },
24097
24098     /**
24099      * Executes a Midas editor command on the editor document and performs necessary focus and
24100      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24101      * @param {String} cmd The Midas command
24102      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24103      */
24104     relayCmd : function(cmd, value){
24105         this.win.focus();
24106         this.execCmd(cmd, value);
24107         this.fireEvent('editorevent', this);
24108         //this.updateToolbar();
24109         this.deferFocus();
24110     },
24111
24112     /**
24113      * Executes a Midas editor command directly on the editor document.
24114      * For visual commands, you should use {@link #relayCmd} instead.
24115      * <b>This should only be called after the editor is initialized.</b>
24116      * @param {String} cmd The Midas command
24117      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24118      */
24119     execCmd : function(cmd, value){
24120         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24121         this.syncValue();
24122     },
24123
24124     // private
24125     applyCommand : function(e){
24126         if(e.ctrlKey){
24127             var c = e.getCharCode(), cmd;
24128             if(c > 0){
24129                 c = String.fromCharCode(c);
24130                 switch(c){
24131                     case 'b':
24132                         cmd = 'bold';
24133                     break;
24134                     case 'i':
24135                         cmd = 'italic';
24136                     break;
24137                     case 'u':
24138                         cmd = 'underline';
24139                     break;
24140                 }
24141                 if(cmd){
24142                     this.win.focus();
24143                     this.execCmd(cmd);
24144                     this.deferFocus();
24145                     e.preventDefault();
24146                 }
24147             }
24148         }
24149     },
24150
24151     /**
24152      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24153      * to insert tRoo.
24154      * @param {String} text
24155      */
24156     insertAtCursor : function(text){
24157         if(!this.activated){
24158             return;
24159         }
24160         if(Roo.isIE){
24161             this.win.focus();
24162             var r = this.doc.selection.createRange();
24163             if(r){
24164                 r.collapse(true);
24165                 r.pasteHTML(text);
24166                 this.syncValue();
24167                 this.deferFocus();
24168             }
24169         }else if(Roo.isGecko || Roo.isOpera){
24170             this.win.focus();
24171             this.execCmd('InsertHTML', text);
24172             this.deferFocus();
24173         }else if(Roo.isSafari){
24174             this.execCmd('InsertText', text);
24175             this.deferFocus();
24176         }
24177     },
24178
24179     // private
24180     fixKeys : function(){ // load time branching for fastest keydown performance
24181         if(Roo.isIE){
24182             return function(e){
24183                 var k = e.getKey(), r;
24184                 if(k == e.TAB){
24185                     e.stopEvent();
24186                     r = this.doc.selection.createRange();
24187                     if(r){
24188                         r.collapse(true);
24189                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24190                         this.deferFocus();
24191                     }
24192                 }else if(k == e.ENTER){
24193                     r = this.doc.selection.createRange();
24194                     if(r){
24195                         var target = r.parentElement();
24196                         if(!target || target.tagName.toLowerCase() != 'li'){
24197                             e.stopEvent();
24198                             r.pasteHTML('<br />');
24199                             r.collapse(false);
24200                             r.select();
24201                         }
24202                     }
24203                 }
24204             };
24205         }else if(Roo.isOpera){
24206             return function(e){
24207                 var k = e.getKey();
24208                 if(k == e.TAB){
24209                     e.stopEvent();
24210                     this.win.focus();
24211                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24212                     this.deferFocus();
24213                 }
24214             };
24215         }else if(Roo.isSafari){
24216             return function(e){
24217                 var k = e.getKey();
24218                 if(k == e.TAB){
24219                     e.stopEvent();
24220                     this.execCmd('InsertText','\t');
24221                     this.deferFocus();
24222                 }
24223              };
24224         }
24225     }(),
24226     
24227     getAllAncestors: function()
24228     {
24229         var p = this.getSelectedNode();
24230         var a = [];
24231         if (!p) {
24232             a.push(p); // push blank onto stack..
24233             p = this.getParentElement();
24234         }
24235         
24236         
24237         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24238             a.push(p);
24239             p = p.parentNode;
24240         }
24241         a.push(this.doc.body);
24242         return a;
24243     },
24244     lastSel : false,
24245     lastSelNode : false,
24246     
24247     
24248     getSelection : function() 
24249     {
24250         this.assignDocWin();
24251         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24252     },
24253     
24254     getSelectedNode: function() 
24255     {
24256         // this may only work on Gecko!!!
24257         
24258         // should we cache this!!!!
24259         
24260         
24261         
24262          
24263         var range = this.createRange(this.getSelection());
24264         
24265         if (Roo.isIE) {
24266             var parent = range.parentElement();
24267             while (true) {
24268                 var testRange = range.duplicate();
24269                 testRange.moveToElementText(parent);
24270                 if (testRange.inRange(range)) {
24271                     break;
24272                 }
24273                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24274                     break;
24275                 }
24276                 parent = parent.parentElement;
24277             }
24278             return parent;
24279         }
24280         
24281         
24282         var ar = range.endContainer.childNodes;
24283         if (!ar.length) {
24284             ar = range.commonAncestorContainer.childNodes;
24285             //alert(ar.length);
24286         }
24287         var nodes = [];
24288         var other_nodes = [];
24289         var has_other_nodes = false;
24290         for (var i=0;i<ar.length;i++) {
24291             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24292                 continue;
24293             }
24294             // fullly contained node.
24295             
24296             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24297                 nodes.push(ar[i]);
24298                 continue;
24299             }
24300             
24301             // probably selected..
24302             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24303                 other_nodes.push(ar[i]);
24304                 continue;
24305             }
24306             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24307                 continue;
24308             }
24309             
24310             
24311             has_other_nodes = true;
24312         }
24313         if (!nodes.length && other_nodes.length) {
24314             nodes= other_nodes;
24315         }
24316         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24317             return false;
24318         }
24319         
24320         return nodes[0];
24321     },
24322     createRange: function(sel)
24323     {
24324         // this has strange effects when using with 
24325         // top toolbar - not sure if it's a great idea.
24326         //this.editor.contentWindow.focus();
24327         if (typeof sel != "undefined") {
24328             try {
24329                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24330             } catch(e) {
24331                 return this.doc.createRange();
24332             }
24333         } else {
24334             return this.doc.createRange();
24335         }
24336     },
24337     getParentElement: function()
24338     {
24339         
24340         this.assignDocWin();
24341         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24342         
24343         var range = this.createRange(sel);
24344          
24345         try {
24346             var p = range.commonAncestorContainer;
24347             while (p.nodeType == 3) { // text node
24348                 p = p.parentNode;
24349             }
24350             return p;
24351         } catch (e) {
24352             return null;
24353         }
24354     
24355     },
24356     
24357     
24358     
24359     // BC Hacks - cause I cant work out what i was trying to do..
24360     rangeIntersectsNode : function(range, node)
24361     {
24362         var nodeRange = node.ownerDocument.createRange();
24363         try {
24364             nodeRange.selectNode(node);
24365         }
24366         catch (e) {
24367             nodeRange.selectNodeContents(node);
24368         }
24369
24370         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24371                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24372     },
24373     rangeCompareNode : function(range, node) {
24374         var nodeRange = node.ownerDocument.createRange();
24375         try {
24376             nodeRange.selectNode(node);
24377         } catch (e) {
24378             nodeRange.selectNodeContents(node);
24379         }
24380         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24381         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24382
24383         if (nodeIsBefore && !nodeIsAfter)
24384             return 0;
24385         if (!nodeIsBefore && nodeIsAfter)
24386             return 1;
24387         if (nodeIsBefore && nodeIsAfter)
24388             return 2;
24389
24390         return 3;
24391     }
24392
24393     
24394     
24395     // hide stuff that is not compatible
24396     /**
24397      * @event blur
24398      * @hide
24399      */
24400     /**
24401      * @event change
24402      * @hide
24403      */
24404     /**
24405      * @event focus
24406      * @hide
24407      */
24408     /**
24409      * @event specialkey
24410      * @hide
24411      */
24412     /**
24413      * @cfg {String} fieldClass @hide
24414      */
24415     /**
24416      * @cfg {String} focusClass @hide
24417      */
24418     /**
24419      * @cfg {String} autoCreate @hide
24420      */
24421     /**
24422      * @cfg {String} inputType @hide
24423      */
24424     /**
24425      * @cfg {String} invalidClass @hide
24426      */
24427     /**
24428      * @cfg {String} invalidText @hide
24429      */
24430     /**
24431      * @cfg {String} msgFx @hide
24432      */
24433     /**
24434      * @cfg {String} validateOnBlur @hide
24435      */
24436 });// <script type="text/javascript">
24437 /*
24438  * Based on
24439  * Ext JS Library 1.1.1
24440  * Copyright(c) 2006-2007, Ext JS, LLC.
24441  *  
24442  
24443  */
24444
24445 /**
24446  * @class Roo.form.HtmlEditorToolbar1
24447  * Basic Toolbar
24448  * 
24449  * Usage:
24450  *
24451  new Roo.form.HtmlEditor({
24452     ....
24453     toolbars : [
24454         new Roo.form.HtmlEditorToolbar1({
24455             disable : { fonts: 1 , format: 1, ..., ... , ...],
24456             btns : [ .... ]
24457         })
24458     }
24459      
24460  * 
24461  * @cfg {Object} disable List of elements to disable..
24462  * @cfg {Array} btns List of additional buttons.
24463  * 
24464  * 
24465  * NEEDS Extra CSS? 
24466  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24467  */
24468  
24469 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24470 {
24471     
24472     Roo.apply(this, config);
24473     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24474     // dont call parent... till later.
24475 }
24476
24477 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24478     
24479     tb: false,
24480     
24481     rendered: false,
24482     
24483     editor : false,
24484     /**
24485      * @cfg {Object} disable  List of toolbar elements to disable
24486          
24487      */
24488     disable : false,
24489       /**
24490      * @cfg {Array} fontFamilies An array of available font families
24491      */
24492     fontFamilies : [
24493         'Arial',
24494         'Courier New',
24495         'Tahoma',
24496         'Times New Roman',
24497         'Verdana'
24498     ],
24499     
24500     specialChars : [
24501            "&#169;",
24502           "&#174;",     
24503           "&#8482;",    
24504           "&#163;" ,    
24505          // "&#8212;",    
24506           "&#8230;",    
24507           "&#247;" ,    
24508         //  "&#225;" ,     ?? a acute?
24509            "&#8364;"    , //Euro
24510        //   "&#8220;"    ,
24511         //  "&#8221;"    ,
24512         //  "&#8226;"    ,
24513           "&#176;"  //   , // degrees
24514
24515          // "&#233;"     , // e ecute
24516          // "&#250;"     , // u ecute?
24517     ],
24518     inputElements : [ 
24519             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24520             "input:submit", "input:button", "select", "textarea", "label" ],
24521     formats : [
24522         ["p"] ,  
24523         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24524         ["pre"],[ "code"], 
24525         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24526     ],
24527      /**
24528      * @cfg {String} defaultFont default font to use.
24529      */
24530     defaultFont: 'tahoma',
24531    
24532     fontSelect : false,
24533     
24534     
24535     formatCombo : false,
24536     
24537     init : function(editor)
24538     {
24539         this.editor = editor;
24540         
24541         
24542         var fid = editor.frameId;
24543         var etb = this;
24544         function btn(id, toggle, handler){
24545             var xid = fid + '-'+ id ;
24546             return {
24547                 id : xid,
24548                 cmd : id,
24549                 cls : 'x-btn-icon x-edit-'+id,
24550                 enableToggle:toggle !== false,
24551                 scope: editor, // was editor...
24552                 handler:handler||editor.relayBtnCmd,
24553                 clickEvent:'mousedown',
24554                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24555                 tabIndex:-1
24556             };
24557         }
24558         
24559         
24560         
24561         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24562         this.tb = tb;
24563          // stop form submits
24564         tb.el.on('click', function(e){
24565             e.preventDefault(); // what does this do?
24566         });
24567
24568         if(!this.disable.font && !Roo.isSafari){
24569             /* why no safari for fonts
24570             editor.fontSelect = tb.el.createChild({
24571                 tag:'select',
24572                 tabIndex: -1,
24573                 cls:'x-font-select',
24574                 html: editor.createFontOptions()
24575             });
24576             editor.fontSelect.on('change', function(){
24577                 var font = editor.fontSelect.dom.value;
24578                 editor.relayCmd('fontname', font);
24579                 editor.deferFocus();
24580             }, editor);
24581             tb.add(
24582                 editor.fontSelect.dom,
24583                 '-'
24584             );
24585             */
24586         };
24587         if(!this.disable.formats){
24588             this.formatCombo = new Roo.form.ComboBox({
24589                 store: new Roo.data.SimpleStore({
24590                     id : 'tag',
24591                     fields: ['tag'],
24592                     data : this.formats // from states.js
24593                 }),
24594                 blockFocus : true,
24595                 //autoCreate : {tag: "div",  size: "20"},
24596                 displayField:'tag',
24597                 typeAhead: false,
24598                 mode: 'local',
24599                 editable : false,
24600                 triggerAction: 'all',
24601                 emptyText:'Add tag',
24602                 selectOnFocus:true,
24603                 width:135,
24604                 listeners : {
24605                     'select': function(c, r, i) {
24606                         editor.insertTag(r.get('tag'));
24607                         editor.focus();
24608                     }
24609                 }
24610
24611             });
24612             tb.addField(this.formatCombo);
24613             
24614         }
24615         
24616         if(!this.disable.format){
24617             tb.add(
24618                 btn('bold'),
24619                 btn('italic'),
24620                 btn('underline')
24621             );
24622         };
24623         if(!this.disable.fontSize){
24624             tb.add(
24625                 '-',
24626                 
24627                 
24628                 btn('increasefontsize', false, editor.adjustFont),
24629                 btn('decreasefontsize', false, editor.adjustFont)
24630             );
24631         };
24632         
24633         
24634         if(this.disable.colors){
24635             tb.add(
24636                 '-', {
24637                     id:editor.frameId +'-forecolor',
24638                     cls:'x-btn-icon x-edit-forecolor',
24639                     clickEvent:'mousedown',
24640                     tooltip: this.buttonTips['forecolor'] || undefined,
24641                     tabIndex:-1,
24642                     menu : new Roo.menu.ColorMenu({
24643                         allowReselect: true,
24644                         focus: Roo.emptyFn,
24645                         value:'000000',
24646                         plain:true,
24647                         selectHandler: function(cp, color){
24648                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
24649                             editor.deferFocus();
24650                         },
24651                         scope: editor,
24652                         clickEvent:'mousedown'
24653                     })
24654                 }, {
24655                     id:editor.frameId +'backcolor',
24656                     cls:'x-btn-icon x-edit-backcolor',
24657                     clickEvent:'mousedown',
24658                     tooltip: this.buttonTips['backcolor'] || undefined,
24659                     tabIndex:-1,
24660                     menu : new Roo.menu.ColorMenu({
24661                         focus: Roo.emptyFn,
24662                         value:'FFFFFF',
24663                         plain:true,
24664                         allowReselect: true,
24665                         selectHandler: function(cp, color){
24666                             if(Roo.isGecko){
24667                                 editor.execCmd('useCSS', false);
24668                                 editor.execCmd('hilitecolor', color);
24669                                 editor.execCmd('useCSS', true);
24670                                 editor.deferFocus();
24671                             }else{
24672                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
24673                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
24674                                 editor.deferFocus();
24675                             }
24676                         },
24677                         scope:editor,
24678                         clickEvent:'mousedown'
24679                     })
24680                 }
24681             );
24682         };
24683         // now add all the items...
24684         
24685
24686         if(!this.disable.alignments){
24687             tb.add(
24688                 '-',
24689                 btn('justifyleft'),
24690                 btn('justifycenter'),
24691                 btn('justifyright')
24692             );
24693         };
24694
24695         //if(!Roo.isSafari){
24696             if(!this.disable.links){
24697                 tb.add(
24698                     '-',
24699                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
24700                 );
24701             };
24702
24703             if(!this.disable.lists){
24704                 tb.add(
24705                     '-',
24706                     btn('insertorderedlist'),
24707                     btn('insertunorderedlist')
24708                 );
24709             }
24710             if(!this.disable.sourceEdit){
24711                 tb.add(
24712                     '-',
24713                     btn('sourceedit', true, function(btn){
24714                         this.toggleSourceEdit(btn.pressed);
24715                     })
24716                 );
24717             }
24718         //}
24719         
24720         var smenu = { };
24721         // special menu.. - needs to be tidied up..
24722         if (!this.disable.special) {
24723             smenu = {
24724                 text: "&#169;",
24725                 cls: 'x-edit-none',
24726                 menu : {
24727                     items : []
24728                    }
24729             };
24730             for (var i =0; i < this.specialChars.length; i++) {
24731                 smenu.menu.items.push({
24732                     
24733                     text: this.specialChars[i],
24734                     handler: function(a,b) {
24735                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
24736                     },
24737                     tabIndex:-1
24738                 });
24739             }
24740             
24741             
24742             tb.add(smenu);
24743             
24744             
24745         }
24746         if (this.btns) {
24747             for(var i =0; i< this.btns.length;i++) {
24748                 var b = this.btns[i];
24749                 b.cls =  'x-edit-none';
24750                 b.scope = editor;
24751                 tb.add(b);
24752             }
24753         
24754         }
24755         
24756         
24757         
24758         // disable everything...
24759         
24760         this.tb.items.each(function(item){
24761            if(item.id != editor.frameId+ '-sourceedit'){
24762                 item.disable();
24763             }
24764         });
24765         this.rendered = true;
24766         
24767         // the all the btns;
24768         editor.on('editorevent', this.updateToolbar, this);
24769         // other toolbars need to implement this..
24770         //editor.on('editmodechange', this.updateToolbar, this);
24771     },
24772     
24773     
24774     
24775     /**
24776      * Protected method that will not generally be called directly. It triggers
24777      * a toolbar update by reading the markup state of the current selection in the editor.
24778      */
24779     updateToolbar: function(){
24780
24781         if(!this.editor.activated){
24782             this.editor.onFirstFocus();
24783             return;
24784         }
24785
24786         var btns = this.tb.items.map, 
24787             doc = this.editor.doc,
24788             frameId = this.editor.frameId;
24789
24790         if(!this.disable.font && !Roo.isSafari){
24791             /*
24792             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
24793             if(name != this.fontSelect.dom.value){
24794                 this.fontSelect.dom.value = name;
24795             }
24796             */
24797         }
24798         if(!this.disable.format){
24799             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
24800             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
24801             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
24802         }
24803         if(!this.disable.alignments){
24804             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
24805             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
24806             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
24807         }
24808         if(!Roo.isSafari && !this.disable.lists){
24809             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
24810             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
24811         }
24812         
24813         var ans = this.editor.getAllAncestors();
24814         if (this.formatCombo) {
24815             
24816             
24817             var store = this.formatCombo.store;
24818             this.formatCombo.setValue("");
24819             for (var i =0; i < ans.length;i++) {
24820                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
24821                     // select it..
24822                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24823                     break;
24824                 }
24825             }
24826         }
24827         
24828         
24829         
24830         // hides menus... - so this cant be on a menu...
24831         Roo.menu.MenuMgr.hideAll();
24832
24833         //this.editorsyncValue();
24834     },
24835    
24836     
24837     createFontOptions : function(){
24838         var buf = [], fs = this.fontFamilies, ff, lc;
24839         for(var i = 0, len = fs.length; i< len; i++){
24840             ff = fs[i];
24841             lc = ff.toLowerCase();
24842             buf.push(
24843                 '<option value="',lc,'" style="font-family:',ff,';"',
24844                     (this.defaultFont == lc ? ' selected="true">' : '>'),
24845                     ff,
24846                 '</option>'
24847             );
24848         }
24849         return buf.join('');
24850     },
24851     
24852     toggleSourceEdit : function(sourceEditMode){
24853         if(sourceEditMode === undefined){
24854             sourceEditMode = !this.sourceEditMode;
24855         }
24856         this.sourceEditMode = sourceEditMode === true;
24857         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
24858         // just toggle the button?
24859         if(btn.pressed !== this.editor.sourceEditMode){
24860             btn.toggle(this.editor.sourceEditMode);
24861             return;
24862         }
24863         
24864         if(this.sourceEditMode){
24865             this.tb.items.each(function(item){
24866                 if(item.cmd != 'sourceedit'){
24867                     item.disable();
24868                 }
24869             });
24870           
24871         }else{
24872             if(this.initialized){
24873                 this.tb.items.each(function(item){
24874                     item.enable();
24875                 });
24876             }
24877             
24878         }
24879         // tell the editor that it's been pressed..
24880         this.editor.toggleSourceEdit(sourceEditMode);
24881        
24882     },
24883      /**
24884      * Object collection of toolbar tooltips for the buttons in the editor. The key
24885      * is the command id associated with that button and the value is a valid QuickTips object.
24886      * For example:
24887 <pre><code>
24888 {
24889     bold : {
24890         title: 'Bold (Ctrl+B)',
24891         text: 'Make the selected text bold.',
24892         cls: 'x-html-editor-tip'
24893     },
24894     italic : {
24895         title: 'Italic (Ctrl+I)',
24896         text: 'Make the selected text italic.',
24897         cls: 'x-html-editor-tip'
24898     },
24899     ...
24900 </code></pre>
24901     * @type Object
24902      */
24903     buttonTips : {
24904         bold : {
24905             title: 'Bold (Ctrl+B)',
24906             text: 'Make the selected text bold.',
24907             cls: 'x-html-editor-tip'
24908         },
24909         italic : {
24910             title: 'Italic (Ctrl+I)',
24911             text: 'Make the selected text italic.',
24912             cls: 'x-html-editor-tip'
24913         },
24914         underline : {
24915             title: 'Underline (Ctrl+U)',
24916             text: 'Underline the selected text.',
24917             cls: 'x-html-editor-tip'
24918         },
24919         increasefontsize : {
24920             title: 'Grow Text',
24921             text: 'Increase the font size.',
24922             cls: 'x-html-editor-tip'
24923         },
24924         decreasefontsize : {
24925             title: 'Shrink Text',
24926             text: 'Decrease the font size.',
24927             cls: 'x-html-editor-tip'
24928         },
24929         backcolor : {
24930             title: 'Text Highlight Color',
24931             text: 'Change the background color of the selected text.',
24932             cls: 'x-html-editor-tip'
24933         },
24934         forecolor : {
24935             title: 'Font Color',
24936             text: 'Change the color of the selected text.',
24937             cls: 'x-html-editor-tip'
24938         },
24939         justifyleft : {
24940             title: 'Align Text Left',
24941             text: 'Align text to the left.',
24942             cls: 'x-html-editor-tip'
24943         },
24944         justifycenter : {
24945             title: 'Center Text',
24946             text: 'Center text in the editor.',
24947             cls: 'x-html-editor-tip'
24948         },
24949         justifyright : {
24950             title: 'Align Text Right',
24951             text: 'Align text to the right.',
24952             cls: 'x-html-editor-tip'
24953         },
24954         insertunorderedlist : {
24955             title: 'Bullet List',
24956             text: 'Start a bulleted list.',
24957             cls: 'x-html-editor-tip'
24958         },
24959         insertorderedlist : {
24960             title: 'Numbered List',
24961             text: 'Start a numbered list.',
24962             cls: 'x-html-editor-tip'
24963         },
24964         createlink : {
24965             title: 'Hyperlink',
24966             text: 'Make the selected text a hyperlink.',
24967             cls: 'x-html-editor-tip'
24968         },
24969         sourceedit : {
24970             title: 'Source Edit',
24971             text: 'Switch to source editing mode.',
24972             cls: 'x-html-editor-tip'
24973         }
24974     },
24975     // private
24976     onDestroy : function(){
24977         if(this.rendered){
24978             
24979             this.tb.items.each(function(item){
24980                 if(item.menu){
24981                     item.menu.removeAll();
24982                     if(item.menu.el){
24983                         item.menu.el.destroy();
24984                     }
24985                 }
24986                 item.destroy();
24987             });
24988              
24989         }
24990     },
24991     onFirstFocus: function() {
24992         this.tb.items.each(function(item){
24993            item.enable();
24994         });
24995     }
24996 });
24997
24998
24999
25000
25001 // <script type="text/javascript">
25002 /*
25003  * Based on
25004  * Ext JS Library 1.1.1
25005  * Copyright(c) 2006-2007, Ext JS, LLC.
25006  *  
25007  
25008  */
25009
25010  
25011 /**
25012  * @class Roo.form.HtmlEditor.ToolbarContext
25013  * Context Toolbar
25014  * 
25015  * Usage:
25016  *
25017  new Roo.form.HtmlEditor({
25018     ....
25019     toolbars : [
25020         new Roo.form.HtmlEditor.ToolbarStandard(),
25021         new Roo.form.HtmlEditor.ToolbarContext()
25022         })
25023     }
25024      
25025  * 
25026  * @config : {Object} disable List of elements to disable.. (not done yet.)
25027  * 
25028  * 
25029  */
25030
25031 Roo.form.HtmlEditor.ToolbarContext = function(config)
25032 {
25033     
25034     Roo.apply(this, config);
25035     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25036     // dont call parent... till later.
25037 }
25038 Roo.form.HtmlEditor.ToolbarContext.types = {
25039     'IMG' : {
25040         width : {
25041             title: "Width",
25042             width: 40
25043         },
25044         height:  {
25045             title: "Height",
25046             width: 40
25047         },
25048         align: {
25049             title: "Align",
25050             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25051             width : 80
25052             
25053         },
25054         border: {
25055             title: "Border",
25056             width: 40
25057         },
25058         alt: {
25059             title: "Alt",
25060             width: 120
25061         },
25062         src : {
25063             title: "Src",
25064             width: 220
25065         }
25066         
25067     },
25068     'A' : {
25069         name : {
25070             title: "Name",
25071             width: 50
25072         },
25073         href:  {
25074             title: "Href",
25075             width: 220
25076         } // border?
25077         
25078     },
25079     'TABLE' : {
25080         rows : {
25081             title: "Rows",
25082             width: 20
25083         },
25084         cols : {
25085             title: "Cols",
25086             width: 20
25087         },
25088         width : {
25089             title: "Width",
25090             width: 40
25091         },
25092         height : {
25093             title: "Height",
25094             width: 40
25095         },
25096         border : {
25097             title: "Border",
25098             width: 20
25099         }
25100     },
25101     'TD' : {
25102         width : {
25103             title: "Width",
25104             width: 40
25105         },
25106         height : {
25107             title: "Height",
25108             width: 40
25109         },   
25110         align: {
25111             title: "Align",
25112             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25113             width: 40
25114         },
25115         valign: {
25116             title: "Valign",
25117             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25118             width: 40
25119         },
25120         colspan: {
25121             title: "Colspan",
25122             width: 20
25123             
25124         }
25125     },
25126     'INPUT' : {
25127         name : {
25128             title: "name",
25129             width: 120
25130         },
25131         value : {
25132             title: "Value",
25133             width: 120
25134         },
25135         width : {
25136             title: "Width",
25137             width: 40
25138         }
25139     },
25140     'LABEL' : {
25141         'for' : {
25142             title: "For",
25143             width: 120
25144         }
25145     },
25146     'TEXTAREA' : {
25147           name : {
25148             title: "name",
25149             width: 120
25150         },
25151         rows : {
25152             title: "Rows",
25153             width: 20
25154         },
25155         cols : {
25156             title: "Cols",
25157             width: 20
25158         }
25159     },
25160     'SELECT' : {
25161         name : {
25162             title: "name",
25163             width: 120
25164         },
25165         selectoptions : {
25166             title: "Options",
25167             width: 200
25168         }
25169     },
25170     'BODY' : {
25171         title : {
25172             title: "title",
25173             width: 120,
25174             disabled : true
25175         }
25176     }
25177 };
25178
25179
25180
25181 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25182     
25183     tb: false,
25184     
25185     rendered: false,
25186     
25187     editor : false,
25188     /**
25189      * @cfg {Object} disable  List of toolbar elements to disable
25190          
25191      */
25192     disable : false,
25193     
25194     
25195     
25196     toolbars : false,
25197     
25198     init : function(editor)
25199     {
25200         this.editor = editor;
25201         
25202         
25203         var fid = editor.frameId;
25204         var etb = this;
25205         function btn(id, toggle, handler){
25206             var xid = fid + '-'+ id ;
25207             return {
25208                 id : xid,
25209                 cmd : id,
25210                 cls : 'x-btn-icon x-edit-'+id,
25211                 enableToggle:toggle !== false,
25212                 scope: editor, // was editor...
25213                 handler:handler||editor.relayBtnCmd,
25214                 clickEvent:'mousedown',
25215                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25216                 tabIndex:-1
25217             };
25218         }
25219         // create a new element.
25220         var wdiv = editor.wrap.createChild({
25221                 tag: 'div'
25222             }, editor.wrap.dom.firstChild.nextSibling, true);
25223         
25224         // can we do this more than once??
25225         
25226          // stop form submits
25227       
25228  
25229         // disable everything...
25230         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25231         this.toolbars = {};
25232            
25233         for (var i in  ty) {
25234             this.toolbars[i] = this.buildToolbar(ty[i],i);
25235         }
25236         this.tb = this.toolbars.BODY;
25237         this.tb.el.show();
25238         
25239          
25240         this.rendered = true;
25241         
25242         // the all the btns;
25243         editor.on('editorevent', this.updateToolbar, this);
25244         // other toolbars need to implement this..
25245         //editor.on('editmodechange', this.updateToolbar, this);
25246     },
25247     
25248     
25249     
25250     /**
25251      * Protected method that will not generally be called directly. It triggers
25252      * a toolbar update by reading the markup state of the current selection in the editor.
25253      */
25254     updateToolbar: function(){
25255
25256         if(!this.editor.activated){
25257             this.editor.onFirstFocus();
25258             return;
25259         }
25260
25261         
25262         var ans = this.editor.getAllAncestors();
25263         
25264         // pick
25265         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25266         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25267         sel = sel ? sel : this.editor.doc.body;
25268         sel = sel.tagName.length ? sel : this.editor.doc.body;
25269         var tn = sel.tagName.toUpperCase();
25270         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25271         tn = sel.tagName.toUpperCase();
25272         if (this.tb.name  == tn) {
25273             return; // no change
25274         }
25275         this.tb.el.hide();
25276         ///console.log("show: " + tn);
25277         this.tb =  this.toolbars[tn];
25278         this.tb.el.show();
25279         this.tb.fields.each(function(e) {
25280             e.setValue(sel.getAttribute(e.name));
25281         });
25282         this.tb.selectedNode = sel;
25283         
25284         
25285         Roo.menu.MenuMgr.hideAll();
25286
25287         //this.editorsyncValue();
25288     },
25289    
25290        
25291     // private
25292     onDestroy : function(){
25293         if(this.rendered){
25294             
25295             this.tb.items.each(function(item){
25296                 if(item.menu){
25297                     item.menu.removeAll();
25298                     if(item.menu.el){
25299                         item.menu.el.destroy();
25300                     }
25301                 }
25302                 item.destroy();
25303             });
25304              
25305         }
25306     },
25307     onFirstFocus: function() {
25308         // need to do this for all the toolbars..
25309         this.tb.items.each(function(item){
25310            item.enable();
25311         });
25312     },
25313     buildToolbar: function(tlist, nm)
25314     {
25315         var editor = this.editor;
25316          // create a new element.
25317         var wdiv = editor.wrap.createChild({
25318                 tag: 'div'
25319             }, editor.wrap.dom.firstChild.nextSibling, true);
25320         
25321        
25322         var tb = new Roo.Toolbar(wdiv);
25323         tb.add(nm+ ":&nbsp;");
25324         for (var i in tlist) {
25325             var item = tlist[i];
25326             tb.add(item.title + ":&nbsp;");
25327             if (item.opts) {
25328                 // fixme
25329                 
25330               
25331                 tb.addField( new Roo.form.ComboBox({
25332                     store: new Roo.data.SimpleStore({
25333                         id : 'val',
25334                         fields: ['val'],
25335                         data : item.opts // from states.js
25336                     }),
25337                     name : i,
25338                     displayField:'val',
25339                     typeAhead: false,
25340                     mode: 'local',
25341                     editable : false,
25342                     triggerAction: 'all',
25343                     emptyText:'Select',
25344                     selectOnFocus:true,
25345                     width: item.width ? item.width  : 130,
25346                     listeners : {
25347                         'select': function(c, r, i) {
25348                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25349                         }
25350                     }
25351
25352                 }));
25353                 continue;
25354                     
25355                 
25356                 
25357                 
25358                 
25359                 tb.addField( new Roo.form.TextField({
25360                     name: i,
25361                     width: 100,
25362                     //allowBlank:false,
25363                     value: ''
25364                 }));
25365                 continue;
25366             }
25367             tb.addField( new Roo.form.TextField({
25368                 name: i,
25369                 width: item.width,
25370                 //allowBlank:true,
25371                 value: '',
25372                 listeners: {
25373                     'change' : function(f, nv, ov) {
25374                         tb.selectedNode.setAttribute(f.name, nv);
25375                     }
25376                 }
25377             }));
25378              
25379         }
25380         tb.el.on('click', function(e){
25381             e.preventDefault(); // what does this do?
25382         });
25383         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25384         tb.el.hide();
25385         tb.name = nm;
25386         // dont need to disable them... as they will get hidden
25387         return tb;
25388          
25389         
25390     }
25391     
25392     
25393     
25394     
25395 });
25396
25397
25398
25399
25400
25401 /*
25402  * Based on:
25403  * Ext JS Library 1.1.1
25404  * Copyright(c) 2006-2007, Ext JS, LLC.
25405  *
25406  * Originally Released Under LGPL - original licence link has changed is not relivant.
25407  *
25408  * Fork - LGPL
25409  * <script type="text/javascript">
25410  */
25411  
25412 /**
25413  * @class Roo.form.BasicForm
25414  * @extends Roo.util.Observable
25415  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25416  * @constructor
25417  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25418  * @param {Object} config Configuration options
25419  */
25420 Roo.form.BasicForm = function(el, config){
25421     this.allItems = [];
25422     this.childForms = [];
25423     Roo.apply(this, config);
25424     /*
25425      * The Roo.form.Field items in this form.
25426      * @type MixedCollection
25427      */
25428      
25429      
25430     this.items = new Roo.util.MixedCollection(false, function(o){
25431         return o.id || (o.id = Roo.id());
25432     });
25433     this.addEvents({
25434         /**
25435          * @event beforeaction
25436          * Fires before any action is performed. Return false to cancel the action.
25437          * @param {Form} this
25438          * @param {Action} action The action to be performed
25439          */
25440         beforeaction: true,
25441         /**
25442          * @event actionfailed
25443          * Fires when an action fails.
25444          * @param {Form} this
25445          * @param {Action} action The action that failed
25446          */
25447         actionfailed : true,
25448         /**
25449          * @event actioncomplete
25450          * Fires when an action is completed.
25451          * @param {Form} this
25452          * @param {Action} action The action that completed
25453          */
25454         actioncomplete : true
25455     });
25456     if(el){
25457         this.initEl(el);
25458     }
25459     Roo.form.BasicForm.superclass.constructor.call(this);
25460 };
25461
25462 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25463     /**
25464      * @cfg {String} method
25465      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25466      */
25467     /**
25468      * @cfg {DataReader} reader
25469      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25470      * This is optional as there is built-in support for processing JSON.
25471      */
25472     /**
25473      * @cfg {DataReader} errorReader
25474      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25475      * This is completely optional as there is built-in support for processing JSON.
25476      */
25477     /**
25478      * @cfg {String} url
25479      * The URL to use for form actions if one isn't supplied in the action options.
25480      */
25481     /**
25482      * @cfg {Boolean} fileUpload
25483      * Set to true if this form is a file upload.
25484      */
25485     /**
25486      * @cfg {Object} baseParams
25487      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25488      */
25489     /**
25490      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25491      */
25492     timeout: 30,
25493
25494     // private
25495     activeAction : null,
25496
25497     /**
25498      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25499      * or setValues() data instead of when the form was first created.
25500      */
25501     trackResetOnLoad : false,
25502     
25503     
25504     /**
25505      * childForms - used for multi-tab forms
25506      * @type {Array}
25507      */
25508     childForms : false,
25509     
25510     /**
25511      * allItems - full list of fields.
25512      * @type {Array}
25513      */
25514     allItems : false,
25515     
25516     /**
25517      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25518      * element by passing it or its id or mask the form itself by passing in true.
25519      * @type Mixed
25520      */
25521     waitMsgTarget : undefined,
25522
25523     // private
25524     initEl : function(el){
25525         this.el = Roo.get(el);
25526         this.id = this.el.id || Roo.id();
25527         this.el.on('submit', this.onSubmit, this);
25528         this.el.addClass('x-form');
25529     },
25530
25531     // private
25532     onSubmit : function(e){
25533         e.stopEvent();
25534     },
25535
25536     /**
25537      * Returns true if client-side validation on the form is successful.
25538      * @return Boolean
25539      */
25540     isValid : function(){
25541         var valid = true;
25542         this.items.each(function(f){
25543            if(!f.validate()){
25544                valid = false;
25545            }
25546         });
25547         return valid;
25548     },
25549
25550     /**
25551      * Returns true if any fields in this form have changed since their original load.
25552      * @return Boolean
25553      */
25554     isDirty : function(){
25555         var dirty = false;
25556         this.items.each(function(f){
25557            if(f.isDirty()){
25558                dirty = true;
25559                return false;
25560            }
25561         });
25562         return dirty;
25563     },
25564
25565     /**
25566      * Performs a predefined action (submit or load) or custom actions you define on this form.
25567      * @param {String} actionName The name of the action type
25568      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25569      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25570      * accept other config options):
25571      * <pre>
25572 Property          Type             Description
25573 ----------------  ---------------  ----------------------------------------------------------------------------------
25574 url               String           The url for the action (defaults to the form's url)
25575 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25576 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25577 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25578                                    validate the form on the client (defaults to false)
25579      * </pre>
25580      * @return {BasicForm} this
25581      */
25582     doAction : function(action, options){
25583         if(typeof action == 'string'){
25584             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25585         }
25586         if(this.fireEvent('beforeaction', this, action) !== false){
25587             this.beforeAction(action);
25588             action.run.defer(100, action);
25589         }
25590         return this;
25591     },
25592
25593     /**
25594      * Shortcut to do a submit action.
25595      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25596      * @return {BasicForm} this
25597      */
25598     submit : function(options){
25599         this.doAction('submit', options);
25600         return this;
25601     },
25602
25603     /**
25604      * Shortcut to do a load action.
25605      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25606      * @return {BasicForm} this
25607      */
25608     load : function(options){
25609         this.doAction('load', options);
25610         return this;
25611     },
25612
25613     /**
25614      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25615      * @param {Record} record The record to edit
25616      * @return {BasicForm} this
25617      */
25618     updateRecord : function(record){
25619         record.beginEdit();
25620         var fs = record.fields;
25621         fs.each(function(f){
25622             var field = this.findField(f.name);
25623             if(field){
25624                 record.set(f.name, field.getValue());
25625             }
25626         }, this);
25627         record.endEdit();
25628         return this;
25629     },
25630
25631     /**
25632      * Loads an Roo.data.Record into this form.
25633      * @param {Record} record The record to load
25634      * @return {BasicForm} this
25635      */
25636     loadRecord : function(record){
25637         this.setValues(record.data);
25638         return this;
25639     },
25640
25641     // private
25642     beforeAction : function(action){
25643         var o = action.options;
25644         if(o.waitMsg){
25645             if(this.waitMsgTarget === true){
25646                 this.el.mask(o.waitMsg, 'x-mask-loading');
25647             }else if(this.waitMsgTarget){
25648                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25649                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
25650             }else{
25651                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
25652             }
25653         }
25654     },
25655
25656     // private
25657     afterAction : function(action, success){
25658         this.activeAction = null;
25659         var o = action.options;
25660         if(o.waitMsg){
25661             if(this.waitMsgTarget === true){
25662                 this.el.unmask();
25663             }else if(this.waitMsgTarget){
25664                 this.waitMsgTarget.unmask();
25665             }else{
25666                 Roo.MessageBox.updateProgress(1);
25667                 Roo.MessageBox.hide();
25668             }
25669         }
25670         if(success){
25671             if(o.reset){
25672                 this.reset();
25673             }
25674             Roo.callback(o.success, o.scope, [this, action]);
25675             this.fireEvent('actioncomplete', this, action);
25676         }else{
25677             Roo.callback(o.failure, o.scope, [this, action]);
25678             this.fireEvent('actionfailed', this, action);
25679         }
25680     },
25681
25682     /**
25683      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25684      * @param {String} id The value to search for
25685      * @return Field
25686      */
25687     findField : function(id){
25688         var field = this.items.get(id);
25689         if(!field){
25690             this.items.each(function(f){
25691                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25692                     field = f;
25693                     return false;
25694                 }
25695             });
25696         }
25697         return field || null;
25698     },
25699
25700     /**
25701      * Add a secondary form to this one, 
25702      * Used to provide tabbed forms. One form is primary, with hidden values 
25703      * which mirror the elements from the other forms.
25704      * 
25705      * @param {Roo.form.Form} form to add.
25706      * 
25707      */
25708     addForm : function(form)
25709     {
25710        
25711         if (this.childForms.indexOf(form) > -1) {
25712             // already added..
25713             return;
25714         }
25715         this.childForms.push(form);
25716         Roo.each(form.allItems, function (fe) {
25717             
25718             if (this.findField(fe.name)) { // already added..
25719                 return;
25720             }
25721             var add = new Roo.form.Hidden({
25722                 name : fe.name
25723             });
25724             add.render(this.el);
25725             
25726             this.add( add );
25727         }, this);
25728         
25729     },
25730     /**
25731      * Mark fields in this form invalid in bulk.
25732      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25733      * @return {BasicForm} this
25734      */
25735     markInvalid : function(errors){
25736         if(errors instanceof Array){
25737             for(var i = 0, len = errors.length; i < len; i++){
25738                 var fieldError = errors[i];
25739                 var f = this.findField(fieldError.id);
25740                 if(f){
25741                     f.markInvalid(fieldError.msg);
25742                 }
25743             }
25744         }else{
25745             var field, id;
25746             for(id in errors){
25747                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25748                     field.markInvalid(errors[id]);
25749                 }
25750             }
25751         }
25752         Roo.each(this.childForms || [], function (f) {
25753             f.markInvalid(errors);
25754         });
25755         
25756         return this;
25757     },
25758
25759     /**
25760      * Set values for fields in this form in bulk.
25761      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25762      * @return {BasicForm} this
25763      */
25764     setValues : function(values){
25765         if(values instanceof Array){ // array of objects
25766             for(var i = 0, len = values.length; i < len; i++){
25767                 var v = values[i];
25768                 var f = this.findField(v.id);
25769                 if(f){
25770                     f.setValue(v.value);
25771                     if(this.trackResetOnLoad){
25772                         f.originalValue = f.getValue();
25773                     }
25774                 }
25775             }
25776         }else{ // object hash
25777             var field, id;
25778             for(id in values){
25779                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25780                     
25781                     if (field.setFromData && 
25782                         field.valueField && 
25783                         field.displayField &&
25784                         // combos' with local stores can 
25785                         // be queried via setValue()
25786                         // to set their value..
25787                         (field.store && !field.store.isLocal)
25788                         ) {
25789                         // it's a combo
25790                         var sd = { };
25791                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25792                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25793                         field.setFromData(sd);
25794                         
25795                     } else {
25796                         field.setValue(values[id]);
25797                     }
25798                     
25799                     
25800                     if(this.trackResetOnLoad){
25801                         field.originalValue = field.getValue();
25802                     }
25803                 }
25804             }
25805         }
25806          
25807         Roo.each(this.childForms || [], function (f) {
25808             f.setValues(values);
25809         });
25810                 
25811         return this;
25812     },
25813
25814     /**
25815      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25816      * they are returned as an array.
25817      * @param {Boolean} asString
25818      * @return {Object}
25819      */
25820     getValues : function(asString){
25821         if (this.childForms) {
25822             // copy values from the child forms
25823             Roo.each(this.childForms, function (f) {
25824                 this.setValues(f.getValues());
25825             }, this);
25826         }
25827         
25828         
25829         
25830         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25831         if(asString === true){
25832             return fs;
25833         }
25834         return Roo.urlDecode(fs);
25835     },
25836
25837     /**
25838      * Clears all invalid messages in this form.
25839      * @return {BasicForm} this
25840      */
25841     clearInvalid : function(){
25842         this.items.each(function(f){
25843            f.clearInvalid();
25844         });
25845         
25846         Roo.each(this.childForms || [], function (f) {
25847             f.clearInvalid();
25848         });
25849         
25850         
25851         return this;
25852     },
25853
25854     /**
25855      * Resets this form.
25856      * @return {BasicForm} this
25857      */
25858     reset : function(){
25859         this.items.each(function(f){
25860             f.reset();
25861         });
25862         
25863         Roo.each(this.childForms || [], function (f) {
25864             f.reset();
25865         });
25866        
25867         
25868         return this;
25869     },
25870
25871     /**
25872      * Add Roo.form components to this form.
25873      * @param {Field} field1
25874      * @param {Field} field2 (optional)
25875      * @param {Field} etc (optional)
25876      * @return {BasicForm} this
25877      */
25878     add : function(){
25879         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25880         return this;
25881     },
25882
25883
25884     /**
25885      * Removes a field from the items collection (does NOT remove its markup).
25886      * @param {Field} field
25887      * @return {BasicForm} this
25888      */
25889     remove : function(field){
25890         this.items.remove(field);
25891         return this;
25892     },
25893
25894     /**
25895      * Looks at the fields in this form, checks them for an id attribute,
25896      * and calls applyTo on the existing dom element with that id.
25897      * @return {BasicForm} this
25898      */
25899     render : function(){
25900         this.items.each(function(f){
25901             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25902                 f.applyTo(f.id);
25903             }
25904         });
25905         return this;
25906     },
25907
25908     /**
25909      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25910      * @param {Object} values
25911      * @return {BasicForm} this
25912      */
25913     applyToFields : function(o){
25914         this.items.each(function(f){
25915            Roo.apply(f, o);
25916         });
25917         return this;
25918     },
25919
25920     /**
25921      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25922      * @param {Object} values
25923      * @return {BasicForm} this
25924      */
25925     applyIfToFields : function(o){
25926         this.items.each(function(f){
25927            Roo.applyIf(f, o);
25928         });
25929         return this;
25930     }
25931 });
25932
25933 // back compat
25934 Roo.BasicForm = Roo.form.BasicForm;/*
25935  * Based on:
25936  * Ext JS Library 1.1.1
25937  * Copyright(c) 2006-2007, Ext JS, LLC.
25938  *
25939  * Originally Released Under LGPL - original licence link has changed is not relivant.
25940  *
25941  * Fork - LGPL
25942  * <script type="text/javascript">
25943  */
25944
25945 /**
25946  * @class Roo.form.Form
25947  * @extends Roo.form.BasicForm
25948  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25949  * @constructor
25950  * @param {Object} config Configuration options
25951  */
25952 Roo.form.Form = function(config){
25953     var xitems =  [];
25954     if (config.items) {
25955         xitems = config.items;
25956         delete config.items;
25957     }
25958    
25959     
25960     Roo.form.Form.superclass.constructor.call(this, null, config);
25961     this.url = this.url || this.action;
25962     if(!this.root){
25963         this.root = new Roo.form.Layout(Roo.applyIf({
25964             id: Roo.id()
25965         }, config));
25966     }
25967     this.active = this.root;
25968     /**
25969      * Array of all the buttons that have been added to this form via {@link addButton}
25970      * @type Array
25971      */
25972     this.buttons = [];
25973     this.allItems = [];
25974     this.addEvents({
25975         /**
25976          * @event clientvalidation
25977          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25978          * @param {Form} this
25979          * @param {Boolean} valid true if the form has passed client-side validation
25980          */
25981         clientvalidation: true,
25982         /**
25983          * @event rendered
25984          * Fires when the form is rendered
25985          * @param {Roo.form.Form} form
25986          */
25987         rendered : true
25988     });
25989     
25990     Roo.each(xitems, this.addxtype, this);
25991     
25992     
25993     
25994 };
25995
25996 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25997     /**
25998      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25999      */
26000     /**
26001      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26002      */
26003     /**
26004      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26005      */
26006     buttonAlign:'center',
26007
26008     /**
26009      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26010      */
26011     minButtonWidth:75,
26012
26013     /**
26014      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26015      * This property cascades to child containers if not set.
26016      */
26017     labelAlign:'left',
26018
26019     /**
26020      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26021      * fires a looping event with that state. This is required to bind buttons to the valid
26022      * state using the config value formBind:true on the button.
26023      */
26024     monitorValid : false,
26025
26026     /**
26027      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26028      */
26029     monitorPoll : 200,
26030
26031   
26032     /**
26033      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26034      * fields are added and the column is closed. If no fields are passed the column remains open
26035      * until end() is called.
26036      * @param {Object} config The config to pass to the column
26037      * @param {Field} field1 (optional)
26038      * @param {Field} field2 (optional)
26039      * @param {Field} etc (optional)
26040      * @return Column The column container object
26041      */
26042     column : function(c){
26043         var col = new Roo.form.Column(c);
26044         this.start(col);
26045         if(arguments.length > 1){ // duplicate code required because of Opera
26046             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26047             this.end();
26048         }
26049         return col;
26050     },
26051
26052     /**
26053      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26054      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26055      * until end() is called.
26056      * @param {Object} config The config to pass to the fieldset
26057      * @param {Field} field1 (optional)
26058      * @param {Field} field2 (optional)
26059      * @param {Field} etc (optional)
26060      * @return FieldSet The fieldset container object
26061      */
26062     fieldset : function(c){
26063         var fs = new Roo.form.FieldSet(c);
26064         this.start(fs);
26065         if(arguments.length > 1){ // duplicate code required because of Opera
26066             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26067             this.end();
26068         }
26069         return fs;
26070     },
26071
26072     /**
26073      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26074      * fields are added and the container is closed. If no fields are passed the container remains open
26075      * until end() is called.
26076      * @param {Object} config The config to pass to the Layout
26077      * @param {Field} field1 (optional)
26078      * @param {Field} field2 (optional)
26079      * @param {Field} etc (optional)
26080      * @return Layout The container object
26081      */
26082     container : function(c){
26083         var l = new Roo.form.Layout(c);
26084         this.start(l);
26085         if(arguments.length > 1){ // duplicate code required because of Opera
26086             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26087             this.end();
26088         }
26089         return l;
26090     },
26091
26092     /**
26093      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26094      * @param {Object} container A Roo.form.Layout or subclass of Layout
26095      * @return {Form} this
26096      */
26097     start : function(c){
26098         // cascade label info
26099         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26100         this.active.stack.push(c);
26101         c.ownerCt = this.active;
26102         this.active = c;
26103         return this;
26104     },
26105
26106     /**
26107      * Closes the current open container
26108      * @return {Form} this
26109      */
26110     end : function(){
26111         if(this.active == this.root){
26112             return this;
26113         }
26114         this.active = this.active.ownerCt;
26115         return this;
26116     },
26117
26118     /**
26119      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26120      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26121      * as the label of the field.
26122      * @param {Field} field1
26123      * @param {Field} field2 (optional)
26124      * @param {Field} etc. (optional)
26125      * @return {Form} this
26126      */
26127     add : function(){
26128         this.active.stack.push.apply(this.active.stack, arguments);
26129         this.allItems.push.apply(this.allItems,arguments);
26130         var r = [];
26131         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26132             if(a[i].isFormField){
26133                 r.push(a[i]);
26134             }
26135         }
26136         if(r.length > 0){
26137             Roo.form.Form.superclass.add.apply(this, r);
26138         }
26139         return this;
26140     },
26141     
26142
26143     
26144     
26145     
26146      /**
26147      * Find any element that has been added to a form, using it's ID or name
26148      * This can include framesets, columns etc. along with regular fields..
26149      * @param {String} id - id or name to find.
26150      
26151      * @return {Element} e - or false if nothing found.
26152      */
26153     findbyId : function(id)
26154     {
26155         var ret = false;
26156         if (!id) {
26157             return ret;
26158         }
26159         Ext.each(this.allItems, function(f){
26160             if (f.id == id || f.name == id ){
26161                 ret = f;
26162                 return false;
26163             }
26164         });
26165         return ret;
26166     },
26167
26168     
26169     
26170     /**
26171      * Render this form into the passed container. This should only be called once!
26172      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26173      * @return {Form} this
26174      */
26175     render : function(ct){
26176         ct = Roo.get(ct);
26177         var o = this.autoCreate || {
26178             tag: 'form',
26179             method : this.method || 'POST',
26180             id : this.id || Roo.id()
26181         };
26182         this.initEl(ct.createChild(o));
26183
26184         this.root.render(this.el);
26185
26186         this.items.each(function(f){
26187             f.render('x-form-el-'+f.id);
26188         });
26189
26190         if(this.buttons.length > 0){
26191             // tables are required to maintain order and for correct IE layout
26192             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26193                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26194                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26195             }}, null, true);
26196             var tr = tb.getElementsByTagName('tr')[0];
26197             for(var i = 0, len = this.buttons.length; i < len; i++) {
26198                 var b = this.buttons[i];
26199                 var td = document.createElement('td');
26200                 td.className = 'x-form-btn-td';
26201                 b.render(tr.appendChild(td));
26202             }
26203         }
26204         if(this.monitorValid){ // initialize after render
26205             this.startMonitoring();
26206         }
26207         this.fireEvent('rendered', this);
26208         return this;
26209     },
26210
26211     /**
26212      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26213      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26214      * object or a valid Roo.DomHelper element config
26215      * @param {Function} handler The function called when the button is clicked
26216      * @param {Object} scope (optional) The scope of the handler function
26217      * @return {Roo.Button}
26218      */
26219     addButton : function(config, handler, scope){
26220         var bc = {
26221             handler: handler,
26222             scope: scope,
26223             minWidth: this.minButtonWidth,
26224             hideParent:true
26225         };
26226         if(typeof config == "string"){
26227             bc.text = config;
26228         }else{
26229             Roo.apply(bc, config);
26230         }
26231         var btn = new Roo.Button(null, bc);
26232         this.buttons.push(btn);
26233         return btn;
26234     },
26235
26236      /**
26237      * Adds a series of form elements (using the xtype property as the factory method.
26238      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26239      * @param {Object} config 
26240      */
26241     
26242     addxtype : function()
26243     {
26244         var ar = Array.prototype.slice.call(arguments, 0);
26245         var ret = false;
26246         for(var i = 0; i < ar.length; i++) {
26247             if (!ar[i]) {
26248                 continue; // skip -- if this happends something invalid got sent, we 
26249                 // should ignore it, as basically that interface element will not show up
26250                 // and that should be pretty obvious!!
26251             }
26252             
26253             if (Roo.form[ar[i].xtype]) {
26254                 ar[i].form = this;
26255                 var fe = Roo.factory(ar[i], Roo.form);
26256                 if (!ret) {
26257                     ret = fe;
26258                 }
26259                 fe.form = this;
26260                 if (fe.store) {
26261                     fe.store.form = this;
26262                 }
26263                 if (fe.isLayout) {  
26264                          
26265                     this.start(fe);
26266                     this.allItems.push(fe);
26267                     if (fe.items && fe.addxtype) {
26268                         fe.addxtype.apply(fe, fe.items);
26269                         delete fe.items;
26270                     }
26271                      this.end();
26272                     continue;
26273                 }
26274                 
26275                 
26276                  
26277                 this.add(fe);
26278               //  console.log('adding ' + ar[i].xtype);
26279             }
26280             if (ar[i].xtype == 'Button') {  
26281                 //console.log('adding button');
26282                 //console.log(ar[i]);
26283                 this.addButton(ar[i]);
26284                 this.allItems.push(fe);
26285                 continue;
26286             }
26287             
26288             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26289                 alert('end is not supported on xtype any more, use items');
26290             //    this.end();
26291             //    //console.log('adding end');
26292             }
26293             
26294         }
26295         return ret;
26296     },
26297     
26298     /**
26299      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26300      * option "monitorValid"
26301      */
26302     startMonitoring : function(){
26303         if(!this.bound){
26304             this.bound = true;
26305             Roo.TaskMgr.start({
26306                 run : this.bindHandler,
26307                 interval : this.monitorPoll || 200,
26308                 scope: this
26309             });
26310         }
26311     },
26312
26313     /**
26314      * Stops monitoring of the valid state of this form
26315      */
26316     stopMonitoring : function(){
26317         this.bound = false;
26318     },
26319
26320     // private
26321     bindHandler : function(){
26322         if(!this.bound){
26323             return false; // stops binding
26324         }
26325         var valid = true;
26326         this.items.each(function(f){
26327             if(!f.isValid(true)){
26328                 valid = false;
26329                 return false;
26330             }
26331         });
26332         for(var i = 0, len = this.buttons.length; i < len; i++){
26333             var btn = this.buttons[i];
26334             if(btn.formBind === true && btn.disabled === valid){
26335                 btn.setDisabled(!valid);
26336             }
26337         }
26338         this.fireEvent('clientvalidation', this, valid);
26339     }
26340     
26341     
26342     
26343     
26344     
26345     
26346     
26347     
26348 });
26349
26350
26351 // back compat
26352 Roo.Form = Roo.form.Form;
26353 /*
26354  * Based on:
26355  * Ext JS Library 1.1.1
26356  * Copyright(c) 2006-2007, Ext JS, LLC.
26357  *
26358  * Originally Released Under LGPL - original licence link has changed is not relivant.
26359  *
26360  * Fork - LGPL
26361  * <script type="text/javascript">
26362  */
26363  
26364  /**
26365  * @class Roo.form.Action
26366  * Internal Class used to handle form actions
26367  * @constructor
26368  * @param {Roo.form.BasicForm} el The form element or its id
26369  * @param {Object} config Configuration options
26370  */
26371  
26372  
26373 // define the action interface
26374 Roo.form.Action = function(form, options){
26375     this.form = form;
26376     this.options = options || {};
26377 };
26378 /**
26379  * Client Validation Failed
26380  * @const 
26381  */
26382 Roo.form.Action.CLIENT_INVALID = 'client';
26383 /**
26384  * Server Validation Failed
26385  * @const 
26386  */
26387  Roo.form.Action.SERVER_INVALID = 'server';
26388  /**
26389  * Connect to Server Failed
26390  * @const 
26391  */
26392 Roo.form.Action.CONNECT_FAILURE = 'connect';
26393 /**
26394  * Reading Data from Server Failed
26395  * @const 
26396  */
26397 Roo.form.Action.LOAD_FAILURE = 'load';
26398
26399 Roo.form.Action.prototype = {
26400     type : 'default',
26401     failureType : undefined,
26402     response : undefined,
26403     result : undefined,
26404
26405     // interface method
26406     run : function(options){
26407
26408     },
26409
26410     // interface method
26411     success : function(response){
26412
26413     },
26414
26415     // interface method
26416     handleResponse : function(response){
26417
26418     },
26419
26420     // default connection failure
26421     failure : function(response){
26422         this.response = response;
26423         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26424         this.form.afterAction(this, false);
26425     },
26426
26427     processResponse : function(response){
26428         this.response = response;
26429         if(!response.responseText){
26430             return true;
26431         }
26432         this.result = this.handleResponse(response);
26433         return this.result;
26434     },
26435
26436     // utility functions used internally
26437     getUrl : function(appendParams){
26438         var url = this.options.url || this.form.url || this.form.el.dom.action;
26439         if(appendParams){
26440             var p = this.getParams();
26441             if(p){
26442                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26443             }
26444         }
26445         return url;
26446     },
26447
26448     getMethod : function(){
26449         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26450     },
26451
26452     getParams : function(){
26453         var bp = this.form.baseParams;
26454         var p = this.options.params;
26455         if(p){
26456             if(typeof p == "object"){
26457                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26458             }else if(typeof p == 'string' && bp){
26459                 p += '&' + Roo.urlEncode(bp);
26460             }
26461         }else if(bp){
26462             p = Roo.urlEncode(bp);
26463         }
26464         return p;
26465     },
26466
26467     createCallback : function(){
26468         return {
26469             success: this.success,
26470             failure: this.failure,
26471             scope: this,
26472             timeout: (this.form.timeout*1000),
26473             upload: this.form.fileUpload ? this.success : undefined
26474         };
26475     }
26476 };
26477
26478 Roo.form.Action.Submit = function(form, options){
26479     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26480 };
26481
26482 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26483     type : 'submit',
26484
26485     run : function()
26486     {
26487         // run get Values on the form, so it syncs any secondary forms.
26488         this.form.getValues();
26489         
26490         var o = this.options;
26491         var method = this.getMethod();
26492         var isPost = method == 'POST';
26493         if(o.clientValidation === false || this.form.isValid()){
26494             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26495                 form:this.form.el.dom,
26496                 url:this.getUrl(!isPost),
26497                 method: method,
26498                 params:isPost ? this.getParams() : null,
26499                 isUpload: this.form.fileUpload
26500             }));
26501
26502         }else if (o.clientValidation !== false){ // client validation failed
26503             this.failureType = Roo.form.Action.CLIENT_INVALID;
26504             this.form.afterAction(this, false);
26505         }
26506     },
26507
26508     success : function(response){
26509         var result = this.processResponse(response);
26510         if(result === true || result.success){
26511             this.form.afterAction(this, true);
26512             return;
26513         }
26514         if(result.errors){
26515             this.form.markInvalid(result.errors);
26516             this.failureType = Roo.form.Action.SERVER_INVALID;
26517         }
26518         this.form.afterAction(this, false);
26519     },
26520
26521     handleResponse : function(response){
26522         if(this.form.errorReader){
26523             var rs = this.form.errorReader.read(response);
26524             var errors = [];
26525             if(rs.records){
26526                 for(var i = 0, len = rs.records.length; i < len; i++) {
26527                     var r = rs.records[i];
26528                     errors[i] = r.data;
26529                 }
26530             }
26531             if(errors.length < 1){
26532                 errors = null;
26533             }
26534             return {
26535                 success : rs.success,
26536                 errors : errors
26537             };
26538         }
26539         var ret = false;
26540         try {
26541             ret = Roo.decode(response.responseText);
26542         } catch (e) {
26543             ret = {
26544                 success: false,
26545                 errorMsg: "Failed to read server message: " + response.responseText,
26546                 errors : []
26547             };
26548         }
26549         return ret;
26550         
26551     }
26552 });
26553
26554
26555 Roo.form.Action.Load = function(form, options){
26556     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26557     this.reader = this.form.reader;
26558 };
26559
26560 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26561     type : 'load',
26562
26563     run : function(){
26564         Roo.Ajax.request(Roo.apply(
26565                 this.createCallback(), {
26566                     method:this.getMethod(),
26567                     url:this.getUrl(false),
26568                     params:this.getParams()
26569         }));
26570     },
26571
26572     success : function(response){
26573         var result = this.processResponse(response);
26574         if(result === true || !result.success || !result.data){
26575             this.failureType = Roo.form.Action.LOAD_FAILURE;
26576             this.form.afterAction(this, false);
26577             return;
26578         }
26579         this.form.clearInvalid();
26580         this.form.setValues(result.data);
26581         this.form.afterAction(this, true);
26582     },
26583
26584     handleResponse : function(response){
26585         if(this.form.reader){
26586             var rs = this.form.reader.read(response);
26587             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26588             return {
26589                 success : rs.success,
26590                 data : data
26591             };
26592         }
26593         return Roo.decode(response.responseText);
26594     }
26595 });
26596
26597 Roo.form.Action.ACTION_TYPES = {
26598     'load' : Roo.form.Action.Load,
26599     'submit' : Roo.form.Action.Submit
26600 };/*
26601  * Based on:
26602  * Ext JS Library 1.1.1
26603  * Copyright(c) 2006-2007, Ext JS, LLC.
26604  *
26605  * Originally Released Under LGPL - original licence link has changed is not relivant.
26606  *
26607  * Fork - LGPL
26608  * <script type="text/javascript">
26609  */
26610  
26611 /**
26612  * @class Roo.form.Layout
26613  * @extends Roo.Component
26614  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26615  * @constructor
26616  * @param {Object} config Configuration options
26617  */
26618 Roo.form.Layout = function(config){
26619     var xitems = [];
26620     if (config.items) {
26621         xitems = config.items;
26622         delete config.items;
26623     }
26624     Roo.form.Layout.superclass.constructor.call(this, config);
26625     this.stack = [];
26626     Roo.each(xitems, this.addxtype, this);
26627      
26628 };
26629
26630 Roo.extend(Roo.form.Layout, Roo.Component, {
26631     /**
26632      * @cfg {String/Object} autoCreate
26633      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26634      */
26635     /**
26636      * @cfg {String/Object/Function} style
26637      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26638      * a function which returns such a specification.
26639      */
26640     /**
26641      * @cfg {String} labelAlign
26642      * Valid values are "left," "top" and "right" (defaults to "left")
26643      */
26644     /**
26645      * @cfg {Number} labelWidth
26646      * Fixed width in pixels of all field labels (defaults to undefined)
26647      */
26648     /**
26649      * @cfg {Boolean} clear
26650      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26651      */
26652     clear : true,
26653     /**
26654      * @cfg {String} labelSeparator
26655      * The separator to use after field labels (defaults to ':')
26656      */
26657     labelSeparator : ':',
26658     /**
26659      * @cfg {Boolean} hideLabels
26660      * True to suppress the display of field labels in this layout (defaults to false)
26661      */
26662     hideLabels : false,
26663
26664     // private
26665     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26666     
26667     isLayout : true,
26668     
26669     // private
26670     onRender : function(ct, position){
26671         if(this.el){ // from markup
26672             this.el = Roo.get(this.el);
26673         }else {  // generate
26674             var cfg = this.getAutoCreate();
26675             this.el = ct.createChild(cfg, position);
26676         }
26677         if(this.style){
26678             this.el.applyStyles(this.style);
26679         }
26680         if(this.labelAlign){
26681             this.el.addClass('x-form-label-'+this.labelAlign);
26682         }
26683         if(this.hideLabels){
26684             this.labelStyle = "display:none";
26685             this.elementStyle = "padding-left:0;";
26686         }else{
26687             if(typeof this.labelWidth == 'number'){
26688                 this.labelStyle = "width:"+this.labelWidth+"px;";
26689                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26690             }
26691             if(this.labelAlign == 'top'){
26692                 this.labelStyle = "width:auto;";
26693                 this.elementStyle = "padding-left:0;";
26694             }
26695         }
26696         var stack = this.stack;
26697         var slen = stack.length;
26698         if(slen > 0){
26699             if(!this.fieldTpl){
26700                 var t = new Roo.Template(
26701                     '<div class="x-form-item {5}">',
26702                         '<label for="{0}" style="{2}">{1}{4}</label>',
26703                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26704                         '</div>',
26705                     '</div><div class="x-form-clear-left"></div>'
26706                 );
26707                 t.disableFormats = true;
26708                 t.compile();
26709                 Roo.form.Layout.prototype.fieldTpl = t;
26710             }
26711             for(var i = 0; i < slen; i++) {
26712                 if(stack[i].isFormField){
26713                     this.renderField(stack[i]);
26714                 }else{
26715                     this.renderComponent(stack[i]);
26716                 }
26717             }
26718         }
26719         if(this.clear){
26720             this.el.createChild({cls:'x-form-clear'});
26721         }
26722     },
26723
26724     // private
26725     renderField : function(f){
26726         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26727                f.id, //0
26728                f.fieldLabel, //1
26729                f.labelStyle||this.labelStyle||'', //2
26730                this.elementStyle||'', //3
26731                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26732                f.itemCls||this.itemCls||''  //5
26733        ], true).getPrevSibling());
26734     },
26735
26736     // private
26737     renderComponent : function(c){
26738         c.render(c.isLayout ? this.el : this.el.createChild());    
26739     },
26740     /**
26741      * Adds a object form elements (using the xtype property as the factory method.)
26742      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26743      * @param {Object} config 
26744      */
26745     addxtype : function(o)
26746     {
26747         // create the lement.
26748         o.form = this.form;
26749         var fe = Roo.factory(o, Roo.form);
26750         this.form.allItems.push(fe);
26751         this.stack.push(fe);
26752         
26753         if (fe.isFormField) {
26754             this.form.items.add(fe);
26755         }
26756          
26757         return fe;
26758     }
26759 });
26760
26761 /**
26762  * @class Roo.form.Column
26763  * @extends Roo.form.Layout
26764  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26765  * @constructor
26766  * @param {Object} config Configuration options
26767  */
26768 Roo.form.Column = function(config){
26769     Roo.form.Column.superclass.constructor.call(this, config);
26770 };
26771
26772 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26773     /**
26774      * @cfg {Number/String} width
26775      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26776      */
26777     /**
26778      * @cfg {String/Object} autoCreate
26779      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26780      */
26781
26782     // private
26783     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26784
26785     // private
26786     onRender : function(ct, position){
26787         Roo.form.Column.superclass.onRender.call(this, ct, position);
26788         if(this.width){
26789             this.el.setWidth(this.width);
26790         }
26791     }
26792 });
26793
26794
26795 /**
26796  * @class Roo.form.Row
26797  * @extends Roo.form.Layout
26798  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26799  * @constructor
26800  * @param {Object} config Configuration options
26801  */
26802
26803  
26804 Roo.form.Row = function(config){
26805     Roo.form.Row.superclass.constructor.call(this, config);
26806 };
26807  
26808 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26809       /**
26810      * @cfg {Number/String} width
26811      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26812      */
26813     /**
26814      * @cfg {Number/String} height
26815      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26816      */
26817     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26818     
26819     padWidth : 20,
26820     // private
26821     onRender : function(ct, position){
26822         //console.log('row render');
26823         if(!this.rowTpl){
26824             var t = new Roo.Template(
26825                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26826                     '<label for="{0}" style="{2}">{1}{4}</label>',
26827                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26828                     '</div>',
26829                 '</div>'
26830             );
26831             t.disableFormats = true;
26832             t.compile();
26833             Roo.form.Layout.prototype.rowTpl = t;
26834         }
26835         this.fieldTpl = this.rowTpl;
26836         
26837         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26838         var labelWidth = 100;
26839         
26840         if ((this.labelAlign != 'top')) {
26841             if (typeof this.labelWidth == 'number') {
26842                 labelWidth = this.labelWidth
26843             }
26844             this.padWidth =  20 + labelWidth;
26845             
26846         }
26847         
26848         Roo.form.Column.superclass.onRender.call(this, ct, position);
26849         if(this.width){
26850             this.el.setWidth(this.width);
26851         }
26852         if(this.height){
26853             this.el.setHeight(this.height);
26854         }
26855     },
26856     
26857     // private
26858     renderField : function(f){
26859         f.fieldEl = this.fieldTpl.append(this.el, [
26860                f.id, f.fieldLabel,
26861                f.labelStyle||this.labelStyle||'',
26862                this.elementStyle||'',
26863                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26864                f.itemCls||this.itemCls||'',
26865                f.width ? f.width + this.padWidth : 160 + this.padWidth
26866        ],true);
26867     }
26868 });
26869  
26870
26871 /**
26872  * @class Roo.form.FieldSet
26873  * @extends Roo.form.Layout
26874  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26875  * @constructor
26876  * @param {Object} config Configuration options
26877  */
26878 Roo.form.FieldSet = function(config){
26879     Roo.form.FieldSet.superclass.constructor.call(this, config);
26880 };
26881
26882 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26883     /**
26884      * @cfg {String} legend
26885      * The text to display as the legend for the FieldSet (defaults to '')
26886      */
26887     /**
26888      * @cfg {String/Object} autoCreate
26889      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26890      */
26891
26892     // private
26893     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26894
26895     // private
26896     onRender : function(ct, position){
26897         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26898         if(this.legend){
26899             this.setLegend(this.legend);
26900         }
26901     },
26902
26903     // private
26904     setLegend : function(text){
26905         if(this.rendered){
26906             this.el.child('legend').update(text);
26907         }
26908     }
26909 });/*
26910  * Based on:
26911  * Ext JS Library 1.1.1
26912  * Copyright(c) 2006-2007, Ext JS, LLC.
26913  *
26914  * Originally Released Under LGPL - original licence link has changed is not relivant.
26915  *
26916  * Fork - LGPL
26917  * <script type="text/javascript">
26918  */
26919 /**
26920  * @class Roo.form.VTypes
26921  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26922  * @singleton
26923  */
26924 Roo.form.VTypes = function(){
26925     // closure these in so they are only created once.
26926     var alpha = /^[a-zA-Z_]+$/;
26927     var alphanum = /^[a-zA-Z0-9_]+$/;
26928     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
26929     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26930
26931     // All these messages and functions are configurable
26932     return {
26933         /**
26934          * The function used to validate email addresses
26935          * @param {String} value The email address
26936          */
26937         'email' : function(v){
26938             return email.test(v);
26939         },
26940         /**
26941          * The error text to display when the email validation function returns false
26942          * @type String
26943          */
26944         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26945         /**
26946          * The keystroke filter mask to be applied on email input
26947          * @type RegExp
26948          */
26949         'emailMask' : /[a-z0-9_\.\-@]/i,
26950
26951         /**
26952          * The function used to validate URLs
26953          * @param {String} value The URL
26954          */
26955         'url' : function(v){
26956             return url.test(v);
26957         },
26958         /**
26959          * The error text to display when the url validation function returns false
26960          * @type String
26961          */
26962         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26963         
26964         /**
26965          * The function used to validate alpha values
26966          * @param {String} value The value
26967          */
26968         'alpha' : function(v){
26969             return alpha.test(v);
26970         },
26971         /**
26972          * The error text to display when the alpha validation function returns false
26973          * @type String
26974          */
26975         'alphaText' : 'This field should only contain letters and _',
26976         /**
26977          * The keystroke filter mask to be applied on alpha input
26978          * @type RegExp
26979          */
26980         'alphaMask' : /[a-z_]/i,
26981
26982         /**
26983          * The function used to validate alphanumeric values
26984          * @param {String} value The value
26985          */
26986         'alphanum' : function(v){
26987             return alphanum.test(v);
26988         },
26989         /**
26990          * The error text to display when the alphanumeric validation function returns false
26991          * @type String
26992          */
26993         'alphanumText' : 'This field should only contain letters, numbers and _',
26994         /**
26995          * The keystroke filter mask to be applied on alphanumeric input
26996          * @type RegExp
26997          */
26998         'alphanumMask' : /[a-z0-9_]/i
26999     };
27000 }();//<script type="text/javascript">
27001
27002 /**
27003  * @class Roo.form.FCKeditor
27004  * @extends Roo.form.TextArea
27005  * Wrapper around the FCKEditor http://www.fckeditor.net
27006  * @constructor
27007  * Creates a new FCKeditor
27008  * @param {Object} config Configuration options
27009  */
27010 Roo.form.FCKeditor = function(config){
27011     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27012     this.addEvents({
27013          /**
27014          * @event editorinit
27015          * Fired when the editor is initialized - you can add extra handlers here..
27016          * @param {FCKeditor} this
27017          * @param {Object} the FCK object.
27018          */
27019         editorinit : true
27020     });
27021     
27022     
27023 };
27024 Roo.form.FCKeditor.editors = { };
27025 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27026 {
27027     //defaultAutoCreate : {
27028     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27029     //},
27030     // private
27031     /**
27032      * @cfg {Object} fck options - see fck manual for details.
27033      */
27034     fckconfig : false,
27035     
27036     /**
27037      * @cfg {Object} fck toolbar set (Basic or Default)
27038      */
27039     toolbarSet : 'Basic',
27040     /**
27041      * @cfg {Object} fck BasePath
27042      */ 
27043     basePath : '/fckeditor/',
27044     
27045     
27046     frame : false,
27047     
27048     value : '',
27049     
27050    
27051     onRender : function(ct, position)
27052     {
27053         if(!this.el){
27054             this.defaultAutoCreate = {
27055                 tag: "textarea",
27056                 style:"width:300px;height:60px;",
27057                 autocomplete: "off"
27058             };
27059         }
27060         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27061         /*
27062         if(this.grow){
27063             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27064             if(this.preventScrollbars){
27065                 this.el.setStyle("overflow", "hidden");
27066             }
27067             this.el.setHeight(this.growMin);
27068         }
27069         */
27070         //console.log('onrender' + this.getId() );
27071         Roo.form.FCKeditor.editors[this.getId()] = this;
27072          
27073
27074         this.replaceTextarea() ;
27075         
27076     },
27077     
27078     getEditor : function() {
27079         return this.fckEditor;
27080     },
27081     /**
27082      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27083      * @param {Mixed} value The value to set
27084      */
27085     
27086     
27087     setValue : function(value)
27088     {
27089         //console.log('setValue: ' + value);
27090         
27091         if(typeof(value) == 'undefined') { // not sure why this is happending...
27092             return;
27093         }
27094         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27095         
27096         //if(!this.el || !this.getEditor()) {
27097         //    this.value = value;
27098             //this.setValue.defer(100,this,[value]);    
27099         //    return;
27100         //} 
27101         
27102         if(!this.getEditor()) {
27103             return;
27104         }
27105         
27106         this.getEditor().SetData(value);
27107         
27108         //
27109
27110     },
27111
27112     /**
27113      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27114      * @return {Mixed} value The field value
27115      */
27116     getValue : function()
27117     {
27118         
27119         if (this.frame && this.frame.dom.style.display == 'none') {
27120             return Roo.form.FCKeditor.superclass.getValue.call(this);
27121         }
27122         
27123         if(!this.el || !this.getEditor()) {
27124            
27125            // this.getValue.defer(100,this); 
27126             return this.value;
27127         }
27128        
27129         
27130         var value=this.getEditor().GetData();
27131         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27132         return Roo.form.FCKeditor.superclass.getValue.call(this);
27133         
27134
27135     },
27136
27137     /**
27138      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27139      * @return {Mixed} value The field value
27140      */
27141     getRawValue : function()
27142     {
27143         if (this.frame && this.frame.dom.style.display == 'none') {
27144             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27145         }
27146         
27147         if(!this.el || !this.getEditor()) {
27148             //this.getRawValue.defer(100,this); 
27149             return this.value;
27150             return;
27151         }
27152         
27153         
27154         
27155         var value=this.getEditor().GetData();
27156         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27157         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27158          
27159     },
27160     
27161     setSize : function(w,h) {
27162         
27163         
27164         
27165         //if (this.frame && this.frame.dom.style.display == 'none') {
27166         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27167         //    return;
27168         //}
27169         //if(!this.el || !this.getEditor()) {
27170         //    this.setSize.defer(100,this, [w,h]); 
27171         //    return;
27172         //}
27173         
27174         
27175         
27176         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27177         
27178         this.frame.dom.setAttribute('width', w);
27179         this.frame.dom.setAttribute('height', h);
27180         this.frame.setSize(w,h);
27181         
27182     },
27183     
27184     toggleSourceEdit : function(value) {
27185         
27186       
27187          
27188         this.el.dom.style.display = value ? '' : 'none';
27189         this.frame.dom.style.display = value ?  'none' : '';
27190         
27191     },
27192     
27193     
27194     focus: function(tag)
27195     {
27196         if (this.frame.dom.style.display == 'none') {
27197             return Roo.form.FCKeditor.superclass.focus.call(this);
27198         }
27199         if(!this.el || !this.getEditor()) {
27200             this.focus.defer(100,this, [tag]); 
27201             return;
27202         }
27203         
27204         
27205         
27206         
27207         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27208         this.getEditor().Focus();
27209         if (tgs.length) {
27210             if (!this.getEditor().Selection.GetSelection()) {
27211                 this.focus.defer(100,this, [tag]); 
27212                 return;
27213             }
27214             
27215             
27216             var r = this.getEditor().EditorDocument.createRange();
27217             r.setStart(tgs[0],0);
27218             r.setEnd(tgs[0],0);
27219             this.getEditor().Selection.GetSelection().removeAllRanges();
27220             this.getEditor().Selection.GetSelection().addRange(r);
27221             this.getEditor().Focus();
27222         }
27223         
27224     },
27225     
27226     
27227     
27228     replaceTextarea : function()
27229     {
27230         if ( document.getElementById( this.getId() + '___Frame' ) )
27231             return ;
27232         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27233         //{
27234             // We must check the elements firstly using the Id and then the name.
27235         var oTextarea = document.getElementById( this.getId() );
27236         
27237         var colElementsByName = document.getElementsByName( this.getId() ) ;
27238          
27239         oTextarea.style.display = 'none' ;
27240
27241         if ( oTextarea.tabIndex ) {            
27242             this.TabIndex = oTextarea.tabIndex ;
27243         }
27244         
27245         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27246         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27247         this.frame = Roo.get(this.getId() + '___Frame')
27248     },
27249     
27250     _getConfigHtml : function()
27251     {
27252         var sConfig = '' ;
27253
27254         for ( var o in this.fckconfig ) {
27255             sConfig += sConfig.length > 0  ? '&amp;' : '';
27256             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27257         }
27258
27259         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27260     },
27261     
27262     
27263     _getIFrameHtml : function()
27264     {
27265         var sFile = 'fckeditor.html' ;
27266         /* no idea what this is about..
27267         try
27268         {
27269             if ( (/fcksource=true/i).test( window.top.location.search ) )
27270                 sFile = 'fckeditor.original.html' ;
27271         }
27272         catch (e) { 
27273         */
27274
27275         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27276         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27277         
27278         
27279         var html = '<iframe id="' + this.getId() +
27280             '___Frame" src="' + sLink +
27281             '" width="' + this.width +
27282             '" height="' + this.height + '"' +
27283             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27284             ' frameborder="0" scrolling="no"></iframe>' ;
27285
27286         return html ;
27287     },
27288     
27289     _insertHtmlBefore : function( html, element )
27290     {
27291         if ( element.insertAdjacentHTML )       {
27292             // IE
27293             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27294         } else { // Gecko
27295             var oRange = document.createRange() ;
27296             oRange.setStartBefore( element ) ;
27297             var oFragment = oRange.createContextualFragment( html );
27298             element.parentNode.insertBefore( oFragment, element ) ;
27299         }
27300     }
27301     
27302     
27303   
27304     
27305     
27306     
27307     
27308
27309 });
27310
27311 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27312
27313 function FCKeditor_OnComplete(editorInstance){
27314     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27315     f.fckEditor = editorInstance;
27316     //console.log("loaded");
27317     f.fireEvent('editorinit', f, editorInstance);
27318
27319   
27320
27321  
27322
27323
27324
27325
27326
27327
27328
27329
27330
27331
27332
27333
27334
27335
27336
27337 //<script type="text/javascript">
27338 /**
27339  * @class Roo.form.GridField
27340  * @extends Roo.form.Field
27341  * Embed a grid (or editable grid into a form)
27342  * STATUS ALPHA
27343  * @constructor
27344  * Creates a new GridField
27345  * @param {Object} config Configuration options
27346  */
27347 Roo.form.GridField = function(config){
27348     Roo.form.GridField.superclass.constructor.call(this, config);
27349      
27350 };
27351
27352 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27353     /**
27354      * @cfg {Number} width  - used to restrict width of grid..
27355      */
27356     width : 100,
27357     /**
27358      * @cfg {Number} height - used to restrict height of grid..
27359      */
27360     height : 50,
27361      /**
27362      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
27363      */
27364     xgrid : false, 
27365     /**
27366      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27367      * {tag: "input", type: "checkbox", autocomplete: "off"})
27368      */
27369    // defaultAutoCreate : { tag: 'div' },
27370     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27371     /**
27372      * @cfg {String} addTitle Text to include for adding a title.
27373      */
27374     addTitle : false,
27375     //
27376     onResize : function(){
27377         Roo.form.Field.superclass.onResize.apply(this, arguments);
27378     },
27379
27380     initEvents : function(){
27381         // Roo.form.Checkbox.superclass.initEvents.call(this);
27382         // has no events...
27383        
27384     },
27385
27386
27387     getResizeEl : function(){
27388         return this.wrap;
27389     },
27390
27391     getPositionEl : function(){
27392         return this.wrap;
27393     },
27394
27395     // private
27396     onRender : function(ct, position){
27397         
27398         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27399         var style = this.style;
27400         delete this.style;
27401         
27402         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
27403         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27404         this.viewEl = this.wrap.createChild({ tag: 'div' });
27405         if (style) {
27406             this.viewEl.applyStyles(style);
27407         }
27408         if (this.width) {
27409             this.viewEl.setWidth(this.width);
27410         }
27411         if (this.height) {
27412             this.viewEl.setHeight(this.height);
27413         }
27414         //if(this.inputValue !== undefined){
27415         //this.setValue(this.value);
27416         
27417         
27418         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27419         
27420         
27421         this.grid.render();
27422         this.grid.getDataSource().on('remove', this.refreshValue, this);
27423         this.grid.getDataSource().on('update', this.refreshValue, this);
27424         this.grid.on('afteredit', this.refreshValue, this);
27425  
27426     },
27427      
27428     
27429     /**
27430      * Sets the value of the item. 
27431      * @param {String} either an object  or a string..
27432      */
27433     setValue : function(v){
27434         //this.value = v;
27435         v = v || []; // empty set..
27436         // this does not seem smart - it really only affects memoryproxy grids..
27437         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27438             var ds = this.grid.getDataSource();
27439             // assumes a json reader..
27440             var data = {}
27441             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27442             ds.loadData( data);
27443         }
27444         Roo.form.GridField.superclass.setValue.call(this, v);
27445         this.refreshValue();
27446         // should load data in the grid really....
27447     },
27448     
27449     // private
27450     refreshValue: function() {
27451          var val = [];
27452         this.grid.getDataSource().each(function(r) {
27453             val.push(r.data);
27454         });
27455         this.el.dom.value = Roo.encode(val);
27456     }
27457     
27458      
27459     
27460     
27461 });/*
27462  * Based on:
27463  * Ext JS Library 1.1.1
27464  * Copyright(c) 2006-2007, Ext JS, LLC.
27465  *
27466  * Originally Released Under LGPL - original licence link has changed is not relivant.
27467  *
27468  * Fork - LGPL
27469  * <script type="text/javascript">
27470  */
27471 /**
27472  * @class Roo.form.DisplayField
27473  * @extends Roo.form.Field
27474  * A generic Field to display non-editable data.
27475  * @constructor
27476  * Creates a new Display Field item.
27477  * @param {Object} config Configuration options
27478  */
27479 Roo.form.DisplayField = function(config){
27480     Roo.form.DisplayField.superclass.constructor.call(this, config);
27481     
27482 };
27483
27484 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27485     inputType:      'hidden',
27486     allowBlank:     true,
27487     readOnly:         true,
27488     
27489  
27490     /**
27491      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27492      */
27493     focusClass : undefined,
27494     /**
27495      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27496      */
27497     fieldClass: 'x-form-field',
27498     
27499      /**
27500      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27501      */
27502     valueRenderer: undefined,
27503     
27504     width: 100,
27505     /**
27506      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27507      * {tag: "input", type: "checkbox", autocomplete: "off"})
27508      */
27509      
27510  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27511
27512     onResize : function(){
27513         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27514         
27515     },
27516
27517     initEvents : function(){
27518         // Roo.form.Checkbox.superclass.initEvents.call(this);
27519         // has no events...
27520        
27521     },
27522
27523
27524     getResizeEl : function(){
27525         return this.wrap;
27526     },
27527
27528     getPositionEl : function(){
27529         return this.wrap;
27530     },
27531
27532     // private
27533     onRender : function(ct, position){
27534         
27535         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27536         //if(this.inputValue !== undefined){
27537         this.wrap = this.el.wrap();
27538         
27539         this.viewEl = this.wrap.createChild({ tag: 'div'});
27540         
27541         if (this.bodyStyle) {
27542             this.viewEl.applyStyles(this.bodyStyle);
27543         }
27544         //this.viewEl.setStyle('padding', '2px');
27545         
27546         this.setValue(this.value);
27547         
27548     },
27549 /*
27550     // private
27551     initValue : Roo.emptyFn,
27552
27553   */
27554
27555         // private
27556     onClick : function(){
27557         
27558     },
27559
27560     /**
27561      * Sets the checked state of the checkbox.
27562      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27563      */
27564     setValue : function(v){
27565         this.value = v;
27566         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27567         // this might be called before we have a dom element..
27568         if (!this.viewEl) {
27569             return;
27570         }
27571         this.viewEl.dom.innerHTML = html;
27572         Roo.form.DisplayField.superclass.setValue.call(this, v);
27573
27574     }
27575 });//<script type="text/javasscript">
27576  
27577
27578 /**
27579  * @class Roo.DDView
27580  * A DnD enabled version of Roo.View.
27581  * @param {Element/String} container The Element in which to create the View.
27582  * @param {String} tpl The template string used to create the markup for each element of the View
27583  * @param {Object} config The configuration properties. These include all the config options of
27584  * {@link Roo.View} plus some specific to this class.<br>
27585  * <p>
27586  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
27587  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
27588  * <p>
27589  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
27590 .x-view-drag-insert-above {
27591         border-top:1px dotted #3366cc;
27592 }
27593 .x-view-drag-insert-below {
27594         border-bottom:1px dotted #3366cc;
27595 }
27596 </code></pre>
27597  * 
27598  */
27599  
27600 Roo.DDView = function(container, tpl, config) {
27601     Roo.DDView.superclass.constructor.apply(this, arguments);
27602     this.getEl().setStyle("outline", "0px none");
27603     this.getEl().unselectable();
27604     if (this.dragGroup) {
27605                 this.setDraggable(this.dragGroup.split(","));
27606     }
27607     if (this.dropGroup) {
27608                 this.setDroppable(this.dropGroup.split(","));
27609     }
27610     if (this.deletable) {
27611         this.setDeletable();
27612     }
27613     this.isDirtyFlag = false;
27614         this.addEvents({
27615                 "drop" : true
27616         });
27617 };
27618
27619 Roo.extend(Roo.DDView, Roo.View, {
27620 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
27621 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
27622 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
27623 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
27624
27625         isFormField: true,
27626
27627         reset: Roo.emptyFn,
27628         
27629         clearInvalid: Roo.form.Field.prototype.clearInvalid,
27630
27631         validate: function() {
27632                 return true;
27633         },
27634         
27635         destroy: function() {
27636                 this.purgeListeners();
27637                 this.getEl.removeAllListeners();
27638                 this.getEl().remove();
27639                 if (this.dragZone) {
27640                         if (this.dragZone.destroy) {
27641                                 this.dragZone.destroy();
27642                         }
27643                 }
27644                 if (this.dropZone) {
27645                         if (this.dropZone.destroy) {
27646                                 this.dropZone.destroy();
27647                         }
27648                 }
27649         },
27650
27651 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
27652         getName: function() {
27653                 return this.name;
27654         },
27655
27656 /**     Loads the View from a JSON string representing the Records to put into the Store. */
27657         setValue: function(v) {
27658                 if (!this.store) {
27659                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
27660                 }
27661                 var data = {};
27662                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
27663                 this.store.proxy = new Roo.data.MemoryProxy(data);
27664                 this.store.load();
27665         },
27666
27667 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
27668         getValue: function() {
27669                 var result = '(';
27670                 this.store.each(function(rec) {
27671                         result += rec.id + ',';
27672                 });
27673                 return result.substr(0, result.length - 1) + ')';
27674         },
27675         
27676         getIds: function() {
27677                 var i = 0, result = new Array(this.store.getCount());
27678                 this.store.each(function(rec) {
27679                         result[i++] = rec.id;
27680                 });
27681                 return result;
27682         },
27683         
27684         isDirty: function() {
27685                 return this.isDirtyFlag;
27686         },
27687
27688 /**
27689  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
27690  *      whole Element becomes the target, and this causes the drop gesture to append.
27691  */
27692     getTargetFromEvent : function(e) {
27693                 var target = e.getTarget();
27694                 while ((target !== null) && (target.parentNode != this.el.dom)) {
27695                 target = target.parentNode;
27696                 }
27697                 if (!target) {
27698                         target = this.el.dom.lastChild || this.el.dom;
27699                 }
27700                 return target;
27701     },
27702
27703 /**
27704  *      Create the drag data which consists of an object which has the property "ddel" as
27705  *      the drag proxy element. 
27706  */
27707     getDragData : function(e) {
27708         var target = this.findItemFromChild(e.getTarget());
27709                 if(target) {
27710                         this.handleSelection(e);
27711                         var selNodes = this.getSelectedNodes();
27712             var dragData = {
27713                 source: this,
27714                 copy: this.copy || (this.allowCopy && e.ctrlKey),
27715                 nodes: selNodes,
27716                 records: []
27717                         };
27718                         var selectedIndices = this.getSelectedIndexes();
27719                         for (var i = 0; i < selectedIndices.length; i++) {
27720                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
27721                         }
27722                         if (selNodes.length == 1) {
27723                                 dragData.ddel = target.cloneNode(true); // the div element
27724                         } else {
27725                                 var div = document.createElement('div'); // create the multi element drag "ghost"
27726                                 div.className = 'multi-proxy';
27727                                 for (var i = 0, len = selNodes.length; i < len; i++) {
27728                                         div.appendChild(selNodes[i].cloneNode(true));
27729                                 }
27730                                 dragData.ddel = div;
27731                         }
27732             //console.log(dragData)
27733             //console.log(dragData.ddel.innerHTML)
27734                         return dragData;
27735                 }
27736         //console.log('nodragData')
27737                 return false;
27738     },
27739     
27740 /**     Specify to which ddGroup items in this DDView may be dragged. */
27741     setDraggable: function(ddGroup) {
27742         if (ddGroup instanceof Array) {
27743                 Roo.each(ddGroup, this.setDraggable, this);
27744                 return;
27745         }
27746         if (this.dragZone) {
27747                 this.dragZone.addToGroup(ddGroup);
27748         } else {
27749                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
27750                                 containerScroll: true,
27751                                 ddGroup: ddGroup 
27752
27753                         });
27754 //                      Draggability implies selection. DragZone's mousedown selects the element.
27755                         if (!this.multiSelect) { this.singleSelect = true; }
27756
27757 //                      Wire the DragZone's handlers up to methods in *this*
27758                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
27759                 }
27760     },
27761
27762 /**     Specify from which ddGroup this DDView accepts drops. */
27763     setDroppable: function(ddGroup) {
27764         if (ddGroup instanceof Array) {
27765                 Roo.each(ddGroup, this.setDroppable, this);
27766                 return;
27767         }
27768         if (this.dropZone) {
27769                 this.dropZone.addToGroup(ddGroup);
27770         } else {
27771                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
27772                                 containerScroll: true,
27773                                 ddGroup: ddGroup
27774                         });
27775
27776 //                      Wire the DropZone's handlers up to methods in *this*
27777                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
27778                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
27779                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
27780                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
27781                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
27782                 }
27783     },
27784
27785 /**     Decide whether to drop above or below a View node. */
27786     getDropPoint : function(e, n, dd){
27787         if (n == this.el.dom) { return "above"; }
27788                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
27789                 var c = t + (b - t) / 2;
27790                 var y = Roo.lib.Event.getPageY(e);
27791                 if(y <= c) {
27792                         return "above";
27793                 }else{
27794                         return "below";
27795                 }
27796     },
27797
27798     onNodeEnter : function(n, dd, e, data){
27799                 return false;
27800     },
27801     
27802     onNodeOver : function(n, dd, e, data){
27803                 var pt = this.getDropPoint(e, n, dd);
27804                 // set the insert point style on the target node
27805                 var dragElClass = this.dropNotAllowed;
27806                 if (pt) {
27807                         var targetElClass;
27808                         if (pt == "above"){
27809                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
27810                                 targetElClass = "x-view-drag-insert-above";
27811                         } else {
27812                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
27813                                 targetElClass = "x-view-drag-insert-below";
27814                         }
27815                         if (this.lastInsertClass != targetElClass){
27816                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
27817                                 this.lastInsertClass = targetElClass;
27818                         }
27819                 }
27820                 return dragElClass;
27821         },
27822
27823     onNodeOut : function(n, dd, e, data){
27824                 this.removeDropIndicators(n);
27825     },
27826
27827     onNodeDrop : function(n, dd, e, data){
27828         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
27829                 return false;
27830         }
27831         var pt = this.getDropPoint(e, n, dd);
27832                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
27833                 if (pt == "below") { insertAt++; }
27834                 for (var i = 0; i < data.records.length; i++) {
27835                         var r = data.records[i];
27836                         var dup = this.store.getById(r.id);
27837                         if (dup && (dd != this.dragZone)) {
27838                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
27839                         } else {
27840                                 if (data.copy) {
27841                                         this.store.insert(insertAt++, r.copy());
27842                                 } else {
27843                                         data.source.isDirtyFlag = true;
27844                                         r.store.remove(r);
27845                                         this.store.insert(insertAt++, r);
27846                                 }
27847                                 this.isDirtyFlag = true;
27848                         }
27849                 }
27850                 this.dragZone.cachedTarget = null;
27851                 return true;
27852     },
27853
27854     removeDropIndicators : function(n){
27855                 if(n){
27856                         Roo.fly(n).removeClass([
27857                                 "x-view-drag-insert-above",
27858                                 "x-view-drag-insert-below"]);
27859                         this.lastInsertClass = "_noclass";
27860                 }
27861     },
27862
27863 /**
27864  *      Utility method. Add a delete option to the DDView's context menu.
27865  *      @param {String} imageUrl The URL of the "delete" icon image.
27866  */
27867         setDeletable: function(imageUrl) {
27868                 if (!this.singleSelect && !this.multiSelect) {
27869                         this.singleSelect = true;
27870                 }
27871                 var c = this.getContextMenu();
27872                 this.contextMenu.on("itemclick", function(item) {
27873                         switch (item.id) {
27874                                 case "delete":
27875                                         this.remove(this.getSelectedIndexes());
27876                                         break;
27877                         }
27878                 }, this);
27879                 this.contextMenu.add({
27880                         icon: imageUrl,
27881                         id: "delete",
27882                         text: 'Delete'
27883                 });
27884         },
27885         
27886 /**     Return the context menu for this DDView. */
27887         getContextMenu: function() {
27888                 if (!this.contextMenu) {
27889 //                      Create the View's context menu
27890                         this.contextMenu = new Roo.menu.Menu({
27891                                 id: this.id + "-contextmenu"
27892                         });
27893                         this.el.on("contextmenu", this.showContextMenu, this);
27894                 }
27895                 return this.contextMenu;
27896         },
27897         
27898         disableContextMenu: function() {
27899                 if (this.contextMenu) {
27900                         this.el.un("contextmenu", this.showContextMenu, this);
27901                 }
27902         },
27903
27904         showContextMenu: function(e, item) {
27905         item = this.findItemFromChild(e.getTarget());
27906                 if (item) {
27907                         e.stopEvent();
27908                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
27909                         this.contextMenu.showAt(e.getXY());
27910             }
27911     },
27912
27913 /**
27914  *      Remove {@link Roo.data.Record}s at the specified indices.
27915  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
27916  */
27917     remove: function(selectedIndices) {
27918                 selectedIndices = [].concat(selectedIndices);
27919                 for (var i = 0; i < selectedIndices.length; i++) {
27920                         var rec = this.store.getAt(selectedIndices[i]);
27921                         this.store.remove(rec);
27922                 }
27923     },
27924
27925 /**
27926  *      Double click fires the event, but also, if this is draggable, and there is only one other
27927  *      related DropZone, it transfers the selected node.
27928  */
27929     onDblClick : function(e){
27930         var item = this.findItemFromChild(e.getTarget());
27931         if(item){
27932             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
27933                 return false;
27934             }
27935             if (this.dragGroup) {
27936                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
27937                     while (targets.indexOf(this.dropZone) > -1) {
27938                             targets.remove(this.dropZone);
27939                                 }
27940                     if (targets.length == 1) {
27941                                         this.dragZone.cachedTarget = null;
27942                         var el = Roo.get(targets[0].getEl());
27943                         var box = el.getBox(true);
27944                         targets[0].onNodeDrop(el.dom, {
27945                                 target: el.dom,
27946                                 xy: [box.x, box.y + box.height - 1]
27947                         }, null, this.getDragData(e));
27948                     }
27949                 }
27950         }
27951     },
27952     
27953     handleSelection: function(e) {
27954                 this.dragZone.cachedTarget = null;
27955         var item = this.findItemFromChild(e.getTarget());
27956         if (!item) {
27957                 this.clearSelections(true);
27958                 return;
27959         }
27960                 if (item && (this.multiSelect || this.singleSelect)){
27961                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
27962                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
27963                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
27964                                 this.unselect(item);
27965                         } else {
27966                                 this.select(item, this.multiSelect && e.ctrlKey);
27967                                 this.lastSelection = item;
27968                         }
27969                 }
27970     },
27971
27972     onItemClick : function(item, index, e){
27973                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
27974                         return false;
27975                 }
27976                 return true;
27977     },
27978
27979     unselect : function(nodeInfo, suppressEvent){
27980                 var node = this.getNode(nodeInfo);
27981                 if(node && this.isSelected(node)){
27982                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27983                                 Roo.fly(node).removeClass(this.selectedClass);
27984                                 this.selections.remove(node);
27985                                 if(!suppressEvent){
27986                                         this.fireEvent("selectionchange", this, this.selections);
27987                                 }
27988                         }
27989                 }
27990     }
27991 });
27992 /*
27993  * Based on:
27994  * Ext JS Library 1.1.1
27995  * Copyright(c) 2006-2007, Ext JS, LLC.
27996  *
27997  * Originally Released Under LGPL - original licence link has changed is not relivant.
27998  *
27999  * Fork - LGPL
28000  * <script type="text/javascript">
28001  */
28002  
28003 /**
28004  * @class Roo.LayoutManager
28005  * @extends Roo.util.Observable
28006  * Base class for layout managers.
28007  */
28008 Roo.LayoutManager = function(container, config){
28009     Roo.LayoutManager.superclass.constructor.call(this);
28010     this.el = Roo.get(container);
28011     // ie scrollbar fix
28012     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28013         document.body.scroll = "no";
28014     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28015         this.el.position('relative');
28016     }
28017     this.id = this.el.id;
28018     this.el.addClass("x-layout-container");
28019     /** false to disable window resize monitoring @type Boolean */
28020     this.monitorWindowResize = true;
28021     this.regions = {};
28022     this.addEvents({
28023         /**
28024          * @event layout
28025          * Fires when a layout is performed. 
28026          * @param {Roo.LayoutManager} this
28027          */
28028         "layout" : true,
28029         /**
28030          * @event regionresized
28031          * Fires when the user resizes a region. 
28032          * @param {Roo.LayoutRegion} region The resized region
28033          * @param {Number} newSize The new size (width for east/west, height for north/south)
28034          */
28035         "regionresized" : true,
28036         /**
28037          * @event regioncollapsed
28038          * Fires when a region is collapsed. 
28039          * @param {Roo.LayoutRegion} region The collapsed region
28040          */
28041         "regioncollapsed" : true,
28042         /**
28043          * @event regionexpanded
28044          * Fires when a region is expanded.  
28045          * @param {Roo.LayoutRegion} region The expanded region
28046          */
28047         "regionexpanded" : true
28048     });
28049     this.updating = false;
28050     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28051 };
28052
28053 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28054     /**
28055      * Returns true if this layout is currently being updated
28056      * @return {Boolean}
28057      */
28058     isUpdating : function(){
28059         return this.updating; 
28060     },
28061     
28062     /**
28063      * Suspend the LayoutManager from doing auto-layouts while
28064      * making multiple add or remove calls
28065      */
28066     beginUpdate : function(){
28067         this.updating = true;    
28068     },
28069     
28070     /**
28071      * Restore auto-layouts and optionally disable the manager from performing a layout
28072      * @param {Boolean} noLayout true to disable a layout update 
28073      */
28074     endUpdate : function(noLayout){
28075         this.updating = false;
28076         if(!noLayout){
28077             this.layout();
28078         }    
28079     },
28080     
28081     layout: function(){
28082         
28083     },
28084     
28085     onRegionResized : function(region, newSize){
28086         this.fireEvent("regionresized", region, newSize);
28087         this.layout();
28088     },
28089     
28090     onRegionCollapsed : function(region){
28091         this.fireEvent("regioncollapsed", region);
28092     },
28093     
28094     onRegionExpanded : function(region){
28095         this.fireEvent("regionexpanded", region);
28096     },
28097         
28098     /**
28099      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28100      * performs box-model adjustments.
28101      * @return {Object} The size as an object {width: (the width), height: (the height)}
28102      */
28103     getViewSize : function(){
28104         var size;
28105         if(this.el.dom != document.body){
28106             size = this.el.getSize();
28107         }else{
28108             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28109         }
28110         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28111         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28112         return size;
28113     },
28114     
28115     /**
28116      * Returns the Element this layout is bound to.
28117      * @return {Roo.Element}
28118      */
28119     getEl : function(){
28120         return this.el;
28121     },
28122     
28123     /**
28124      * Returns the specified region.
28125      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28126      * @return {Roo.LayoutRegion}
28127      */
28128     getRegion : function(target){
28129         return this.regions[target.toLowerCase()];
28130     },
28131     
28132     onWindowResize : function(){
28133         if(this.monitorWindowResize){
28134             this.layout();
28135         }
28136     }
28137 });/*
28138  * Based on:
28139  * Ext JS Library 1.1.1
28140  * Copyright(c) 2006-2007, Ext JS, LLC.
28141  *
28142  * Originally Released Under LGPL - original licence link has changed is not relivant.
28143  *
28144  * Fork - LGPL
28145  * <script type="text/javascript">
28146  */
28147 /**
28148  * @class Roo.BorderLayout
28149  * @extends Roo.LayoutManager
28150  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28151  * please see: <br><br>
28152  * <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>
28153  * <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>
28154  * Example:
28155  <pre><code>
28156  var layout = new Roo.BorderLayout(document.body, {
28157     north: {
28158         initialSize: 25,
28159         titlebar: false
28160     },
28161     west: {
28162         split:true,
28163         initialSize: 200,
28164         minSize: 175,
28165         maxSize: 400,
28166         titlebar: true,
28167         collapsible: true
28168     },
28169     east: {
28170         split:true,
28171         initialSize: 202,
28172         minSize: 175,
28173         maxSize: 400,
28174         titlebar: true,
28175         collapsible: true
28176     },
28177     south: {
28178         split:true,
28179         initialSize: 100,
28180         minSize: 100,
28181         maxSize: 200,
28182         titlebar: true,
28183         collapsible: true
28184     },
28185     center: {
28186         titlebar: true,
28187         autoScroll:true,
28188         resizeTabs: true,
28189         minTabWidth: 50,
28190         preferredTabWidth: 150
28191     }
28192 });
28193
28194 // shorthand
28195 var CP = Roo.ContentPanel;
28196
28197 layout.beginUpdate();
28198 layout.add("north", new CP("north", "North"));
28199 layout.add("south", new CP("south", {title: "South", closable: true}));
28200 layout.add("west", new CP("west", {title: "West"}));
28201 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28202 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28203 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28204 layout.getRegion("center").showPanel("center1");
28205 layout.endUpdate();
28206 </code></pre>
28207
28208 <b>The container the layout is rendered into can be either the body element or any other element.
28209 If it is not the body element, the container needs to either be an absolute positioned element,
28210 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28211 the container size if it is not the body element.</b>
28212
28213 * @constructor
28214 * Create a new BorderLayout
28215 * @param {String/HTMLElement/Element} container The container this layout is bound to
28216 * @param {Object} config Configuration options
28217  */
28218 Roo.BorderLayout = function(container, config){
28219     config = config || {};
28220     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28221     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28222     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28223         var target = this.factory.validRegions[i];
28224         if(config[target]){
28225             this.addRegion(target, config[target]);
28226         }
28227     }
28228 };
28229
28230 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28231     /**
28232      * Creates and adds a new region if it doesn't already exist.
28233      * @param {String} target The target region key (north, south, east, west or center).
28234      * @param {Object} config The regions config object
28235      * @return {BorderLayoutRegion} The new region
28236      */
28237     addRegion : function(target, config){
28238         if(!this.regions[target]){
28239             var r = this.factory.create(target, this, config);
28240             this.bindRegion(target, r);
28241         }
28242         return this.regions[target];
28243     },
28244
28245     // private (kinda)
28246     bindRegion : function(name, r){
28247         this.regions[name] = r;
28248         r.on("visibilitychange", this.layout, this);
28249         r.on("paneladded", this.layout, this);
28250         r.on("panelremoved", this.layout, this);
28251         r.on("invalidated", this.layout, this);
28252         r.on("resized", this.onRegionResized, this);
28253         r.on("collapsed", this.onRegionCollapsed, this);
28254         r.on("expanded", this.onRegionExpanded, this);
28255     },
28256
28257     /**
28258      * Performs a layout update.
28259      */
28260     layout : function(){
28261         if(this.updating) return;
28262         var size = this.getViewSize();
28263         var w = size.width;
28264         var h = size.height;
28265         var centerW = w;
28266         var centerH = h;
28267         var centerY = 0;
28268         var centerX = 0;
28269         //var x = 0, y = 0;
28270
28271         var rs = this.regions;
28272         var north = rs["north"];
28273         var south = rs["south"]; 
28274         var west = rs["west"];
28275         var east = rs["east"];
28276         var center = rs["center"];
28277         //if(this.hideOnLayout){ // not supported anymore
28278             //c.el.setStyle("display", "none");
28279         //}
28280         if(north && north.isVisible()){
28281             var b = north.getBox();
28282             var m = north.getMargins();
28283             b.width = w - (m.left+m.right);
28284             b.x = m.left;
28285             b.y = m.top;
28286             centerY = b.height + b.y + m.bottom;
28287             centerH -= centerY;
28288             north.updateBox(this.safeBox(b));
28289         }
28290         if(south && south.isVisible()){
28291             var b = south.getBox();
28292             var m = south.getMargins();
28293             b.width = w - (m.left+m.right);
28294             b.x = m.left;
28295             var totalHeight = (b.height + m.top + m.bottom);
28296             b.y = h - totalHeight + m.top;
28297             centerH -= totalHeight;
28298             south.updateBox(this.safeBox(b));
28299         }
28300         if(west && west.isVisible()){
28301             var b = west.getBox();
28302             var m = west.getMargins();
28303             b.height = centerH - (m.top+m.bottom);
28304             b.x = m.left;
28305             b.y = centerY + m.top;
28306             var totalWidth = (b.width + m.left + m.right);
28307             centerX += totalWidth;
28308             centerW -= totalWidth;
28309             west.updateBox(this.safeBox(b));
28310         }
28311         if(east && east.isVisible()){
28312             var b = east.getBox();
28313             var m = east.getMargins();
28314             b.height = centerH - (m.top+m.bottom);
28315             var totalWidth = (b.width + m.left + m.right);
28316             b.x = w - totalWidth + m.left;
28317             b.y = centerY + m.top;
28318             centerW -= totalWidth;
28319             east.updateBox(this.safeBox(b));
28320         }
28321         if(center){
28322             var m = center.getMargins();
28323             var centerBox = {
28324                 x: centerX + m.left,
28325                 y: centerY + m.top,
28326                 width: centerW - (m.left+m.right),
28327                 height: centerH - (m.top+m.bottom)
28328             };
28329             //if(this.hideOnLayout){
28330                 //center.el.setStyle("display", "block");
28331             //}
28332             center.updateBox(this.safeBox(centerBox));
28333         }
28334         this.el.repaint();
28335         this.fireEvent("layout", this);
28336     },
28337
28338     // private
28339     safeBox : function(box){
28340         box.width = Math.max(0, box.width);
28341         box.height = Math.max(0, box.height);
28342         return box;
28343     },
28344
28345     /**
28346      * Adds a ContentPanel (or subclass) to this layout.
28347      * @param {String} target The target region key (north, south, east, west or center).
28348      * @param {Roo.ContentPanel} panel The panel to add
28349      * @return {Roo.ContentPanel} The added panel
28350      */
28351     add : function(target, panel){
28352          
28353         target = target.toLowerCase();
28354         return this.regions[target].add(panel);
28355     },
28356
28357     /**
28358      * Remove a ContentPanel (or subclass) to this layout.
28359      * @param {String} target The target region key (north, south, east, west or center).
28360      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28361      * @return {Roo.ContentPanel} The removed panel
28362      */
28363     remove : function(target, panel){
28364         target = target.toLowerCase();
28365         return this.regions[target].remove(panel);
28366     },
28367
28368     /**
28369      * Searches all regions for a panel with the specified id
28370      * @param {String} panelId
28371      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28372      */
28373     findPanel : function(panelId){
28374         var rs = this.regions;
28375         for(var target in rs){
28376             if(typeof rs[target] != "function"){
28377                 var p = rs[target].getPanel(panelId);
28378                 if(p){
28379                     return p;
28380                 }
28381             }
28382         }
28383         return null;
28384     },
28385
28386     /**
28387      * Searches all regions for a panel with the specified id and activates (shows) it.
28388      * @param {String/ContentPanel} panelId The panels id or the panel itself
28389      * @return {Roo.ContentPanel} The shown panel or null
28390      */
28391     showPanel : function(panelId) {
28392       var rs = this.regions;
28393       for(var target in rs){
28394          var r = rs[target];
28395          if(typeof r != "function"){
28396             if(r.hasPanel(panelId)){
28397                return r.showPanel(panelId);
28398             }
28399          }
28400       }
28401       return null;
28402    },
28403
28404    /**
28405      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28406      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28407      */
28408     restoreState : function(provider){
28409         if(!provider){
28410             provider = Roo.state.Manager;
28411         }
28412         var sm = new Roo.LayoutStateManager();
28413         sm.init(this, provider);
28414     },
28415
28416     /**
28417      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28418      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28419      * a valid ContentPanel config object.  Example:
28420      * <pre><code>
28421 // Create the main layout
28422 var layout = new Roo.BorderLayout('main-ct', {
28423     west: {
28424         split:true,
28425         minSize: 175,
28426         titlebar: true
28427     },
28428     center: {
28429         title:'Components'
28430     }
28431 }, 'main-ct');
28432
28433 // Create and add multiple ContentPanels at once via configs
28434 layout.batchAdd({
28435    west: {
28436        id: 'source-files',
28437        autoCreate:true,
28438        title:'Ext Source Files',
28439        autoScroll:true,
28440        fitToFrame:true
28441    },
28442    center : {
28443        el: cview,
28444        autoScroll:true,
28445        fitToFrame:true,
28446        toolbar: tb,
28447        resizeEl:'cbody'
28448    }
28449 });
28450 </code></pre>
28451      * @param {Object} regions An object containing ContentPanel configs by region name
28452      */
28453     batchAdd : function(regions){
28454         this.beginUpdate();
28455         for(var rname in regions){
28456             var lr = this.regions[rname];
28457             if(lr){
28458                 this.addTypedPanels(lr, regions[rname]);
28459             }
28460         }
28461         this.endUpdate();
28462     },
28463
28464     // private
28465     addTypedPanels : function(lr, ps){
28466         if(typeof ps == 'string'){
28467             lr.add(new Roo.ContentPanel(ps));
28468         }
28469         else if(ps instanceof Array){
28470             for(var i =0, len = ps.length; i < len; i++){
28471                 this.addTypedPanels(lr, ps[i]);
28472             }
28473         }
28474         else if(!ps.events){ // raw config?
28475             var el = ps.el;
28476             delete ps.el; // prevent conflict
28477             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
28478         }
28479         else {  // panel object assumed!
28480             lr.add(ps);
28481         }
28482     },
28483     /**
28484      * Adds a xtype elements to the layout.
28485      * <pre><code>
28486
28487 layout.addxtype({
28488        xtype : 'ContentPanel',
28489        region: 'west',
28490        items: [ .... ]
28491    }
28492 );
28493
28494 layout.addxtype({
28495         xtype : 'NestedLayoutPanel',
28496         region: 'west',
28497         layout: {
28498            center: { },
28499            west: { }   
28500         },
28501         items : [ ... list of content panels or nested layout panels.. ]
28502    }
28503 );
28504 </code></pre>
28505      * @param {Object} cfg Xtype definition of item to add.
28506      */
28507     addxtype : function(cfg)
28508     {
28509         // basically accepts a pannel...
28510         // can accept a layout region..!?!?
28511        // console.log('BorderLayout add ' + cfg.xtype)
28512         
28513         if (!cfg.xtype.match(/Panel$/)) {
28514             return false;
28515         }
28516         var ret = false;
28517         var region = cfg.region;
28518         delete cfg.region;
28519         
28520           
28521         var xitems = [];
28522         if (cfg.items) {
28523             xitems = cfg.items;
28524             delete cfg.items;
28525         }
28526         
28527         
28528         switch(cfg.xtype) 
28529         {
28530             case 'ContentPanel':  // ContentPanel (el, cfg)
28531                 if(cfg.autoCreate) {
28532                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28533                 } else {
28534                     var el = this.el.createChild();
28535                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
28536                 }
28537                 
28538                 this.add(region, ret);
28539                 break;
28540             
28541             
28542             case 'TreePanel': // our new panel!
28543                 cfg.el = this.el.createChild();
28544                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
28545                 this.add(region, ret);
28546                 break;
28547             
28548             case 'NestedLayoutPanel': 
28549                 // create a new Layout (which is  a Border Layout...
28550                 var el = this.el.createChild();
28551                 var clayout = cfg.layout;
28552                 delete cfg.layout;
28553                 clayout.items   = clayout.items  || [];
28554                 // replace this exitems with the clayout ones..
28555                 xitems = clayout.items;
28556                  
28557                 
28558                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
28559                     cfg.background = false;
28560                 }
28561                 var layout = new Roo.BorderLayout(el, clayout);
28562                 
28563                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
28564                 //console.log('adding nested layout panel '  + cfg.toSource());
28565                 this.add(region, ret);
28566                 
28567                 break;
28568                 
28569             case 'GridPanel': 
28570             
28571                 // needs grid and region
28572                 
28573                 //var el = this.getRegion(region).el.createChild();
28574                 var el = this.el.createChild();
28575                 // create the grid first...
28576                 
28577                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
28578                 delete cfg.grid;
28579                 if (region == 'center' && this.active ) {
28580                     cfg.background = false;
28581                 }
28582                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
28583                 
28584                 this.add(region, ret);
28585                 if (cfg.background) {
28586                     ret.on('activate', function(gp) {
28587                         if (!gp.grid.rendered) {
28588                             gp.grid.render();
28589                         }
28590                     });
28591                 } else {
28592                     grid.render();
28593                 }
28594                 break;
28595            
28596                
28597                 
28598                 
28599             default: 
28600                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
28601                 return;
28602              // GridPanel (grid, cfg)
28603             
28604         }
28605         this.beginUpdate();
28606         // add children..
28607         Roo.each(xitems, function(i)  {
28608             ret.addxtype(i);
28609         });
28610         this.endUpdate();
28611         return ret;
28612         
28613     }
28614 });
28615
28616 /**
28617  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
28618  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
28619  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
28620  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
28621  * <pre><code>
28622 // shorthand
28623 var CP = Roo.ContentPanel;
28624
28625 var layout = Roo.BorderLayout.create({
28626     north: {
28627         initialSize: 25,
28628         titlebar: false,
28629         panels: [new CP("north", "North")]
28630     },
28631     west: {
28632         split:true,
28633         initialSize: 200,
28634         minSize: 175,
28635         maxSize: 400,
28636         titlebar: true,
28637         collapsible: true,
28638         panels: [new CP("west", {title: "West"})]
28639     },
28640     east: {
28641         split:true,
28642         initialSize: 202,
28643         minSize: 175,
28644         maxSize: 400,
28645         titlebar: true,
28646         collapsible: true,
28647         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
28648     },
28649     south: {
28650         split:true,
28651         initialSize: 100,
28652         minSize: 100,
28653         maxSize: 200,
28654         titlebar: true,
28655         collapsible: true,
28656         panels: [new CP("south", {title: "South", closable: true})]
28657     },
28658     center: {
28659         titlebar: true,
28660         autoScroll:true,
28661         resizeTabs: true,
28662         minTabWidth: 50,
28663         preferredTabWidth: 150,
28664         panels: [
28665             new CP("center1", {title: "Close Me", closable: true}),
28666             new CP("center2", {title: "Center Panel", closable: false})
28667         ]
28668     }
28669 }, document.body);
28670
28671 layout.getRegion("center").showPanel("center1");
28672 </code></pre>
28673  * @param config
28674  * @param targetEl
28675  */
28676 Roo.BorderLayout.create = function(config, targetEl){
28677     var layout = new Roo.BorderLayout(targetEl || document.body, config);
28678     layout.beginUpdate();
28679     var regions = Roo.BorderLayout.RegionFactory.validRegions;
28680     for(var j = 0, jlen = regions.length; j < jlen; j++){
28681         var lr = regions[j];
28682         if(layout.regions[lr] && config[lr].panels){
28683             var r = layout.regions[lr];
28684             var ps = config[lr].panels;
28685             layout.addTypedPanels(r, ps);
28686         }
28687     }
28688     layout.endUpdate();
28689     return layout;
28690 };
28691
28692 // private
28693 Roo.BorderLayout.RegionFactory = {
28694     // private
28695     validRegions : ["north","south","east","west","center"],
28696
28697     // private
28698     create : function(target, mgr, config){
28699         target = target.toLowerCase();
28700         if(config.lightweight || config.basic){
28701             return new Roo.BasicLayoutRegion(mgr, config, target);
28702         }
28703         switch(target){
28704             case "north":
28705                 return new Roo.NorthLayoutRegion(mgr, config);
28706             case "south":
28707                 return new Roo.SouthLayoutRegion(mgr, config);
28708             case "east":
28709                 return new Roo.EastLayoutRegion(mgr, config);
28710             case "west":
28711                 return new Roo.WestLayoutRegion(mgr, config);
28712             case "center":
28713                 return new Roo.CenterLayoutRegion(mgr, config);
28714         }
28715         throw 'Layout region "'+target+'" not supported.';
28716     }
28717 };/*
28718  * Based on:
28719  * Ext JS Library 1.1.1
28720  * Copyright(c) 2006-2007, Ext JS, LLC.
28721  *
28722  * Originally Released Under LGPL - original licence link has changed is not relivant.
28723  *
28724  * Fork - LGPL
28725  * <script type="text/javascript">
28726  */
28727  
28728 /**
28729  * @class Roo.BasicLayoutRegion
28730  * @extends Roo.util.Observable
28731  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
28732  * and does not have a titlebar, tabs or any other features. All it does is size and position 
28733  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
28734  */
28735 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
28736     this.mgr = mgr;
28737     this.position  = pos;
28738     this.events = {
28739         /**
28740          * @scope Roo.BasicLayoutRegion
28741          */
28742         
28743         /**
28744          * @event beforeremove
28745          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
28746          * @param {Roo.LayoutRegion} this
28747          * @param {Roo.ContentPanel} panel The panel
28748          * @param {Object} e The cancel event object
28749          */
28750         "beforeremove" : true,
28751         /**
28752          * @event invalidated
28753          * Fires when the layout for this region is changed.
28754          * @param {Roo.LayoutRegion} this
28755          */
28756         "invalidated" : true,
28757         /**
28758          * @event visibilitychange
28759          * Fires when this region is shown or hidden 
28760          * @param {Roo.LayoutRegion} this
28761          * @param {Boolean} visibility true or false
28762          */
28763         "visibilitychange" : true,
28764         /**
28765          * @event paneladded
28766          * Fires when a panel is added. 
28767          * @param {Roo.LayoutRegion} this
28768          * @param {Roo.ContentPanel} panel The panel
28769          */
28770         "paneladded" : true,
28771         /**
28772          * @event panelremoved
28773          * Fires when a panel is removed. 
28774          * @param {Roo.LayoutRegion} this
28775          * @param {Roo.ContentPanel} panel The panel
28776          */
28777         "panelremoved" : true,
28778         /**
28779          * @event collapsed
28780          * Fires when this region is collapsed.
28781          * @param {Roo.LayoutRegion} this
28782          */
28783         "collapsed" : true,
28784         /**
28785          * @event expanded
28786          * Fires when this region is expanded.
28787          * @param {Roo.LayoutRegion} this
28788          */
28789         "expanded" : true,
28790         /**
28791          * @event slideshow
28792          * Fires when this region is slid into view.
28793          * @param {Roo.LayoutRegion} this
28794          */
28795         "slideshow" : true,
28796         /**
28797          * @event slidehide
28798          * Fires when this region slides out of view. 
28799          * @param {Roo.LayoutRegion} this
28800          */
28801         "slidehide" : true,
28802         /**
28803          * @event panelactivated
28804          * Fires when a panel is activated. 
28805          * @param {Roo.LayoutRegion} this
28806          * @param {Roo.ContentPanel} panel The activated panel
28807          */
28808         "panelactivated" : true,
28809         /**
28810          * @event resized
28811          * Fires when the user resizes this region. 
28812          * @param {Roo.LayoutRegion} this
28813          * @param {Number} newSize The new size (width for east/west, height for north/south)
28814          */
28815         "resized" : true
28816     };
28817     /** A collection of panels in this region. @type Roo.util.MixedCollection */
28818     this.panels = new Roo.util.MixedCollection();
28819     this.panels.getKey = this.getPanelId.createDelegate(this);
28820     this.box = null;
28821     this.activePanel = null;
28822     // ensure listeners are added...
28823     
28824     if (config.listeners || config.events) {
28825         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
28826             listeners : config.listeners || {},
28827             events : config.events || {}
28828         });
28829     }
28830     
28831     if(skipConfig !== true){
28832         this.applyConfig(config);
28833     }
28834 };
28835
28836 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
28837     getPanelId : function(p){
28838         return p.getId();
28839     },
28840     
28841     applyConfig : function(config){
28842         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
28843         this.config = config;
28844         
28845     },
28846     
28847     /**
28848      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
28849      * the width, for horizontal (north, south) the height.
28850      * @param {Number} newSize The new width or height
28851      */
28852     resizeTo : function(newSize){
28853         var el = this.el ? this.el :
28854                  (this.activePanel ? this.activePanel.getEl() : null);
28855         if(el){
28856             switch(this.position){
28857                 case "east":
28858                 case "west":
28859                     el.setWidth(newSize);
28860                     this.fireEvent("resized", this, newSize);
28861                 break;
28862                 case "north":
28863                 case "south":
28864                     el.setHeight(newSize);
28865                     this.fireEvent("resized", this, newSize);
28866                 break;                
28867             }
28868         }
28869     },
28870     
28871     getBox : function(){
28872         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
28873     },
28874     
28875     getMargins : function(){
28876         return this.margins;
28877     },
28878     
28879     updateBox : function(box){
28880         this.box = box;
28881         var el = this.activePanel.getEl();
28882         el.dom.style.left = box.x + "px";
28883         el.dom.style.top = box.y + "px";
28884         this.activePanel.setSize(box.width, box.height);
28885     },
28886     
28887     /**
28888      * Returns the container element for this region.
28889      * @return {Roo.Element}
28890      */
28891     getEl : function(){
28892         return this.activePanel;
28893     },
28894     
28895     /**
28896      * Returns true if this region is currently visible.
28897      * @return {Boolean}
28898      */
28899     isVisible : function(){
28900         return this.activePanel ? true : false;
28901     },
28902     
28903     setActivePanel : function(panel){
28904         panel = this.getPanel(panel);
28905         if(this.activePanel && this.activePanel != panel){
28906             this.activePanel.setActiveState(false);
28907             this.activePanel.getEl().setLeftTop(-10000,-10000);
28908         }
28909         this.activePanel = panel;
28910         panel.setActiveState(true);
28911         if(this.box){
28912             panel.setSize(this.box.width, this.box.height);
28913         }
28914         this.fireEvent("panelactivated", this, panel);
28915         this.fireEvent("invalidated");
28916     },
28917     
28918     /**
28919      * Show the specified panel.
28920      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
28921      * @return {Roo.ContentPanel} The shown panel or null
28922      */
28923     showPanel : function(panel){
28924         if(panel = this.getPanel(panel)){
28925             this.setActivePanel(panel);
28926         }
28927         return panel;
28928     },
28929     
28930     /**
28931      * Get the active panel for this region.
28932      * @return {Roo.ContentPanel} The active panel or null
28933      */
28934     getActivePanel : function(){
28935         return this.activePanel;
28936     },
28937     
28938     /**
28939      * Add the passed ContentPanel(s)
28940      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
28941      * @return {Roo.ContentPanel} The panel added (if only one was added)
28942      */
28943     add : function(panel){
28944         if(arguments.length > 1){
28945             for(var i = 0, len = arguments.length; i < len; i++) {
28946                 this.add(arguments[i]);
28947             }
28948             return null;
28949         }
28950         if(this.hasPanel(panel)){
28951             this.showPanel(panel);
28952             return panel;
28953         }
28954         var el = panel.getEl();
28955         if(el.dom.parentNode != this.mgr.el.dom){
28956             this.mgr.el.dom.appendChild(el.dom);
28957         }
28958         if(panel.setRegion){
28959             panel.setRegion(this);
28960         }
28961         this.panels.add(panel);
28962         el.setStyle("position", "absolute");
28963         if(!panel.background){
28964             this.setActivePanel(panel);
28965             if(this.config.initialSize && this.panels.getCount()==1){
28966                 this.resizeTo(this.config.initialSize);
28967             }
28968         }
28969         this.fireEvent("paneladded", this, panel);
28970         return panel;
28971     },
28972     
28973     /**
28974      * Returns true if the panel is in this region.
28975      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28976      * @return {Boolean}
28977      */
28978     hasPanel : function(panel){
28979         if(typeof panel == "object"){ // must be panel obj
28980             panel = panel.getId();
28981         }
28982         return this.getPanel(panel) ? true : false;
28983     },
28984     
28985     /**
28986      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
28987      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
28988      * @param {Boolean} preservePanel Overrides the config preservePanel option
28989      * @return {Roo.ContentPanel} The panel that was removed
28990      */
28991     remove : function(panel, preservePanel){
28992         panel = this.getPanel(panel);
28993         if(!panel){
28994             return null;
28995         }
28996         var e = {};
28997         this.fireEvent("beforeremove", this, panel, e);
28998         if(e.cancel === true){
28999             return null;
29000         }
29001         var panelId = panel.getId();
29002         this.panels.removeKey(panelId);
29003         return panel;
29004     },
29005     
29006     /**
29007      * Returns the panel specified or null if it's not in this region.
29008      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29009      * @return {Roo.ContentPanel}
29010      */
29011     getPanel : function(id){
29012         if(typeof id == "object"){ // must be panel obj
29013             return id;
29014         }
29015         return this.panels.get(id);
29016     },
29017     
29018     /**
29019      * Returns this regions position (north/south/east/west/center).
29020      * @return {String} 
29021      */
29022     getPosition: function(){
29023         return this.position;    
29024     }
29025 });/*
29026  * Based on:
29027  * Ext JS Library 1.1.1
29028  * Copyright(c) 2006-2007, Ext JS, LLC.
29029  *
29030  * Originally Released Under LGPL - original licence link has changed is not relivant.
29031  *
29032  * Fork - LGPL
29033  * <script type="text/javascript">
29034  */
29035  
29036 /**
29037  * @class Roo.LayoutRegion
29038  * @extends Roo.BasicLayoutRegion
29039  * This class represents a region in a layout manager.
29040  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29041  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29042  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29043  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29044  * @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})
29045  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29046  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29047  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29048  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29049  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29050  * @cfg {String} title The title for the region (overrides panel titles)
29051  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29052  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29053  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29054  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29055  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29056  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29057  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29058  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29059  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29060  * @cfg {Boolean} showPin True to show a pin button
29061 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29062 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29063 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29064 * @cfg {Number} width  For East/West panels
29065 * @cfg {Number} height For North/South panels
29066 * @cfg {Boolean} split To show the splitter
29067  */
29068 Roo.LayoutRegion = function(mgr, config, pos){
29069     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29070     var dh = Roo.DomHelper;
29071     /** This region's container element 
29072     * @type Roo.Element */
29073     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29074     /** This region's title element 
29075     * @type Roo.Element */
29076
29077     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29078         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29079         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29080     ]}, true);
29081     this.titleEl.enableDisplayMode();
29082     /** This region's title text element 
29083     * @type HTMLElement */
29084     this.titleTextEl = this.titleEl.dom.firstChild;
29085     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29086     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29087     this.closeBtn.enableDisplayMode();
29088     this.closeBtn.on("click", this.closeClicked, this);
29089     this.closeBtn.hide();
29090
29091     this.createBody(config);
29092     this.visible = true;
29093     this.collapsed = false;
29094
29095     if(config.hideWhenEmpty){
29096         this.hide();
29097         this.on("paneladded", this.validateVisibility, this);
29098         this.on("panelremoved", this.validateVisibility, this);
29099     }
29100     this.applyConfig(config);
29101 };
29102
29103 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29104
29105     createBody : function(){
29106         /** This region's body element 
29107         * @type Roo.Element */
29108         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29109     },
29110
29111     applyConfig : function(c){
29112         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29113             var dh = Roo.DomHelper;
29114             if(c.titlebar !== false){
29115                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29116                 this.collapseBtn.on("click", this.collapse, this);
29117                 this.collapseBtn.enableDisplayMode();
29118
29119                 if(c.showPin === true || this.showPin){
29120                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29121                     this.stickBtn.enableDisplayMode();
29122                     this.stickBtn.on("click", this.expand, this);
29123                     this.stickBtn.hide();
29124                 }
29125             }
29126             /** This region's collapsed element
29127             * @type Roo.Element */
29128             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29129                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29130             ]}, true);
29131             if(c.floatable !== false){
29132                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29133                this.collapsedEl.on("click", this.collapseClick, this);
29134             }
29135
29136             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29137                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29138                    id: "message", unselectable: "on", style:{"float":"left"}});
29139                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29140              }
29141             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29142             this.expandBtn.on("click", this.expand, this);
29143         }
29144         if(this.collapseBtn){
29145             this.collapseBtn.setVisible(c.collapsible == true);
29146         }
29147         this.cmargins = c.cmargins || this.cmargins ||
29148                          (this.position == "west" || this.position == "east" ?
29149                              {top: 0, left: 2, right:2, bottom: 0} :
29150                              {top: 2, left: 0, right:0, bottom: 2});
29151         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29152         this.bottomTabs = c.tabPosition != "top";
29153         this.autoScroll = c.autoScroll || false;
29154         if(this.autoScroll){
29155             this.bodyEl.setStyle("overflow", "auto");
29156         }else{
29157             this.bodyEl.setStyle("overflow", "hidden");
29158         }
29159         //if(c.titlebar !== false){
29160             if((!c.titlebar && !c.title) || c.titlebar === false){
29161                 this.titleEl.hide();
29162             }else{
29163                 this.titleEl.show();
29164                 if(c.title){
29165                     this.titleTextEl.innerHTML = c.title;
29166                 }
29167             }
29168         //}
29169         this.duration = c.duration || .30;
29170         this.slideDuration = c.slideDuration || .45;
29171         this.config = c;
29172         if(c.collapsed){
29173             this.collapse(true);
29174         }
29175         if(c.hidden){
29176             this.hide();
29177         }
29178     },
29179     /**
29180      * Returns true if this region is currently visible.
29181      * @return {Boolean}
29182      */
29183     isVisible : function(){
29184         return this.visible;
29185     },
29186
29187     /**
29188      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29189      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29190      */
29191     setCollapsedTitle : function(title){
29192         title = title || "&#160;";
29193         if(this.collapsedTitleTextEl){
29194             this.collapsedTitleTextEl.innerHTML = title;
29195         }
29196     },
29197
29198     getBox : function(){
29199         var b;
29200         if(!this.collapsed){
29201             b = this.el.getBox(false, true);
29202         }else{
29203             b = this.collapsedEl.getBox(false, true);
29204         }
29205         return b;
29206     },
29207
29208     getMargins : function(){
29209         return this.collapsed ? this.cmargins : this.margins;
29210     },
29211
29212     highlight : function(){
29213         this.el.addClass("x-layout-panel-dragover");
29214     },
29215
29216     unhighlight : function(){
29217         this.el.removeClass("x-layout-panel-dragover");
29218     },
29219
29220     updateBox : function(box){
29221         this.box = box;
29222         if(!this.collapsed){
29223             this.el.dom.style.left = box.x + "px";
29224             this.el.dom.style.top = box.y + "px";
29225             this.updateBody(box.width, box.height);
29226         }else{
29227             this.collapsedEl.dom.style.left = box.x + "px";
29228             this.collapsedEl.dom.style.top = box.y + "px";
29229             this.collapsedEl.setSize(box.width, box.height);
29230         }
29231         if(this.tabs){
29232             this.tabs.autoSizeTabs();
29233         }
29234     },
29235
29236     updateBody : function(w, h){
29237         if(w !== null){
29238             this.el.setWidth(w);
29239             w -= this.el.getBorderWidth("rl");
29240             if(this.config.adjustments){
29241                 w += this.config.adjustments[0];
29242             }
29243         }
29244         if(h !== null){
29245             this.el.setHeight(h);
29246             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29247             h -= this.el.getBorderWidth("tb");
29248             if(this.config.adjustments){
29249                 h += this.config.adjustments[1];
29250             }
29251             this.bodyEl.setHeight(h);
29252             if(this.tabs){
29253                 h = this.tabs.syncHeight(h);
29254             }
29255         }
29256         if(this.panelSize){
29257             w = w !== null ? w : this.panelSize.width;
29258             h = h !== null ? h : this.panelSize.height;
29259         }
29260         if(this.activePanel){
29261             var el = this.activePanel.getEl();
29262             w = w !== null ? w : el.getWidth();
29263             h = h !== null ? h : el.getHeight();
29264             this.panelSize = {width: w, height: h};
29265             this.activePanel.setSize(w, h);
29266         }
29267         if(Roo.isIE && this.tabs){
29268             this.tabs.el.repaint();
29269         }
29270     },
29271
29272     /**
29273      * Returns the container element for this region.
29274      * @return {Roo.Element}
29275      */
29276     getEl : function(){
29277         return this.el;
29278     },
29279
29280     /**
29281      * Hides this region.
29282      */
29283     hide : function(){
29284         if(!this.collapsed){
29285             this.el.dom.style.left = "-2000px";
29286             this.el.hide();
29287         }else{
29288             this.collapsedEl.dom.style.left = "-2000px";
29289             this.collapsedEl.hide();
29290         }
29291         this.visible = false;
29292         this.fireEvent("visibilitychange", this, false);
29293     },
29294
29295     /**
29296      * Shows this region if it was previously hidden.
29297      */
29298     show : function(){
29299         if(!this.collapsed){
29300             this.el.show();
29301         }else{
29302             this.collapsedEl.show();
29303         }
29304         this.visible = true;
29305         this.fireEvent("visibilitychange", this, true);
29306     },
29307
29308     closeClicked : function(){
29309         if(this.activePanel){
29310             this.remove(this.activePanel);
29311         }
29312     },
29313
29314     collapseClick : function(e){
29315         if(this.isSlid){
29316            e.stopPropagation();
29317            this.slideIn();
29318         }else{
29319            e.stopPropagation();
29320            this.slideOut();
29321         }
29322     },
29323
29324     /**
29325      * Collapses this region.
29326      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29327      */
29328     collapse : function(skipAnim){
29329         if(this.collapsed) return;
29330         this.collapsed = true;
29331         if(this.split){
29332             this.split.el.hide();
29333         }
29334         if(this.config.animate && skipAnim !== true){
29335             this.fireEvent("invalidated", this);
29336             this.animateCollapse();
29337         }else{
29338             this.el.setLocation(-20000,-20000);
29339             this.el.hide();
29340             this.collapsedEl.show();
29341             this.fireEvent("collapsed", this);
29342             this.fireEvent("invalidated", this);
29343         }
29344     },
29345
29346     animateCollapse : function(){
29347         // overridden
29348     },
29349
29350     /**
29351      * Expands this region if it was previously collapsed.
29352      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29353      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29354      */
29355     expand : function(e, skipAnim){
29356         if(e) e.stopPropagation();
29357         if(!this.collapsed || this.el.hasActiveFx()) return;
29358         if(this.isSlid){
29359             this.afterSlideIn();
29360             skipAnim = true;
29361         }
29362         this.collapsed = false;
29363         if(this.config.animate && skipAnim !== true){
29364             this.animateExpand();
29365         }else{
29366             this.el.show();
29367             if(this.split){
29368                 this.split.el.show();
29369             }
29370             this.collapsedEl.setLocation(-2000,-2000);
29371             this.collapsedEl.hide();
29372             this.fireEvent("invalidated", this);
29373             this.fireEvent("expanded", this);
29374         }
29375     },
29376
29377     animateExpand : function(){
29378         // overridden
29379     },
29380
29381     initTabs : function(){
29382         this.bodyEl.setStyle("overflow", "hidden");
29383         var ts = new Roo.TabPanel(this.bodyEl.dom, {
29384             tabPosition: this.bottomTabs ? 'bottom' : 'top',
29385             disableTooltips: this.config.disableTabTips
29386         });
29387         if(this.config.hideTabs){
29388             ts.stripWrap.setDisplayed(false);
29389         }
29390         this.tabs = ts;
29391         ts.resizeTabs = this.config.resizeTabs === true;
29392         ts.minTabWidth = this.config.minTabWidth || 40;
29393         ts.maxTabWidth = this.config.maxTabWidth || 250;
29394         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29395         ts.monitorResize = false;
29396         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29397         ts.bodyEl.addClass('x-layout-tabs-body');
29398         this.panels.each(this.initPanelAsTab, this);
29399     },
29400
29401     initPanelAsTab : function(panel){
29402         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29403                     this.config.closeOnTab && panel.isClosable());
29404         if(panel.tabTip !== undefined){
29405             ti.setTooltip(panel.tabTip);
29406         }
29407         ti.on("activate", function(){
29408               this.setActivePanel(panel);
29409         }, this);
29410         if(this.config.closeOnTab){
29411             ti.on("beforeclose", function(t, e){
29412                 e.cancel = true;
29413                 this.remove(panel);
29414             }, this);
29415         }
29416         return ti;
29417     },
29418
29419     updatePanelTitle : function(panel, title){
29420         if(this.activePanel == panel){
29421             this.updateTitle(title);
29422         }
29423         if(this.tabs){
29424             var ti = this.tabs.getTab(panel.getEl().id);
29425             ti.setText(title);
29426             if(panel.tabTip !== undefined){
29427                 ti.setTooltip(panel.tabTip);
29428             }
29429         }
29430     },
29431
29432     updateTitle : function(title){
29433         if(this.titleTextEl && !this.config.title){
29434             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29435         }
29436     },
29437
29438     setActivePanel : function(panel){
29439         panel = this.getPanel(panel);
29440         if(this.activePanel && this.activePanel != panel){
29441             this.activePanel.setActiveState(false);
29442         }
29443         this.activePanel = panel;
29444         panel.setActiveState(true);
29445         if(this.panelSize){
29446             panel.setSize(this.panelSize.width, this.panelSize.height);
29447         }
29448         if(this.closeBtn){
29449             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29450         }
29451         this.updateTitle(panel.getTitle());
29452         if(this.tabs){
29453             this.fireEvent("invalidated", this);
29454         }
29455         this.fireEvent("panelactivated", this, panel);
29456     },
29457
29458     /**
29459      * Shows the specified panel.
29460      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
29461      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
29462      */
29463     showPanel : function(panel){
29464         if(panel = this.getPanel(panel)){
29465             if(this.tabs){
29466                 var tab = this.tabs.getTab(panel.getEl().id);
29467                 if(tab.isHidden()){
29468                     this.tabs.unhideTab(tab.id);
29469                 }
29470                 tab.activate();
29471             }else{
29472                 this.setActivePanel(panel);
29473             }
29474         }
29475         return panel;
29476     },
29477
29478     /**
29479      * Get the active panel for this region.
29480      * @return {Roo.ContentPanel} The active panel or null
29481      */
29482     getActivePanel : function(){
29483         return this.activePanel;
29484     },
29485
29486     validateVisibility : function(){
29487         if(this.panels.getCount() < 1){
29488             this.updateTitle("&#160;");
29489             this.closeBtn.hide();
29490             this.hide();
29491         }else{
29492             if(!this.isVisible()){
29493                 this.show();
29494             }
29495         }
29496     },
29497
29498     /**
29499      * Adds the passed ContentPanel(s) to this region.
29500      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29501      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
29502      */
29503     add : function(panel){
29504         if(arguments.length > 1){
29505             for(var i = 0, len = arguments.length; i < len; i++) {
29506                 this.add(arguments[i]);
29507             }
29508             return null;
29509         }
29510         if(this.hasPanel(panel)){
29511             this.showPanel(panel);
29512             return panel;
29513         }
29514         panel.setRegion(this);
29515         this.panels.add(panel);
29516         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
29517             this.bodyEl.dom.appendChild(panel.getEl().dom);
29518             if(panel.background !== true){
29519                 this.setActivePanel(panel);
29520             }
29521             this.fireEvent("paneladded", this, panel);
29522             return panel;
29523         }
29524         if(!this.tabs){
29525             this.initTabs();
29526         }else{
29527             this.initPanelAsTab(panel);
29528         }
29529         if(panel.background !== true){
29530             this.tabs.activate(panel.getEl().id);
29531         }
29532         this.fireEvent("paneladded", this, panel);
29533         return panel;
29534     },
29535
29536     /**
29537      * Hides the tab for the specified panel.
29538      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29539      */
29540     hidePanel : function(panel){
29541         if(this.tabs && (panel = this.getPanel(panel))){
29542             this.tabs.hideTab(panel.getEl().id);
29543         }
29544     },
29545
29546     /**
29547      * Unhides the tab for a previously hidden panel.
29548      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29549      */
29550     unhidePanel : function(panel){
29551         if(this.tabs && (panel = this.getPanel(panel))){
29552             this.tabs.unhideTab(panel.getEl().id);
29553         }
29554     },
29555
29556     clearPanels : function(){
29557         while(this.panels.getCount() > 0){
29558              this.remove(this.panels.first());
29559         }
29560     },
29561
29562     /**
29563      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29564      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
29565      * @param {Boolean} preservePanel Overrides the config preservePanel option
29566      * @return {Roo.ContentPanel} The panel that was removed
29567      */
29568     remove : function(panel, preservePanel){
29569         panel = this.getPanel(panel);
29570         if(!panel){
29571             return null;
29572         }
29573         var e = {};
29574         this.fireEvent("beforeremove", this, panel, e);
29575         if(e.cancel === true){
29576             return null;
29577         }
29578         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
29579         var panelId = panel.getId();
29580         this.panels.removeKey(panelId);
29581         if(preservePanel){
29582             document.body.appendChild(panel.getEl().dom);
29583         }
29584         if(this.tabs){
29585             this.tabs.removeTab(panel.getEl().id);
29586         }else if (!preservePanel){
29587             this.bodyEl.dom.removeChild(panel.getEl().dom);
29588         }
29589         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
29590             var p = this.panels.first();
29591             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
29592             tempEl.appendChild(p.getEl().dom);
29593             this.bodyEl.update("");
29594             this.bodyEl.dom.appendChild(p.getEl().dom);
29595             tempEl = null;
29596             this.updateTitle(p.getTitle());
29597             this.tabs = null;
29598             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29599             this.setActivePanel(p);
29600         }
29601         panel.setRegion(null);
29602         if(this.activePanel == panel){
29603             this.activePanel = null;
29604         }
29605         if(this.config.autoDestroy !== false && preservePanel !== true){
29606             try{panel.destroy();}catch(e){}
29607         }
29608         this.fireEvent("panelremoved", this, panel);
29609         return panel;
29610     },
29611
29612     /**
29613      * Returns the TabPanel component used by this region
29614      * @return {Roo.TabPanel}
29615      */
29616     getTabs : function(){
29617         return this.tabs;
29618     },
29619
29620     createTool : function(parentEl, className){
29621         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
29622             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
29623         btn.addClassOnOver("x-layout-tools-button-over");
29624         return btn;
29625     }
29626 });/*
29627  * Based on:
29628  * Ext JS Library 1.1.1
29629  * Copyright(c) 2006-2007, Ext JS, LLC.
29630  *
29631  * Originally Released Under LGPL - original licence link has changed is not relivant.
29632  *
29633  * Fork - LGPL
29634  * <script type="text/javascript">
29635  */
29636  
29637
29638
29639 /**
29640  * @class Roo.SplitLayoutRegion
29641  * @extends Roo.LayoutRegion
29642  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
29643  */
29644 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
29645     this.cursor = cursor;
29646     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
29647 };
29648
29649 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
29650     splitTip : "Drag to resize.",
29651     collapsibleSplitTip : "Drag to resize. Double click to hide.",
29652     useSplitTips : false,
29653
29654     applyConfig : function(config){
29655         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
29656         if(config.split){
29657             if(!this.split){
29658                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
29659                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
29660                 /** The SplitBar for this region 
29661                 * @type Roo.SplitBar */
29662                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
29663                 this.split.on("moved", this.onSplitMove, this);
29664                 this.split.useShim = config.useShim === true;
29665                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
29666                 if(this.useSplitTips){
29667                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
29668                 }
29669                 if(config.collapsible){
29670                     this.split.el.on("dblclick", this.collapse,  this);
29671                 }
29672             }
29673             if(typeof config.minSize != "undefined"){
29674                 this.split.minSize = config.minSize;
29675             }
29676             if(typeof config.maxSize != "undefined"){
29677                 this.split.maxSize = config.maxSize;
29678             }
29679             if(config.hideWhenEmpty || config.hidden || config.collapsed){
29680                 this.hideSplitter();
29681             }
29682         }
29683     },
29684
29685     getHMaxSize : function(){
29686          var cmax = this.config.maxSize || 10000;
29687          var center = this.mgr.getRegion("center");
29688          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
29689     },
29690
29691     getVMaxSize : function(){
29692          var cmax = this.config.maxSize || 10000;
29693          var center = this.mgr.getRegion("center");
29694          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
29695     },
29696
29697     onSplitMove : function(split, newSize){
29698         this.fireEvent("resized", this, newSize);
29699     },
29700     
29701     /** 
29702      * Returns the {@link Roo.SplitBar} for this region.
29703      * @return {Roo.SplitBar}
29704      */
29705     getSplitBar : function(){
29706         return this.split;
29707     },
29708     
29709     hide : function(){
29710         this.hideSplitter();
29711         Roo.SplitLayoutRegion.superclass.hide.call(this);
29712     },
29713
29714     hideSplitter : function(){
29715         if(this.split){
29716             this.split.el.setLocation(-2000,-2000);
29717             this.split.el.hide();
29718         }
29719     },
29720
29721     show : function(){
29722         if(this.split){
29723             this.split.el.show();
29724         }
29725         Roo.SplitLayoutRegion.superclass.show.call(this);
29726     },
29727     
29728     beforeSlide: function(){
29729         if(Roo.isGecko){// firefox overflow auto bug workaround
29730             this.bodyEl.clip();
29731             if(this.tabs) this.tabs.bodyEl.clip();
29732             if(this.activePanel){
29733                 this.activePanel.getEl().clip();
29734                 
29735                 if(this.activePanel.beforeSlide){
29736                     this.activePanel.beforeSlide();
29737                 }
29738             }
29739         }
29740     },
29741     
29742     afterSlide : function(){
29743         if(Roo.isGecko){// firefox overflow auto bug workaround
29744             this.bodyEl.unclip();
29745             if(this.tabs) this.tabs.bodyEl.unclip();
29746             if(this.activePanel){
29747                 this.activePanel.getEl().unclip();
29748                 if(this.activePanel.afterSlide){
29749                     this.activePanel.afterSlide();
29750                 }
29751             }
29752         }
29753     },
29754
29755     initAutoHide : function(){
29756         if(this.autoHide !== false){
29757             if(!this.autoHideHd){
29758                 var st = new Roo.util.DelayedTask(this.slideIn, this);
29759                 this.autoHideHd = {
29760                     "mouseout": function(e){
29761                         if(!e.within(this.el, true)){
29762                             st.delay(500);
29763                         }
29764                     },
29765                     "mouseover" : function(e){
29766                         st.cancel();
29767                     },
29768                     scope : this
29769                 };
29770             }
29771             this.el.on(this.autoHideHd);
29772         }
29773     },
29774
29775     clearAutoHide : function(){
29776         if(this.autoHide !== false){
29777             this.el.un("mouseout", this.autoHideHd.mouseout);
29778             this.el.un("mouseover", this.autoHideHd.mouseover);
29779         }
29780     },
29781
29782     clearMonitor : function(){
29783         Roo.get(document).un("click", this.slideInIf, this);
29784     },
29785
29786     // these names are backwards but not changed for compat
29787     slideOut : function(){
29788         if(this.isSlid || this.el.hasActiveFx()){
29789             return;
29790         }
29791         this.isSlid = true;
29792         if(this.collapseBtn){
29793             this.collapseBtn.hide();
29794         }
29795         this.closeBtnState = this.closeBtn.getStyle('display');
29796         this.closeBtn.hide();
29797         if(this.stickBtn){
29798             this.stickBtn.show();
29799         }
29800         this.el.show();
29801         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
29802         this.beforeSlide();
29803         this.el.setStyle("z-index", 10001);
29804         this.el.slideIn(this.getSlideAnchor(), {
29805             callback: function(){
29806                 this.afterSlide();
29807                 this.initAutoHide();
29808                 Roo.get(document).on("click", this.slideInIf, this);
29809                 this.fireEvent("slideshow", this);
29810             },
29811             scope: this,
29812             block: true
29813         });
29814     },
29815
29816     afterSlideIn : function(){
29817         this.clearAutoHide();
29818         this.isSlid = false;
29819         this.clearMonitor();
29820         this.el.setStyle("z-index", "");
29821         if(this.collapseBtn){
29822             this.collapseBtn.show();
29823         }
29824         this.closeBtn.setStyle('display', this.closeBtnState);
29825         if(this.stickBtn){
29826             this.stickBtn.hide();
29827         }
29828         this.fireEvent("slidehide", this);
29829     },
29830
29831     slideIn : function(cb){
29832         if(!this.isSlid || this.el.hasActiveFx()){
29833             Roo.callback(cb);
29834             return;
29835         }
29836         this.isSlid = false;
29837         this.beforeSlide();
29838         this.el.slideOut(this.getSlideAnchor(), {
29839             callback: function(){
29840                 this.el.setLeftTop(-10000, -10000);
29841                 this.afterSlide();
29842                 this.afterSlideIn();
29843                 Roo.callback(cb);
29844             },
29845             scope: this,
29846             block: true
29847         });
29848     },
29849     
29850     slideInIf : function(e){
29851         if(!e.within(this.el)){
29852             this.slideIn();
29853         }
29854     },
29855
29856     animateCollapse : function(){
29857         this.beforeSlide();
29858         this.el.setStyle("z-index", 20000);
29859         var anchor = this.getSlideAnchor();
29860         this.el.slideOut(anchor, {
29861             callback : function(){
29862                 this.el.setStyle("z-index", "");
29863                 this.collapsedEl.slideIn(anchor, {duration:.3});
29864                 this.afterSlide();
29865                 this.el.setLocation(-10000,-10000);
29866                 this.el.hide();
29867                 this.fireEvent("collapsed", this);
29868             },
29869             scope: this,
29870             block: true
29871         });
29872     },
29873
29874     animateExpand : function(){
29875         this.beforeSlide();
29876         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
29877         this.el.setStyle("z-index", 20000);
29878         this.collapsedEl.hide({
29879             duration:.1
29880         });
29881         this.el.slideIn(this.getSlideAnchor(), {
29882             callback : function(){
29883                 this.el.setStyle("z-index", "");
29884                 this.afterSlide();
29885                 if(this.split){
29886                     this.split.el.show();
29887                 }
29888                 this.fireEvent("invalidated", this);
29889                 this.fireEvent("expanded", this);
29890             },
29891             scope: this,
29892             block: true
29893         });
29894     },
29895
29896     anchors : {
29897         "west" : "left",
29898         "east" : "right",
29899         "north" : "top",
29900         "south" : "bottom"
29901     },
29902
29903     sanchors : {
29904         "west" : "l",
29905         "east" : "r",
29906         "north" : "t",
29907         "south" : "b"
29908     },
29909
29910     canchors : {
29911         "west" : "tl-tr",
29912         "east" : "tr-tl",
29913         "north" : "tl-bl",
29914         "south" : "bl-tl"
29915     },
29916
29917     getAnchor : function(){
29918         return this.anchors[this.position];
29919     },
29920
29921     getCollapseAnchor : function(){
29922         return this.canchors[this.position];
29923     },
29924
29925     getSlideAnchor : function(){
29926         return this.sanchors[this.position];
29927     },
29928
29929     getAlignAdj : function(){
29930         var cm = this.cmargins;
29931         switch(this.position){
29932             case "west":
29933                 return [0, 0];
29934             break;
29935             case "east":
29936                 return [0, 0];
29937             break;
29938             case "north":
29939                 return [0, 0];
29940             break;
29941             case "south":
29942                 return [0, 0];
29943             break;
29944         }
29945     },
29946
29947     getExpandAdj : function(){
29948         var c = this.collapsedEl, cm = this.cmargins;
29949         switch(this.position){
29950             case "west":
29951                 return [-(cm.right+c.getWidth()+cm.left), 0];
29952             break;
29953             case "east":
29954                 return [cm.right+c.getWidth()+cm.left, 0];
29955             break;
29956             case "north":
29957                 return [0, -(cm.top+cm.bottom+c.getHeight())];
29958             break;
29959             case "south":
29960                 return [0, cm.top+cm.bottom+c.getHeight()];
29961             break;
29962         }
29963     }
29964 });/*
29965  * Based on:
29966  * Ext JS Library 1.1.1
29967  * Copyright(c) 2006-2007, Ext JS, LLC.
29968  *
29969  * Originally Released Under LGPL - original licence link has changed is not relivant.
29970  *
29971  * Fork - LGPL
29972  * <script type="text/javascript">
29973  */
29974 /*
29975  * These classes are private internal classes
29976  */
29977 Roo.CenterLayoutRegion = function(mgr, config){
29978     Roo.LayoutRegion.call(this, mgr, config, "center");
29979     this.visible = true;
29980     this.minWidth = config.minWidth || 20;
29981     this.minHeight = config.minHeight || 20;
29982 };
29983
29984 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
29985     hide : function(){
29986         // center panel can't be hidden
29987     },
29988     
29989     show : function(){
29990         // center panel can't be hidden
29991     },
29992     
29993     getMinWidth: function(){
29994         return this.minWidth;
29995     },
29996     
29997     getMinHeight: function(){
29998         return this.minHeight;
29999     }
30000 });
30001
30002
30003 Roo.NorthLayoutRegion = function(mgr, config){
30004     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30005     if(this.split){
30006         this.split.placement = Roo.SplitBar.TOP;
30007         this.split.orientation = Roo.SplitBar.VERTICAL;
30008         this.split.el.addClass("x-layout-split-v");
30009     }
30010     var size = config.initialSize || config.height;
30011     if(typeof size != "undefined"){
30012         this.el.setHeight(size);
30013     }
30014 };
30015 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30016     orientation: Roo.SplitBar.VERTICAL,
30017     getBox : function(){
30018         if(this.collapsed){
30019             return this.collapsedEl.getBox();
30020         }
30021         var box = this.el.getBox();
30022         if(this.split){
30023             box.height += this.split.el.getHeight();
30024         }
30025         return box;
30026     },
30027     
30028     updateBox : function(box){
30029         if(this.split && !this.collapsed){
30030             box.height -= this.split.el.getHeight();
30031             this.split.el.setLeft(box.x);
30032             this.split.el.setTop(box.y+box.height);
30033             this.split.el.setWidth(box.width);
30034         }
30035         if(this.collapsed){
30036             this.updateBody(box.width, null);
30037         }
30038         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30039     }
30040 });
30041
30042 Roo.SouthLayoutRegion = function(mgr, config){
30043     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30044     if(this.split){
30045         this.split.placement = Roo.SplitBar.BOTTOM;
30046         this.split.orientation = Roo.SplitBar.VERTICAL;
30047         this.split.el.addClass("x-layout-split-v");
30048     }
30049     var size = config.initialSize || config.height;
30050     if(typeof size != "undefined"){
30051         this.el.setHeight(size);
30052     }
30053 };
30054 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30055     orientation: Roo.SplitBar.VERTICAL,
30056     getBox : function(){
30057         if(this.collapsed){
30058             return this.collapsedEl.getBox();
30059         }
30060         var box = this.el.getBox();
30061         if(this.split){
30062             var sh = this.split.el.getHeight();
30063             box.height += sh;
30064             box.y -= sh;
30065         }
30066         return box;
30067     },
30068     
30069     updateBox : function(box){
30070         if(this.split && !this.collapsed){
30071             var sh = this.split.el.getHeight();
30072             box.height -= sh;
30073             box.y += sh;
30074             this.split.el.setLeft(box.x);
30075             this.split.el.setTop(box.y-sh);
30076             this.split.el.setWidth(box.width);
30077         }
30078         if(this.collapsed){
30079             this.updateBody(box.width, null);
30080         }
30081         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30082     }
30083 });
30084
30085 Roo.EastLayoutRegion = function(mgr, config){
30086     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30087     if(this.split){
30088         this.split.placement = Roo.SplitBar.RIGHT;
30089         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30090         this.split.el.addClass("x-layout-split-h");
30091     }
30092     var size = config.initialSize || config.width;
30093     if(typeof size != "undefined"){
30094         this.el.setWidth(size);
30095     }
30096 };
30097 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30098     orientation: Roo.SplitBar.HORIZONTAL,
30099     getBox : function(){
30100         if(this.collapsed){
30101             return this.collapsedEl.getBox();
30102         }
30103         var box = this.el.getBox();
30104         if(this.split){
30105             var sw = this.split.el.getWidth();
30106             box.width += sw;
30107             box.x -= sw;
30108         }
30109         return box;
30110     },
30111
30112     updateBox : function(box){
30113         if(this.split && !this.collapsed){
30114             var sw = this.split.el.getWidth();
30115             box.width -= sw;
30116             this.split.el.setLeft(box.x);
30117             this.split.el.setTop(box.y);
30118             this.split.el.setHeight(box.height);
30119             box.x += sw;
30120         }
30121         if(this.collapsed){
30122             this.updateBody(null, box.height);
30123         }
30124         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30125     }
30126 });
30127
30128 Roo.WestLayoutRegion = function(mgr, config){
30129     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30130     if(this.split){
30131         this.split.placement = Roo.SplitBar.LEFT;
30132         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30133         this.split.el.addClass("x-layout-split-h");
30134     }
30135     var size = config.initialSize || config.width;
30136     if(typeof size != "undefined"){
30137         this.el.setWidth(size);
30138     }
30139 };
30140 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30141     orientation: Roo.SplitBar.HORIZONTAL,
30142     getBox : function(){
30143         if(this.collapsed){
30144             return this.collapsedEl.getBox();
30145         }
30146         var box = this.el.getBox();
30147         if(this.split){
30148             box.width += this.split.el.getWidth();
30149         }
30150         return box;
30151     },
30152     
30153     updateBox : function(box){
30154         if(this.split && !this.collapsed){
30155             var sw = this.split.el.getWidth();
30156             box.width -= sw;
30157             this.split.el.setLeft(box.x+box.width);
30158             this.split.el.setTop(box.y);
30159             this.split.el.setHeight(box.height);
30160         }
30161         if(this.collapsed){
30162             this.updateBody(null, box.height);
30163         }
30164         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30165     }
30166 });
30167 /*
30168  * Based on:
30169  * Ext JS Library 1.1.1
30170  * Copyright(c) 2006-2007, Ext JS, LLC.
30171  *
30172  * Originally Released Under LGPL - original licence link has changed is not relivant.
30173  *
30174  * Fork - LGPL
30175  * <script type="text/javascript">
30176  */
30177  
30178  
30179 /*
30180  * Private internal class for reading and applying state
30181  */
30182 Roo.LayoutStateManager = function(layout){
30183      // default empty state
30184      this.state = {
30185         north: {},
30186         south: {},
30187         east: {},
30188         west: {}       
30189     };
30190 };
30191
30192 Roo.LayoutStateManager.prototype = {
30193     init : function(layout, provider){
30194         this.provider = provider;
30195         var state = provider.get(layout.id+"-layout-state");
30196         if(state){
30197             var wasUpdating = layout.isUpdating();
30198             if(!wasUpdating){
30199                 layout.beginUpdate();
30200             }
30201             for(var key in state){
30202                 if(typeof state[key] != "function"){
30203                     var rstate = state[key];
30204                     var r = layout.getRegion(key);
30205                     if(r && rstate){
30206                         if(rstate.size){
30207                             r.resizeTo(rstate.size);
30208                         }
30209                         if(rstate.collapsed == true){
30210                             r.collapse(true);
30211                         }else{
30212                             r.expand(null, true);
30213                         }
30214                     }
30215                 }
30216             }
30217             if(!wasUpdating){
30218                 layout.endUpdate();
30219             }
30220             this.state = state; 
30221         }
30222         this.layout = layout;
30223         layout.on("regionresized", this.onRegionResized, this);
30224         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30225         layout.on("regionexpanded", this.onRegionExpanded, this);
30226     },
30227     
30228     storeState : function(){
30229         this.provider.set(this.layout.id+"-layout-state", this.state);
30230     },
30231     
30232     onRegionResized : function(region, newSize){
30233         this.state[region.getPosition()].size = newSize;
30234         this.storeState();
30235     },
30236     
30237     onRegionCollapsed : function(region){
30238         this.state[region.getPosition()].collapsed = true;
30239         this.storeState();
30240     },
30241     
30242     onRegionExpanded : function(region){
30243         this.state[region.getPosition()].collapsed = false;
30244         this.storeState();
30245     }
30246 };/*
30247  * Based on:
30248  * Ext JS Library 1.1.1
30249  * Copyright(c) 2006-2007, Ext JS, LLC.
30250  *
30251  * Originally Released Under LGPL - original licence link has changed is not relivant.
30252  *
30253  * Fork - LGPL
30254  * <script type="text/javascript">
30255  */
30256 /**
30257  * @class Roo.ContentPanel
30258  * @extends Roo.util.Observable
30259  * A basic ContentPanel element.
30260  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30261  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30262  * @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
30263  * @cfg {Boolean} closable True if the panel can be closed/removed
30264  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30265  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30266  * @cfg {Toolbar} toolbar A toolbar for this panel
30267  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30268  * @cfg {String} title The title for this panel
30269  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30270  * @cfg {String} url Calls {@link #setUrl} with this value
30271  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30272  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30273  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30274  * @constructor
30275  * Create a new ContentPanel.
30276  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30277  * @param {String/Object} config A string to set only the title or a config object
30278  * @param {String} content (optional) Set the HTML content for this panel
30279  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30280  */
30281 Roo.ContentPanel = function(el, config, content){
30282     
30283      
30284     /*
30285     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30286         config = el;
30287         el = Roo.id();
30288     }
30289     if (config && config.parentLayout) { 
30290         el = config.parentLayout.el.createChild(); 
30291     }
30292     */
30293     if(el.autoCreate){ // xtype is available if this is called from factory
30294         config = el;
30295         el = Roo.id();
30296     }
30297     this.el = Roo.get(el);
30298     if(!this.el && config && config.autoCreate){
30299         if(typeof config.autoCreate == "object"){
30300             if(!config.autoCreate.id){
30301                 config.autoCreate.id = config.id||el;
30302             }
30303             this.el = Roo.DomHelper.append(document.body,
30304                         config.autoCreate, true);
30305         }else{
30306             this.el = Roo.DomHelper.append(document.body,
30307                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30308         }
30309     }
30310     this.closable = false;
30311     this.loaded = false;
30312     this.active = false;
30313     if(typeof config == "string"){
30314         this.title = config;
30315     }else{
30316         Roo.apply(this, config);
30317     }
30318     
30319     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30320         this.wrapEl = this.el.wrap();    
30321         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30322         
30323     }
30324     
30325     
30326     
30327     if(this.resizeEl){
30328         this.resizeEl = Roo.get(this.resizeEl, true);
30329     }else{
30330         this.resizeEl = this.el;
30331     }
30332     this.addEvents({
30333         /**
30334          * @event activate
30335          * Fires when this panel is activated. 
30336          * @param {Roo.ContentPanel} this
30337          */
30338         "activate" : true,
30339         /**
30340          * @event deactivate
30341          * Fires when this panel is activated. 
30342          * @param {Roo.ContentPanel} this
30343          */
30344         "deactivate" : true,
30345
30346         /**
30347          * @event resize
30348          * Fires when this panel is resized if fitToFrame is true.
30349          * @param {Roo.ContentPanel} this
30350          * @param {Number} width The width after any component adjustments
30351          * @param {Number} height The height after any component adjustments
30352          */
30353         "resize" : true
30354     });
30355     if(this.autoScroll){
30356         this.resizeEl.setStyle("overflow", "auto");
30357     }
30358     content = content || this.content;
30359     if(content){
30360         this.setContent(content);
30361     }
30362     if(config && config.url){
30363         this.setUrl(this.url, this.params, this.loadOnce);
30364     }
30365     
30366     
30367     
30368     Roo.ContentPanel.superclass.constructor.call(this);
30369 };
30370
30371 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30372     tabTip:'',
30373     setRegion : function(region){
30374         this.region = region;
30375         if(region){
30376            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30377         }else{
30378            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30379         } 
30380     },
30381     
30382     /**
30383      * Returns the toolbar for this Panel if one was configured. 
30384      * @return {Roo.Toolbar} 
30385      */
30386     getToolbar : function(){
30387         return this.toolbar;
30388     },
30389     
30390     setActiveState : function(active){
30391         this.active = active;
30392         if(!active){
30393             this.fireEvent("deactivate", this);
30394         }else{
30395             this.fireEvent("activate", this);
30396         }
30397     },
30398     /**
30399      * Updates this panel's element
30400      * @param {String} content The new content
30401      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30402     */
30403     setContent : function(content, loadScripts){
30404         this.el.update(content, loadScripts);
30405     },
30406
30407     ignoreResize : function(w, h){
30408         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30409             return true;
30410         }else{
30411             this.lastSize = {width: w, height: h};
30412             return false;
30413         }
30414     },
30415     /**
30416      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30417      * @return {Roo.UpdateManager} The UpdateManager
30418      */
30419     getUpdateManager : function(){
30420         return this.el.getUpdateManager();
30421     },
30422      /**
30423      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30424      * @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:
30425 <pre><code>
30426 panel.load({
30427     url: "your-url.php",
30428     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30429     callback: yourFunction,
30430     scope: yourObject, //(optional scope)
30431     discardUrl: false,
30432     nocache: false,
30433     text: "Loading...",
30434     timeout: 30,
30435     scripts: false
30436 });
30437 </code></pre>
30438      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30439      * 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.
30440      * @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}
30441      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30442      * @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.
30443      * @return {Roo.ContentPanel} this
30444      */
30445     load : function(){
30446         var um = this.el.getUpdateManager();
30447         um.update.apply(um, arguments);
30448         return this;
30449     },
30450
30451
30452     /**
30453      * 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.
30454      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
30455      * @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)
30456      * @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)
30457      * @return {Roo.UpdateManager} The UpdateManager
30458      */
30459     setUrl : function(url, params, loadOnce){
30460         if(this.refreshDelegate){
30461             this.removeListener("activate", this.refreshDelegate);
30462         }
30463         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30464         this.on("activate", this.refreshDelegate);
30465         return this.el.getUpdateManager();
30466     },
30467     
30468     _handleRefresh : function(url, params, loadOnce){
30469         if(!loadOnce || !this.loaded){
30470             var updater = this.el.getUpdateManager();
30471             updater.update(url, params, this._setLoaded.createDelegate(this));
30472         }
30473     },
30474     
30475     _setLoaded : function(){
30476         this.loaded = true;
30477     }, 
30478     
30479     /**
30480      * Returns this panel's id
30481      * @return {String} 
30482      */
30483     getId : function(){
30484         return this.el.id;
30485     },
30486     
30487     /** 
30488      * Returns this panel's element - used by regiosn to add.
30489      * @return {Roo.Element} 
30490      */
30491     getEl : function(){
30492         return this.wrapEl || this.el;
30493     },
30494     
30495     adjustForComponents : function(width, height){
30496         if(this.resizeEl != this.el){
30497             width -= this.el.getFrameWidth('lr');
30498             height -= this.el.getFrameWidth('tb');
30499         }
30500         if(this.toolbar){
30501             var te = this.toolbar.getEl();
30502             height -= te.getHeight();
30503             te.setWidth(width);
30504         }
30505         if(this.adjustments){
30506             width += this.adjustments[0];
30507             height += this.adjustments[1];
30508         }
30509         return {"width": width, "height": height};
30510     },
30511     
30512     setSize : function(width, height){
30513         if(this.fitToFrame && !this.ignoreResize(width, height)){
30514             if(this.fitContainer && this.resizeEl != this.el){
30515                 this.el.setSize(width, height);
30516             }
30517             var size = this.adjustForComponents(width, height);
30518             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
30519             this.fireEvent('resize', this, size.width, size.height);
30520         }
30521     },
30522     
30523     /**
30524      * Returns this panel's title
30525      * @return {String} 
30526      */
30527     getTitle : function(){
30528         return this.title;
30529     },
30530     
30531     /**
30532      * Set this panel's title
30533      * @param {String} title
30534      */
30535     setTitle : function(title){
30536         this.title = title;
30537         if(this.region){
30538             this.region.updatePanelTitle(this, title);
30539         }
30540     },
30541     
30542     /**
30543      * Returns true is this panel was configured to be closable
30544      * @return {Boolean} 
30545      */
30546     isClosable : function(){
30547         return this.closable;
30548     },
30549     
30550     beforeSlide : function(){
30551         this.el.clip();
30552         this.resizeEl.clip();
30553     },
30554     
30555     afterSlide : function(){
30556         this.el.unclip();
30557         this.resizeEl.unclip();
30558     },
30559     
30560     /**
30561      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
30562      *   Will fail silently if the {@link #setUrl} method has not been called.
30563      *   This does not activate the panel, just updates its content.
30564      */
30565     refresh : function(){
30566         if(this.refreshDelegate){
30567            this.loaded = false;
30568            this.refreshDelegate();
30569         }
30570     },
30571     
30572     /**
30573      * Destroys this panel
30574      */
30575     destroy : function(){
30576         this.el.removeAllListeners();
30577         var tempEl = document.createElement("span");
30578         tempEl.appendChild(this.el.dom);
30579         tempEl.innerHTML = "";
30580         this.el.remove();
30581         this.el = null;
30582     },
30583     
30584       /**
30585      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
30586      * <pre><code>
30587
30588 layout.addxtype({
30589        xtype : 'Form',
30590        items: [ .... ]
30591    }
30592 );
30593
30594 </code></pre>
30595      * @param {Object} cfg Xtype definition of item to add.
30596      */
30597     
30598     addxtype : function(cfg) {
30599         // add form..
30600         if (cfg.xtype.match(/^Form$/)) {
30601             var el = this.el.createChild();
30602
30603             this.form = new  Roo.form.Form(cfg);
30604             
30605             
30606             if ( this.form.allItems.length) this.form.render(el.dom);
30607             return this.form;
30608         }
30609         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
30610             // views..
30611             cfg.el = this.el.appendChild(document.createElement("div"));
30612             // factory?
30613             var ret = new Roo[cfg.xtype](cfg);
30614             ret.render(false, ''); // render blank..
30615             return ret;
30616             
30617         }
30618         return false;
30619         
30620     }
30621 });
30622
30623 /**
30624  * @class Roo.GridPanel
30625  * @extends Roo.ContentPanel
30626  * @constructor
30627  * Create a new GridPanel.
30628  * @param {Roo.grid.Grid} grid The grid for this panel
30629  * @param {String/Object} config A string to set only the panel's title, or a config object
30630  */
30631 Roo.GridPanel = function(grid, config){
30632     
30633   
30634     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
30635         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
30636         
30637     this.wrapper.dom.appendChild(grid.getGridEl().dom);
30638     
30639     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
30640     
30641     if(this.toolbar){
30642         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
30643     }
30644     // xtype created footer. - not sure if will work as we normally have to render first..
30645     if (this.footer && !this.footer.el && this.footer.xtype) {
30646         
30647         this.footer.container = this.grid.getView().getFooterPanel(true);
30648         this.footer.dataSource = this.grid.dataSource;
30649         this.footer = Roo.factory(this.footer, Roo);
30650         
30651     }
30652     
30653     grid.monitorWindowResize = false; // turn off autosizing
30654     grid.autoHeight = false;
30655     grid.autoWidth = false;
30656     this.grid = grid;
30657     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
30658 };
30659
30660 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
30661     getId : function(){
30662         return this.grid.id;
30663     },
30664     
30665     /**
30666      * Returns the grid for this panel
30667      * @return {Roo.grid.Grid} 
30668      */
30669     getGrid : function(){
30670         return this.grid;    
30671     },
30672     
30673     setSize : function(width, height){
30674         if(!this.ignoreResize(width, height)){
30675             var grid = this.grid;
30676             var size = this.adjustForComponents(width, height);
30677             grid.getGridEl().setSize(size.width, size.height);
30678             grid.autoSize();
30679         }
30680     },
30681     
30682     beforeSlide : function(){
30683         this.grid.getView().scroller.clip();
30684     },
30685     
30686     afterSlide : function(){
30687         this.grid.getView().scroller.unclip();
30688     },
30689     
30690     destroy : function(){
30691         this.grid.destroy();
30692         delete this.grid;
30693         Roo.GridPanel.superclass.destroy.call(this); 
30694     }
30695 });
30696
30697
30698 /**
30699  * @class Roo.NestedLayoutPanel
30700  * @extends Roo.ContentPanel
30701  * @constructor
30702  * Create a new NestedLayoutPanel.
30703  * 
30704  * 
30705  * @param {Roo.BorderLayout} layout The layout for this panel
30706  * @param {String/Object} config A string to set only the title or a config object
30707  */
30708 Roo.NestedLayoutPanel = function(layout, config)
30709 {
30710     // construct with only one argument..
30711     /* FIXME - implement nicer consturctors
30712     if (layout.layout) {
30713         config = layout;
30714         layout = config.layout;
30715         delete config.layout;
30716     }
30717     if (layout.xtype && !layout.getEl) {
30718         // then layout needs constructing..
30719         layout = Roo.factory(layout, Roo);
30720     }
30721     */
30722     
30723     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
30724     
30725     layout.monitorWindowResize = false; // turn off autosizing
30726     this.layout = layout;
30727     this.layout.getEl().addClass("x-layout-nested-layout");
30728     
30729     
30730     
30731 };
30732
30733 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
30734
30735     setSize : function(width, height){
30736         if(!this.ignoreResize(width, height)){
30737             var size = this.adjustForComponents(width, height);
30738             var el = this.layout.getEl();
30739             el.setSize(size.width, size.height);
30740             var touch = el.dom.offsetWidth;
30741             this.layout.layout();
30742             // ie requires a double layout on the first pass
30743             if(Roo.isIE && !this.initialized){
30744                 this.initialized = true;
30745                 this.layout.layout();
30746             }
30747         }
30748     },
30749     
30750     // activate all subpanels if not currently active..
30751     
30752     setActiveState : function(active){
30753         this.active = active;
30754         if(!active){
30755             this.fireEvent("deactivate", this);
30756             return;
30757         }
30758         
30759         this.fireEvent("activate", this);
30760         // not sure if this should happen before or after..
30761         if (!this.layout) {
30762             return; // should not happen..
30763         }
30764         var reg = false;
30765         for (var r in this.layout.regions) {
30766             reg = this.layout.getRegion(r);
30767             if (reg.getActivePanel()) {
30768                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
30769                 reg.setActivePanel(reg.getActivePanel());
30770                 continue;
30771             }
30772             if (!reg.panels.length) {
30773                 continue;
30774             }
30775             reg.showPanel(reg.getPanel(0));
30776         }
30777         
30778         
30779         
30780         
30781     },
30782     
30783     /**
30784      * Returns the nested BorderLayout for this panel
30785      * @return {Roo.BorderLayout} 
30786      */
30787     getLayout : function(){
30788         return this.layout;
30789     },
30790     
30791      /**
30792      * Adds a xtype elements to the layout of the nested panel
30793      * <pre><code>
30794
30795 panel.addxtype({
30796        xtype : 'ContentPanel',
30797        region: 'west',
30798        items: [ .... ]
30799    }
30800 );
30801
30802 panel.addxtype({
30803         xtype : 'NestedLayoutPanel',
30804         region: 'west',
30805         layout: {
30806            center: { },
30807            west: { }   
30808         },
30809         items : [ ... list of content panels or nested layout panels.. ]
30810    }
30811 );
30812 </code></pre>
30813      * @param {Object} cfg Xtype definition of item to add.
30814      */
30815     addxtype : function(cfg) {
30816         return this.layout.addxtype(cfg);
30817     
30818     }
30819 });
30820
30821 Roo.ScrollPanel = function(el, config, content){
30822     config = config || {};
30823     config.fitToFrame = true;
30824     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
30825     
30826     this.el.dom.style.overflow = "hidden";
30827     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
30828     this.el.removeClass("x-layout-inactive-content");
30829     this.el.on("mousewheel", this.onWheel, this);
30830
30831     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
30832     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
30833     up.unselectable(); down.unselectable();
30834     up.on("click", this.scrollUp, this);
30835     down.on("click", this.scrollDown, this);
30836     up.addClassOnOver("x-scroller-btn-over");
30837     down.addClassOnOver("x-scroller-btn-over");
30838     up.addClassOnClick("x-scroller-btn-click");
30839     down.addClassOnClick("x-scroller-btn-click");
30840     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
30841
30842     this.resizeEl = this.el;
30843     this.el = wrap; this.up = up; this.down = down;
30844 };
30845
30846 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
30847     increment : 100,
30848     wheelIncrement : 5,
30849     scrollUp : function(){
30850         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
30851     },
30852
30853     scrollDown : function(){
30854         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
30855     },
30856
30857     afterScroll : function(){
30858         var el = this.resizeEl;
30859         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
30860         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30861         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
30862     },
30863
30864     setSize : function(){
30865         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
30866         this.afterScroll();
30867     },
30868
30869     onWheel : function(e){
30870         var d = e.getWheelDelta();
30871         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
30872         this.afterScroll();
30873         e.stopEvent();
30874     },
30875
30876     setContent : function(content, loadScripts){
30877         this.resizeEl.update(content, loadScripts);
30878     }
30879
30880 });
30881
30882
30883
30884
30885
30886
30887
30888
30889
30890 /**
30891  * @class Roo.TreePanel
30892  * @extends Roo.ContentPanel
30893  * @constructor
30894  * Create a new TreePanel. - defaults to fit/scoll contents.
30895  * @param {String/Object} config A string to set only the panel's title, or a config object
30896  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
30897  */
30898 Roo.TreePanel = function(config){
30899     var el = config.el;
30900     var tree = config.tree;
30901     delete config.tree; 
30902     delete config.el; // hopefull!
30903     
30904     // wrapper for IE7 strict & safari scroll issue
30905     
30906     var treeEl = el.createChild();
30907     config.resizeEl = treeEl;
30908     
30909     
30910     
30911     Roo.TreePanel.superclass.constructor.call(this, el, config);
30912  
30913  
30914     this.tree = new Roo.tree.TreePanel(treeEl , tree);
30915     //console.log(tree);
30916     this.on('activate', function()
30917     {
30918         if (this.tree.rendered) {
30919             return;
30920         }
30921         //console.log('render tree');
30922         this.tree.render();
30923     });
30924     
30925     this.on('resize',  function (cp, w, h) {
30926             this.tree.innerCt.setWidth(w);
30927             this.tree.innerCt.setHeight(h);
30928             this.tree.innerCt.setStyle('overflow-y', 'auto');
30929     });
30930
30931         
30932     
30933 };
30934
30935 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
30936     fitToFrame : true,
30937     autoScroll : true
30938 });
30939
30940
30941
30942
30943
30944
30945
30946
30947
30948
30949
30950 /*
30951  * Based on:
30952  * Ext JS Library 1.1.1
30953  * Copyright(c) 2006-2007, Ext JS, LLC.
30954  *
30955  * Originally Released Under LGPL - original licence link has changed is not relivant.
30956  *
30957  * Fork - LGPL
30958  * <script type="text/javascript">
30959  */
30960  
30961
30962 /**
30963  * @class Roo.ReaderLayout
30964  * @extends Roo.BorderLayout
30965  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
30966  * center region containing two nested regions (a top one for a list view and one for item preview below),
30967  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
30968  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
30969  * expedites the setup of the overall layout and regions for this common application style.
30970  * Example:
30971  <pre><code>
30972 var reader = new Roo.ReaderLayout();
30973 var CP = Roo.ContentPanel;  // shortcut for adding
30974
30975 reader.beginUpdate();
30976 reader.add("north", new CP("north", "North"));
30977 reader.add("west", new CP("west", {title: "West"}));
30978 reader.add("east", new CP("east", {title: "East"}));
30979
30980 reader.regions.listView.add(new CP("listView", "List"));
30981 reader.regions.preview.add(new CP("preview", "Preview"));
30982 reader.endUpdate();
30983 </code></pre>
30984 * @constructor
30985 * Create a new ReaderLayout
30986 * @param {Object} config Configuration options
30987 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
30988 * document.body if omitted)
30989 */
30990 Roo.ReaderLayout = function(config, renderTo){
30991     var c = config || {size:{}};
30992     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
30993         north: c.north !== false ? Roo.apply({
30994             split:false,
30995             initialSize: 32,
30996             titlebar: false
30997         }, c.north) : false,
30998         west: c.west !== false ? Roo.apply({
30999             split:true,
31000             initialSize: 200,
31001             minSize: 175,
31002             maxSize: 400,
31003             titlebar: true,
31004             collapsible: true,
31005             animate: true,
31006             margins:{left:5,right:0,bottom:5,top:5},
31007             cmargins:{left:5,right:5,bottom:5,top:5}
31008         }, c.west) : false,
31009         east: c.east !== false ? Roo.apply({
31010             split:true,
31011             initialSize: 200,
31012             minSize: 175,
31013             maxSize: 400,
31014             titlebar: true,
31015             collapsible: true,
31016             animate: true,
31017             margins:{left:0,right:5,bottom:5,top:5},
31018             cmargins:{left:5,right:5,bottom:5,top:5}
31019         }, c.east) : false,
31020         center: Roo.apply({
31021             tabPosition: 'top',
31022             autoScroll:false,
31023             closeOnTab: true,
31024             titlebar:false,
31025             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31026         }, c.center)
31027     });
31028
31029     this.el.addClass('x-reader');
31030
31031     this.beginUpdate();
31032
31033     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31034         south: c.preview !== false ? Roo.apply({
31035             split:true,
31036             initialSize: 200,
31037             minSize: 100,
31038             autoScroll:true,
31039             collapsible:true,
31040             titlebar: true,
31041             cmargins:{top:5,left:0, right:0, bottom:0}
31042         }, c.preview) : false,
31043         center: Roo.apply({
31044             autoScroll:false,
31045             titlebar:false,
31046             minHeight:200
31047         }, c.listView)
31048     });
31049     this.add('center', new Roo.NestedLayoutPanel(inner,
31050             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31051
31052     this.endUpdate();
31053
31054     this.regions.preview = inner.getRegion('south');
31055     this.regions.listView = inner.getRegion('center');
31056 };
31057
31058 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31059  * Based on:
31060  * Ext JS Library 1.1.1
31061  * Copyright(c) 2006-2007, Ext JS, LLC.
31062  *
31063  * Originally Released Under LGPL - original licence link has changed is not relivant.
31064  *
31065  * Fork - LGPL
31066  * <script type="text/javascript">
31067  */
31068  
31069 /**
31070  * @class Roo.grid.Grid
31071  * @extends Roo.util.Observable
31072  * This class represents the primary interface of a component based grid control.
31073  * <br><br>Usage:<pre><code>
31074  var grid = new Roo.grid.Grid("my-container-id", {
31075      ds: myDataStore,
31076      cm: myColModel,
31077      selModel: mySelectionModel,
31078      autoSizeColumns: true,
31079      monitorWindowResize: false,
31080      trackMouseOver: true
31081  });
31082  // set any options
31083  grid.render();
31084  * </code></pre>
31085  * <b>Common Problems:</b><br/>
31086  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31087  * element will correct this<br/>
31088  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31089  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31090  * are unpredictable.<br/>
31091  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31092  * grid to calculate dimensions/offsets.<br/>
31093   * @constructor
31094  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31095  * The container MUST have some type of size defined for the grid to fill. The container will be
31096  * automatically set to position relative if it isn't already.
31097  * @param {Object} config A config object that sets properties on this grid.
31098  */
31099 Roo.grid.Grid = function(container, config){
31100         // initialize the container
31101         this.container = Roo.get(container);
31102         this.container.update("");
31103         this.container.setStyle("overflow", "hidden");
31104     this.container.addClass('x-grid-container');
31105
31106     this.id = this.container.id;
31107
31108     Roo.apply(this, config);
31109     // check and correct shorthanded configs
31110     if(this.ds){
31111         this.dataSource = this.ds;
31112         delete this.ds;
31113     }
31114     if(this.cm){
31115         this.colModel = this.cm;
31116         delete this.cm;
31117     }
31118     if(this.sm){
31119         this.selModel = this.sm;
31120         delete this.sm;
31121     }
31122
31123     if (this.selModel) {
31124         this.selModel = Roo.factory(this.selModel, Roo.grid);
31125         this.sm = this.selModel;
31126         this.sm.xmodule = this.xmodule || false;
31127     }
31128     if (typeof(this.colModel.config) == 'undefined') {
31129         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31130         this.cm = this.colModel;
31131         this.cm.xmodule = this.xmodule || false;
31132     }
31133     if (this.dataSource) {
31134         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31135         this.ds = this.dataSource;
31136         this.ds.xmodule = this.xmodule || false;
31137         
31138     }
31139     
31140     
31141     
31142     if(this.width){
31143         this.container.setWidth(this.width);
31144     }
31145
31146     if(this.height){
31147         this.container.setHeight(this.height);
31148     }
31149     /** @private */
31150         this.addEvents({
31151             // raw events
31152             /**
31153              * @event click
31154              * The raw click event for the entire grid.
31155              * @param {Roo.EventObject} e
31156              */
31157             "click" : true,
31158             /**
31159              * @event dblclick
31160              * The raw dblclick event for the entire grid.
31161              * @param {Roo.EventObject} e
31162              */
31163             "dblclick" : true,
31164             /**
31165              * @event contextmenu
31166              * The raw contextmenu event for the entire grid.
31167              * @param {Roo.EventObject} e
31168              */
31169             "contextmenu" : true,
31170             /**
31171              * @event mousedown
31172              * The raw mousedown event for the entire grid.
31173              * @param {Roo.EventObject} e
31174              */
31175             "mousedown" : true,
31176             /**
31177              * @event mouseup
31178              * The raw mouseup event for the entire grid.
31179              * @param {Roo.EventObject} e
31180              */
31181             "mouseup" : true,
31182             /**
31183              * @event mouseover
31184              * The raw mouseover event for the entire grid.
31185              * @param {Roo.EventObject} e
31186              */
31187             "mouseover" : true,
31188             /**
31189              * @event mouseout
31190              * The raw mouseout event for the entire grid.
31191              * @param {Roo.EventObject} e
31192              */
31193             "mouseout" : true,
31194             /**
31195              * @event keypress
31196              * The raw keypress event for the entire grid.
31197              * @param {Roo.EventObject} e
31198              */
31199             "keypress" : true,
31200             /**
31201              * @event keydown
31202              * The raw keydown event for the entire grid.
31203              * @param {Roo.EventObject} e
31204              */
31205             "keydown" : true,
31206
31207             // custom events
31208
31209             /**
31210              * @event cellclick
31211              * Fires when a cell is clicked
31212              * @param {Grid} this
31213              * @param {Number} rowIndex
31214              * @param {Number} columnIndex
31215              * @param {Roo.EventObject} e
31216              */
31217             "cellclick" : true,
31218             /**
31219              * @event celldblclick
31220              * Fires when a cell is double clicked
31221              * @param {Grid} this
31222              * @param {Number} rowIndex
31223              * @param {Number} columnIndex
31224              * @param {Roo.EventObject} e
31225              */
31226             "celldblclick" : true,
31227             /**
31228              * @event rowclick
31229              * Fires when a row is clicked
31230              * @param {Grid} this
31231              * @param {Number} rowIndex
31232              * @param {Roo.EventObject} e
31233              */
31234             "rowclick" : true,
31235             /**
31236              * @event rowdblclick
31237              * Fires when a row is double clicked
31238              * @param {Grid} this
31239              * @param {Number} rowIndex
31240              * @param {Roo.EventObject} e
31241              */
31242             "rowdblclick" : true,
31243             /**
31244              * @event headerclick
31245              * Fires when a header is clicked
31246              * @param {Grid} this
31247              * @param {Number} columnIndex
31248              * @param {Roo.EventObject} e
31249              */
31250             "headerclick" : true,
31251             /**
31252              * @event headerdblclick
31253              * Fires when a header cell is double clicked
31254              * @param {Grid} this
31255              * @param {Number} columnIndex
31256              * @param {Roo.EventObject} e
31257              */
31258             "headerdblclick" : true,
31259             /**
31260              * @event rowcontextmenu
31261              * Fires when a row is right clicked
31262              * @param {Grid} this
31263              * @param {Number} rowIndex
31264              * @param {Roo.EventObject} e
31265              */
31266             "rowcontextmenu" : true,
31267             /**
31268          * @event cellcontextmenu
31269          * Fires when a cell is right clicked
31270          * @param {Grid} this
31271          * @param {Number} rowIndex
31272          * @param {Number} cellIndex
31273          * @param {Roo.EventObject} e
31274          */
31275          "cellcontextmenu" : true,
31276             /**
31277              * @event headercontextmenu
31278              * Fires when a header is right clicked
31279              * @param {Grid} this
31280              * @param {Number} columnIndex
31281              * @param {Roo.EventObject} e
31282              */
31283             "headercontextmenu" : true,
31284             /**
31285              * @event bodyscroll
31286              * Fires when the body element is scrolled
31287              * @param {Number} scrollLeft
31288              * @param {Number} scrollTop
31289              */
31290             "bodyscroll" : true,
31291             /**
31292              * @event columnresize
31293              * Fires when the user resizes a column
31294              * @param {Number} columnIndex
31295              * @param {Number} newSize
31296              */
31297             "columnresize" : true,
31298             /**
31299              * @event columnmove
31300              * Fires when the user moves a column
31301              * @param {Number} oldIndex
31302              * @param {Number} newIndex
31303              */
31304             "columnmove" : true,
31305             /**
31306              * @event startdrag
31307              * Fires when row(s) start being dragged
31308              * @param {Grid} this
31309              * @param {Roo.GridDD} dd The drag drop object
31310              * @param {event} e The raw browser event
31311              */
31312             "startdrag" : true,
31313             /**
31314              * @event enddrag
31315              * Fires when a drag operation is complete
31316              * @param {Grid} this
31317              * @param {Roo.GridDD} dd The drag drop object
31318              * @param {event} e The raw browser event
31319              */
31320             "enddrag" : true,
31321             /**
31322              * @event dragdrop
31323              * Fires when dragged row(s) are dropped on a valid DD target
31324              * @param {Grid} this
31325              * @param {Roo.GridDD} dd The drag drop object
31326              * @param {String} targetId The target drag drop object
31327              * @param {event} e The raw browser event
31328              */
31329             "dragdrop" : true,
31330             /**
31331              * @event dragover
31332              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31333              * @param {Grid} this
31334              * @param {Roo.GridDD} dd The drag drop object
31335              * @param {String} targetId The target drag drop object
31336              * @param {event} e The raw browser event
31337              */
31338             "dragover" : true,
31339             /**
31340              * @event dragenter
31341              *  Fires when the dragged row(s) first cross another DD target while being dragged
31342              * @param {Grid} this
31343              * @param {Roo.GridDD} dd The drag drop object
31344              * @param {String} targetId The target drag drop object
31345              * @param {event} e The raw browser event
31346              */
31347             "dragenter" : true,
31348             /**
31349              * @event dragout
31350              * Fires when the dragged row(s) leave another DD target while being dragged
31351              * @param {Grid} this
31352              * @param {Roo.GridDD} dd The drag drop object
31353              * @param {String} targetId The target drag drop object
31354              * @param {event} e The raw browser event
31355              */
31356             "dragout" : true,
31357         /**
31358          * @event render
31359          * Fires when the grid is rendered
31360          * @param {Grid} grid
31361          */
31362         render : true
31363     });
31364
31365     Roo.grid.Grid.superclass.constructor.call(this);
31366 };
31367 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31368     
31369     /**
31370      * @cfg {String} ddGroup - drag drop group.
31371          */
31372     
31373     /**
31374      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31375          */
31376         minColumnWidth : 25,
31377
31378     /**
31379          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31380          * <b>on initial render.</b> It is more efficient to explicitly size the columns
31381          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31382          */
31383         autoSizeColumns : false,
31384
31385         /**
31386          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31387          */
31388         autoSizeHeaders : true,
31389
31390         /**
31391          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31392          */
31393         monitorWindowResize : true,
31394
31395         /**
31396          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31397          * rows measured to get a columns size. Default is 0 (all rows).
31398          */
31399         maxRowsToMeasure : 0,
31400
31401         /**
31402          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31403          */
31404         trackMouseOver : true,
31405
31406     /**
31407          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
31408          */
31409     
31410         /**
31411          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
31412          */
31413         enableDragDrop : false,
31414
31415         /**
31416          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
31417          */
31418         enableColumnMove : true,
31419
31420         /**
31421          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
31422          */
31423         enableColumnHide : true,
31424
31425         /**
31426          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
31427          */
31428         enableRowHeightSync : false,
31429
31430         /**
31431          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
31432          */
31433         stripeRows : true,
31434
31435         /**
31436          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
31437          */
31438         autoHeight : false,
31439
31440     /**
31441      * @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.
31442      */
31443     autoExpandColumn : false,
31444
31445     /**
31446     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
31447     * Default is 50.
31448     */
31449     autoExpandMin : 50,
31450
31451     /**
31452     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
31453     */
31454     autoExpandMax : 1000,
31455
31456     /**
31457          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
31458          */
31459         view : null,
31460
31461         /**
31462      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
31463          */
31464         loadMask : false,
31465
31466     // private
31467     rendered : false,
31468
31469     /**
31470     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
31471     * of a fixed width. Default is false.
31472     */
31473     /**
31474     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
31475     */
31476     /**
31477      * Called once after all setup has been completed and the grid is ready to be rendered.
31478      * @return {Roo.grid.Grid} this
31479      */
31480     render : function(){
31481         var c = this.container;
31482         // try to detect autoHeight/width mode
31483         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
31484             this.autoHeight = true;
31485         }
31486         var view = this.getView();
31487         view.init(this);
31488
31489         c.on("click", this.onClick, this);
31490         c.on("dblclick", this.onDblClick, this);
31491         c.on("contextmenu", this.onContextMenu, this);
31492         c.on("keydown", this.onKeyDown, this);
31493
31494         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
31495
31496         this.getSelectionModel().init(this);
31497
31498         view.render();
31499
31500         if(this.loadMask){
31501             this.loadMask = new Roo.LoadMask(this.container,
31502                     Roo.apply({store:this.dataSource}, this.loadMask));
31503         }
31504         
31505         
31506         if (this.toolbar && this.toolbar.xtype) {
31507             this.toolbar.container = this.getView().getHeaderPanel(true);
31508             this.toolbar = new Ext.Toolbar(this.toolbar);
31509         }
31510         if (this.footer && this.footer.xtype) {
31511             this.footer.dataSource = this.getDataSource();
31512             this.footer.container = this.getView().getFooterPanel(true);
31513             this.footer = Roo.factory(this.footer, Roo);
31514         }
31515         this.rendered = true;
31516         this.fireEvent('render', this);
31517         return this;
31518     },
31519
31520         /**
31521          * Reconfigures the grid to use a different Store and Column Model.
31522          * The View will be bound to the new objects and refreshed.
31523          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
31524          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
31525          */
31526     reconfigure : function(dataSource, colModel){
31527         if(this.loadMask){
31528             this.loadMask.destroy();
31529             this.loadMask = new Roo.LoadMask(this.container,
31530                     Roo.apply({store:dataSource}, this.loadMask));
31531         }
31532         this.view.bind(dataSource, colModel);
31533         this.dataSource = dataSource;
31534         this.colModel = colModel;
31535         this.view.refresh(true);
31536     },
31537
31538     // private
31539     onKeyDown : function(e){
31540         this.fireEvent("keydown", e);
31541     },
31542
31543     /**
31544      * Destroy this grid.
31545      * @param {Boolean} removeEl True to remove the element
31546      */
31547     destroy : function(removeEl, keepListeners){
31548         if(this.loadMask){
31549             this.loadMask.destroy();
31550         }
31551         var c = this.container;
31552         c.removeAllListeners();
31553         this.view.destroy();
31554         this.colModel.purgeListeners();
31555         if(!keepListeners){
31556             this.purgeListeners();
31557         }
31558         c.update("");
31559         if(removeEl === true){
31560             c.remove();
31561         }
31562     },
31563
31564     // private
31565     processEvent : function(name, e){
31566         this.fireEvent(name, e);
31567         var t = e.getTarget();
31568         var v = this.view;
31569         var header = v.findHeaderIndex(t);
31570         if(header !== false){
31571             this.fireEvent("header" + name, this, header, e);
31572         }else{
31573             var row = v.findRowIndex(t);
31574             var cell = v.findCellIndex(t);
31575             if(row !== false){
31576                 this.fireEvent("row" + name, this, row, e);
31577                 if(cell !== false){
31578                     this.fireEvent("cell" + name, this, row, cell, e);
31579                 }
31580             }
31581         }
31582     },
31583
31584     // private
31585     onClick : function(e){
31586         this.processEvent("click", e);
31587     },
31588
31589     // private
31590     onContextMenu : function(e, t){
31591         this.processEvent("contextmenu", e);
31592     },
31593
31594     // private
31595     onDblClick : function(e){
31596         this.processEvent("dblclick", e);
31597     },
31598
31599     // private
31600     walkCells : function(row, col, step, fn, scope){
31601         var cm = this.colModel, clen = cm.getColumnCount();
31602         var ds = this.dataSource, rlen = ds.getCount(), first = true;
31603         if(step < 0){
31604             if(col < 0){
31605                 row--;
31606                 first = false;
31607             }
31608             while(row >= 0){
31609                 if(!first){
31610                     col = clen-1;
31611                 }
31612                 first = false;
31613                 while(col >= 0){
31614                     if(fn.call(scope || this, row, col, cm) === true){
31615                         return [row, col];
31616                     }
31617                     col--;
31618                 }
31619                 row--;
31620             }
31621         } else {
31622             if(col >= clen){
31623                 row++;
31624                 first = false;
31625             }
31626             while(row < rlen){
31627                 if(!first){
31628                     col = 0;
31629                 }
31630                 first = false;
31631                 while(col < clen){
31632                     if(fn.call(scope || this, row, col, cm) === true){
31633                         return [row, col];
31634                     }
31635                     col++;
31636                 }
31637                 row++;
31638             }
31639         }
31640         return null;
31641     },
31642
31643     // private
31644     getSelections : function(){
31645         return this.selModel.getSelections();
31646     },
31647
31648     /**
31649      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
31650      * but if manual update is required this method will initiate it.
31651      */
31652     autoSize : function(){
31653         if(this.rendered){
31654             this.view.layout();
31655             if(this.view.adjustForScroll){
31656                 this.view.adjustForScroll();
31657             }
31658         }
31659     },
31660
31661     /**
31662      * Returns the grid's underlying element.
31663      * @return {Element} The element
31664      */
31665     getGridEl : function(){
31666         return this.container;
31667     },
31668
31669     // private for compatibility, overridden by editor grid
31670     stopEditing : function(){},
31671
31672     /**
31673      * Returns the grid's SelectionModel.
31674      * @return {SelectionModel}
31675      */
31676     getSelectionModel : function(){
31677         if(!this.selModel){
31678             this.selModel = new Roo.grid.RowSelectionModel();
31679         }
31680         return this.selModel;
31681     },
31682
31683     /**
31684      * Returns the grid's DataSource.
31685      * @return {DataSource}
31686      */
31687     getDataSource : function(){
31688         return this.dataSource;
31689     },
31690
31691     /**
31692      * Returns the grid's ColumnModel.
31693      * @return {ColumnModel}
31694      */
31695     getColumnModel : function(){
31696         return this.colModel;
31697     },
31698
31699     /**
31700      * Returns the grid's GridView object.
31701      * @return {GridView}
31702      */
31703     getView : function(){
31704         if(!this.view){
31705             this.view = new Roo.grid.GridView(this.viewConfig);
31706         }
31707         return this.view;
31708     },
31709     /**
31710      * Called to get grid's drag proxy text, by default returns this.ddText.
31711      * @return {String}
31712      */
31713     getDragDropText : function(){
31714         var count = this.selModel.getCount();
31715         return String.format(this.ddText, count, count == 1 ? '' : 's');
31716     }
31717 });
31718 /**
31719  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
31720  * %0 is replaced with the number of selected rows.
31721  * @type String
31722  */
31723 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
31724  * Based on:
31725  * Ext JS Library 1.1.1
31726  * Copyright(c) 2006-2007, Ext JS, LLC.
31727  *
31728  * Originally Released Under LGPL - original licence link has changed is not relivant.
31729  *
31730  * Fork - LGPL
31731  * <script type="text/javascript">
31732  */
31733  
31734 Roo.grid.AbstractGridView = function(){
31735         this.grid = null;
31736         
31737         this.events = {
31738             "beforerowremoved" : true,
31739             "beforerowsinserted" : true,
31740             "beforerefresh" : true,
31741             "rowremoved" : true,
31742             "rowsinserted" : true,
31743             "rowupdated" : true,
31744             "refresh" : true
31745         };
31746     Roo.grid.AbstractGridView.superclass.constructor.call(this);
31747 };
31748
31749 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
31750     rowClass : "x-grid-row",
31751     cellClass : "x-grid-cell",
31752     tdClass : "x-grid-td",
31753     hdClass : "x-grid-hd",
31754     splitClass : "x-grid-hd-split",
31755     
31756         init: function(grid){
31757         this.grid = grid;
31758                 var cid = this.grid.getGridEl().id;
31759         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
31760         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
31761         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
31762         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
31763         },
31764         
31765         getColumnRenderers : function(){
31766         var renderers = [];
31767         var cm = this.grid.colModel;
31768         var colCount = cm.getColumnCount();
31769         for(var i = 0; i < colCount; i++){
31770             renderers[i] = cm.getRenderer(i);
31771         }
31772         return renderers;
31773     },
31774     
31775     getColumnIds : function(){
31776         var ids = [];
31777         var cm = this.grid.colModel;
31778         var colCount = cm.getColumnCount();
31779         for(var i = 0; i < colCount; i++){
31780             ids[i] = cm.getColumnId(i);
31781         }
31782         return ids;
31783     },
31784     
31785     getDataIndexes : function(){
31786         if(!this.indexMap){
31787             this.indexMap = this.buildIndexMap();
31788         }
31789         return this.indexMap.colToData;
31790     },
31791     
31792     getColumnIndexByDataIndex : function(dataIndex){
31793         if(!this.indexMap){
31794             this.indexMap = this.buildIndexMap();
31795         }
31796         return this.indexMap.dataToCol[dataIndex];
31797     },
31798     
31799     /**
31800      * Set a css style for a column dynamically. 
31801      * @param {Number} colIndex The index of the column
31802      * @param {String} name The css property name
31803      * @param {String} value The css value
31804      */
31805     setCSSStyle : function(colIndex, name, value){
31806         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
31807         Roo.util.CSS.updateRule(selector, name, value);
31808     },
31809     
31810     generateRules : function(cm){
31811         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
31812         Roo.util.CSS.removeStyleSheet(rulesId);
31813         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
31814             var cid = cm.getColumnId(i);
31815             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
31816                          this.tdSelector, cid, " {\n}\n",
31817                          this.hdSelector, cid, " {\n}\n",
31818                          this.splitSelector, cid, " {\n}\n");
31819         }
31820         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
31821     }
31822 });/*
31823  * Based on:
31824  * Ext JS Library 1.1.1
31825  * Copyright(c) 2006-2007, Ext JS, LLC.
31826  *
31827  * Originally Released Under LGPL - original licence link has changed is not relivant.
31828  *
31829  * Fork - LGPL
31830  * <script type="text/javascript">
31831  */
31832
31833 // private
31834 // This is a support class used internally by the Grid components
31835 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
31836     this.grid = grid;
31837     this.view = grid.getView();
31838     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31839     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
31840     if(hd2){
31841         this.setHandleElId(Roo.id(hd));
31842         this.setOuterHandleElId(Roo.id(hd2));
31843     }
31844     this.scroll = false;
31845 };
31846 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
31847     maxDragWidth: 120,
31848     getDragData : function(e){
31849         var t = Roo.lib.Event.getTarget(e);
31850         var h = this.view.findHeaderCell(t);
31851         if(h){
31852             return {ddel: h.firstChild, header:h};
31853         }
31854         return false;
31855     },
31856
31857     onInitDrag : function(e){
31858         this.view.headersDisabled = true;
31859         var clone = this.dragData.ddel.cloneNode(true);
31860         clone.id = Roo.id();
31861         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
31862         this.proxy.update(clone);
31863         return true;
31864     },
31865
31866     afterValidDrop : function(){
31867         var v = this.view;
31868         setTimeout(function(){
31869             v.headersDisabled = false;
31870         }, 50);
31871     },
31872
31873     afterInvalidDrop : function(){
31874         var v = this.view;
31875         setTimeout(function(){
31876             v.headersDisabled = false;
31877         }, 50);
31878     }
31879 });
31880 /*
31881  * Based on:
31882  * Ext JS Library 1.1.1
31883  * Copyright(c) 2006-2007, Ext JS, LLC.
31884  *
31885  * Originally Released Under LGPL - original licence link has changed is not relivant.
31886  *
31887  * Fork - LGPL
31888  * <script type="text/javascript">
31889  */
31890 // private
31891 // This is a support class used internally by the Grid components
31892 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
31893     this.grid = grid;
31894     this.view = grid.getView();
31895     // split the proxies so they don't interfere with mouse events
31896     this.proxyTop = Roo.DomHelper.append(document.body, {
31897         cls:"col-move-top", html:"&#160;"
31898     }, true);
31899     this.proxyBottom = Roo.DomHelper.append(document.body, {
31900         cls:"col-move-bottom", html:"&#160;"
31901     }, true);
31902     this.proxyTop.hide = this.proxyBottom.hide = function(){
31903         this.setLeftTop(-100,-100);
31904         this.setStyle("visibility", "hidden");
31905     };
31906     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
31907     // temporarily disabled
31908     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
31909     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
31910 };
31911 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
31912     proxyOffsets : [-4, -9],
31913     fly: Roo.Element.fly,
31914
31915     getTargetFromEvent : function(e){
31916         var t = Roo.lib.Event.getTarget(e);
31917         var cindex = this.view.findCellIndex(t);
31918         if(cindex !== false){
31919             return this.view.getHeaderCell(cindex);
31920         }
31921     },
31922
31923     nextVisible : function(h){
31924         var v = this.view, cm = this.grid.colModel;
31925         h = h.nextSibling;
31926         while(h){
31927             if(!cm.isHidden(v.getCellIndex(h))){
31928                 return h;
31929             }
31930             h = h.nextSibling;
31931         }
31932         return null;
31933     },
31934
31935     prevVisible : function(h){
31936         var v = this.view, cm = this.grid.colModel;
31937         h = h.prevSibling;
31938         while(h){
31939             if(!cm.isHidden(v.getCellIndex(h))){
31940                 return h;
31941             }
31942             h = h.prevSibling;
31943         }
31944         return null;
31945     },
31946
31947     positionIndicator : function(h, n, e){
31948         var x = Roo.lib.Event.getPageX(e);
31949         var r = Roo.lib.Dom.getRegion(n.firstChild);
31950         var px, pt, py = r.top + this.proxyOffsets[1];
31951         if((r.right - x) <= (r.right-r.left)/2){
31952             px = r.right+this.view.borderWidth;
31953             pt = "after";
31954         }else{
31955             px = r.left;
31956             pt = "before";
31957         }
31958         var oldIndex = this.view.getCellIndex(h);
31959         var newIndex = this.view.getCellIndex(n);
31960
31961         if(this.grid.colModel.isFixed(newIndex)){
31962             return false;
31963         }
31964
31965         var locked = this.grid.colModel.isLocked(newIndex);
31966
31967         if(pt == "after"){
31968             newIndex++;
31969         }
31970         if(oldIndex < newIndex){
31971             newIndex--;
31972         }
31973         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
31974             return false;
31975         }
31976         px +=  this.proxyOffsets[0];
31977         this.proxyTop.setLeftTop(px, py);
31978         this.proxyTop.show();
31979         if(!this.bottomOffset){
31980             this.bottomOffset = this.view.mainHd.getHeight();
31981         }
31982         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
31983         this.proxyBottom.show();
31984         return pt;
31985     },
31986
31987     onNodeEnter : function(n, dd, e, data){
31988         if(data.header != n){
31989             this.positionIndicator(data.header, n, e);
31990         }
31991     },
31992
31993     onNodeOver : function(n, dd, e, data){
31994         var result = false;
31995         if(data.header != n){
31996             result = this.positionIndicator(data.header, n, e);
31997         }
31998         if(!result){
31999             this.proxyTop.hide();
32000             this.proxyBottom.hide();
32001         }
32002         return result ? this.dropAllowed : this.dropNotAllowed;
32003     },
32004
32005     onNodeOut : function(n, dd, e, data){
32006         this.proxyTop.hide();
32007         this.proxyBottom.hide();
32008     },
32009
32010     onNodeDrop : function(n, dd, e, data){
32011         var h = data.header;
32012         if(h != n){
32013             var cm = this.grid.colModel;
32014             var x = Roo.lib.Event.getPageX(e);
32015             var r = Roo.lib.Dom.getRegion(n.firstChild);
32016             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32017             var oldIndex = this.view.getCellIndex(h);
32018             var newIndex = this.view.getCellIndex(n);
32019             var locked = cm.isLocked(newIndex);
32020             if(pt == "after"){
32021                 newIndex++;
32022             }
32023             if(oldIndex < newIndex){
32024                 newIndex--;
32025             }
32026             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32027                 return false;
32028             }
32029             cm.setLocked(oldIndex, locked, true);
32030             cm.moveColumn(oldIndex, newIndex);
32031             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32032             return true;
32033         }
32034         return false;
32035     }
32036 });
32037 /*
32038  * Based on:
32039  * Ext JS Library 1.1.1
32040  * Copyright(c) 2006-2007, Ext JS, LLC.
32041  *
32042  * Originally Released Under LGPL - original licence link has changed is not relivant.
32043  *
32044  * Fork - LGPL
32045  * <script type="text/javascript">
32046  */
32047   
32048 /**
32049  * @class Roo.grid.GridView
32050  * @extends Roo.util.Observable
32051  *
32052  * @constructor
32053  * @param {Object} config
32054  */
32055 Roo.grid.GridView = function(config){
32056     Roo.grid.GridView.superclass.constructor.call(this);
32057     this.el = null;
32058
32059     Roo.apply(this, config);
32060 };
32061
32062 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32063
32064     /**
32065      * Override this function to apply custom css classes to rows during rendering
32066      * @param {Record} record The record
32067      * @param {Number} index
32068      * @method getRowClass
32069      */
32070     rowClass : "x-grid-row",
32071
32072     cellClass : "x-grid-col",
32073
32074     tdClass : "x-grid-td",
32075
32076     hdClass : "x-grid-hd",
32077
32078     splitClass : "x-grid-split",
32079
32080     sortClasses : ["sort-asc", "sort-desc"],
32081
32082     enableMoveAnim : false,
32083
32084     hlColor: "C3DAF9",
32085
32086     dh : Roo.DomHelper,
32087
32088     fly : Roo.Element.fly,
32089
32090     css : Roo.util.CSS,
32091
32092     borderWidth: 1,
32093
32094     splitOffset: 3,
32095
32096     scrollIncrement : 22,
32097
32098     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32099
32100     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32101
32102     bind : function(ds, cm){
32103         if(this.ds){
32104             this.ds.un("load", this.onLoad, this);
32105             this.ds.un("datachanged", this.onDataChange, this);
32106             this.ds.un("add", this.onAdd, this);
32107             this.ds.un("remove", this.onRemove, this);
32108             this.ds.un("update", this.onUpdate, this);
32109             this.ds.un("clear", this.onClear, this);
32110         }
32111         if(ds){
32112             ds.on("load", this.onLoad, this);
32113             ds.on("datachanged", this.onDataChange, this);
32114             ds.on("add", this.onAdd, this);
32115             ds.on("remove", this.onRemove, this);
32116             ds.on("update", this.onUpdate, this);
32117             ds.on("clear", this.onClear, this);
32118         }
32119         this.ds = ds;
32120
32121         if(this.cm){
32122             this.cm.un("widthchange", this.onColWidthChange, this);
32123             this.cm.un("headerchange", this.onHeaderChange, this);
32124             this.cm.un("hiddenchange", this.onHiddenChange, this);
32125             this.cm.un("columnmoved", this.onColumnMove, this);
32126             this.cm.un("columnlockchange", this.onColumnLock, this);
32127         }
32128         if(cm){
32129             this.generateRules(cm);
32130             cm.on("widthchange", this.onColWidthChange, this);
32131             cm.on("headerchange", this.onHeaderChange, this);
32132             cm.on("hiddenchange", this.onHiddenChange, this);
32133             cm.on("columnmoved", this.onColumnMove, this);
32134             cm.on("columnlockchange", this.onColumnLock, this);
32135         }
32136         this.cm = cm;
32137     },
32138
32139     init: function(grid){
32140                 Roo.grid.GridView.superclass.init.call(this, grid);
32141
32142                 this.bind(grid.dataSource, grid.colModel);
32143
32144             grid.on("headerclick", this.handleHeaderClick, this);
32145
32146         if(grid.trackMouseOver){
32147             grid.on("mouseover", this.onRowOver, this);
32148                 grid.on("mouseout", this.onRowOut, this);
32149             }
32150             grid.cancelTextSelection = function(){};
32151                 this.gridId = grid.id;
32152
32153                 var tpls = this.templates || {};
32154
32155                 if(!tpls.master){
32156                     tpls.master = new Roo.Template(
32157                        '<div class="x-grid" hidefocus="true">',
32158                           '<div class="x-grid-topbar"></div>',
32159                           '<div class="x-grid-scroller"><div></div></div>',
32160                           '<div class="x-grid-locked">',
32161                               '<div class="x-grid-header">{lockedHeader}</div>',
32162                               '<div class="x-grid-body">{lockedBody}</div>',
32163                           "</div>",
32164                           '<div class="x-grid-viewport">',
32165                               '<div class="x-grid-header">{header}</div>',
32166                               '<div class="x-grid-body">{body}</div>',
32167                           "</div>",
32168                           '<div class="x-grid-bottombar"></div>',
32169                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32170                           '<div class="x-grid-resize-proxy">&#160;</div>',
32171                        "</div>"
32172                     );
32173                     tpls.master.disableformats = true;
32174                 }
32175
32176                 if(!tpls.header){
32177                     tpls.header = new Roo.Template(
32178                        '<table border="0" cellspacing="0" cellpadding="0">',
32179                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32180                        "</table>{splits}"
32181                     );
32182                     tpls.header.disableformats = true;
32183                 }
32184                 tpls.header.compile();
32185
32186                 if(!tpls.hcell){
32187                     tpls.hcell = new Roo.Template(
32188                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32189                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32190                         "</div></td>"
32191                      );
32192                      tpls.hcell.disableFormats = true;
32193                 }
32194                 tpls.hcell.compile();
32195
32196                 if(!tpls.hsplit){
32197                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32198                     tpls.hsplit.disableFormats = true;
32199                 }
32200                 tpls.hsplit.compile();
32201
32202                 if(!tpls.body){
32203                     tpls.body = new Roo.Template(
32204                        '<table border="0" cellspacing="0" cellpadding="0">',
32205                        "<tbody>{rows}</tbody>",
32206                        "</table>"
32207                     );
32208                     tpls.body.disableFormats = true;
32209                 }
32210                 tpls.body.compile();
32211
32212                 if(!tpls.row){
32213                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32214                     tpls.row.disableFormats = true;
32215                 }
32216                 tpls.row.compile();
32217
32218                 if(!tpls.cell){
32219                     tpls.cell = new Roo.Template(
32220                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32221                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32222                         "</td>"
32223                     );
32224             tpls.cell.disableFormats = true;
32225         }
32226                 tpls.cell.compile();
32227
32228                 this.templates = tpls;
32229         },
32230
32231         // remap these for backwards compat
32232     onColWidthChange : function(){
32233         this.updateColumns.apply(this, arguments);
32234     },
32235     onHeaderChange : function(){
32236         this.updateHeaders.apply(this, arguments);
32237     }, 
32238     onHiddenChange : function(){
32239         this.handleHiddenChange.apply(this, arguments);
32240     },
32241     onColumnMove : function(){
32242         this.handleColumnMove.apply(this, arguments);
32243     },
32244     onColumnLock : function(){
32245         this.handleLockChange.apply(this, arguments);
32246     },
32247
32248     onDataChange : function(){
32249         this.refresh();
32250         this.updateHeaderSortState();
32251     },
32252
32253         onClear : function(){
32254         this.refresh();
32255     },
32256
32257         onUpdate : function(ds, record){
32258         this.refreshRow(record);
32259     },
32260
32261     refreshRow : function(record){
32262         var ds = this.ds, index;
32263         if(typeof record == 'number'){
32264             index = record;
32265             record = ds.getAt(index);
32266         }else{
32267             index = ds.indexOf(record);
32268         }
32269         this.insertRows(ds, index, index, true);
32270         this.onRemove(ds, record, index+1, true);
32271         this.syncRowHeights(index, index);
32272         this.layout();
32273         this.fireEvent("rowupdated", this, index, record);
32274     },
32275
32276     onAdd : function(ds, records, index){
32277         this.insertRows(ds, index, index + (records.length-1));
32278     },
32279
32280     onRemove : function(ds, record, index, isUpdate){
32281         if(isUpdate !== true){
32282             this.fireEvent("beforerowremoved", this, index, record);
32283         }
32284         var bt = this.getBodyTable(), lt = this.getLockedTable();
32285         if(bt.rows[index]){
32286             bt.firstChild.removeChild(bt.rows[index]);
32287         }
32288         if(lt.rows[index]){
32289             lt.firstChild.removeChild(lt.rows[index]);
32290         }
32291         if(isUpdate !== true){
32292             this.stripeRows(index);
32293             this.syncRowHeights(index, index);
32294             this.layout();
32295             this.fireEvent("rowremoved", this, index, record);
32296         }
32297     },
32298
32299     onLoad : function(){
32300         this.scrollToTop();
32301     },
32302
32303     /**
32304      * Scrolls the grid to the top
32305      */
32306     scrollToTop : function(){
32307         if(this.scroller){
32308             this.scroller.dom.scrollTop = 0;
32309             this.syncScroll();
32310         }
32311     },
32312
32313     /**
32314      * Gets a panel in the header of the grid that can be used for toolbars etc.
32315      * After modifying the contents of this panel a call to grid.autoSize() may be
32316      * required to register any changes in size.
32317      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32318      * @return Roo.Element
32319      */
32320     getHeaderPanel : function(doShow){
32321         if(doShow){
32322             this.headerPanel.show();
32323         }
32324         return this.headerPanel;
32325         },
32326
32327         /**
32328      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32329      * After modifying the contents of this panel a call to grid.autoSize() may be
32330      * required to register any changes in size.
32331      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32332      * @return Roo.Element
32333      */
32334     getFooterPanel : function(doShow){
32335         if(doShow){
32336             this.footerPanel.show();
32337         }
32338         return this.footerPanel;
32339         },
32340
32341         initElements : function(){
32342             var E = Roo.Element;
32343             var el = this.grid.getGridEl().dom.firstChild;
32344             var cs = el.childNodes;
32345
32346             this.el = new E(el);
32347             this.headerPanel = new E(el.firstChild);
32348             this.headerPanel.enableDisplayMode("block");
32349
32350         this.scroller = new E(cs[1]);
32351             this.scrollSizer = new E(this.scroller.dom.firstChild);
32352
32353             this.lockedWrap = new E(cs[2]);
32354             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
32355             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
32356
32357             this.mainWrap = new E(cs[3]);
32358             this.mainHd = new E(this.mainWrap.dom.firstChild);
32359             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
32360
32361             this.footerPanel = new E(cs[4]);
32362             this.footerPanel.enableDisplayMode("block");
32363
32364         this.focusEl = new E(cs[5]);
32365         this.focusEl.swallowEvent("click", true);
32366         this.resizeProxy = new E(cs[6]);
32367
32368             this.headerSelector = String.format(
32369                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
32370                this.lockedHd.id, this.mainHd.id
32371             );
32372
32373             this.splitterSelector = String.format(
32374                '#{0} div.x-grid-split, #{1} div.x-grid-split',
32375                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
32376             );
32377     },
32378     idToCssName : function(s)
32379     {
32380         return s.replace(/[^a-z0-9]+/ig, '-');
32381     },
32382
32383         getHeaderCell : function(index){
32384             return Roo.DomQuery.select(this.headerSelector)[index];
32385         },
32386
32387         getHeaderCellMeasure : function(index){
32388             return this.getHeaderCell(index).firstChild;
32389         },
32390
32391         getHeaderCellText : function(index){
32392             return this.getHeaderCell(index).firstChild.firstChild;
32393         },
32394
32395         getLockedTable : function(){
32396             return this.lockedBody.dom.firstChild;
32397         },
32398
32399         getBodyTable : function(){
32400             return this.mainBody.dom.firstChild;
32401         },
32402
32403         getLockedRow : function(index){
32404             return this.getLockedTable().rows[index];
32405         },
32406
32407         getRow : function(index){
32408             return this.getBodyTable().rows[index];
32409         },
32410
32411         getRowComposite : function(index){
32412             if(!this.rowEl){
32413                 this.rowEl = new Roo.CompositeElementLite();
32414             }
32415         var els = [], lrow, mrow;
32416         if(lrow = this.getLockedRow(index)){
32417             els.push(lrow);
32418         }
32419         if(mrow = this.getRow(index)){
32420             els.push(mrow);
32421         }
32422         this.rowEl.elements = els;
32423             return this.rowEl;
32424         },
32425
32426         getCell : function(rowIndex, colIndex){
32427             var locked = this.cm.getLockedCount();
32428             var source;
32429             if(colIndex < locked){
32430                 source = this.lockedBody.dom.firstChild;
32431             }else{
32432                 source = this.mainBody.dom.firstChild;
32433                 colIndex -= locked;
32434             }
32435         return source.rows[rowIndex].childNodes[colIndex];
32436         },
32437
32438         getCellText : function(rowIndex, colIndex){
32439             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
32440         },
32441
32442         getCellBox : function(cell){
32443             var b = this.fly(cell).getBox();
32444         if(Roo.isOpera){ // opera fails to report the Y
32445             b.y = cell.offsetTop + this.mainBody.getY();
32446         }
32447         return b;
32448     },
32449
32450     getCellIndex : function(cell){
32451         var id = String(cell.className).match(this.cellRE);
32452         if(id){
32453             return parseInt(id[1], 10);
32454         }
32455         return 0;
32456     },
32457
32458     findHeaderIndex : function(n){
32459         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32460         return r ? this.getCellIndex(r) : false;
32461     },
32462
32463     findHeaderCell : function(n){
32464         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
32465         return r ? r : false;
32466     },
32467
32468     findRowIndex : function(n){
32469         if(!n){
32470             return false;
32471         }
32472         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
32473         return r ? r.rowIndex : false;
32474     },
32475
32476     findCellIndex : function(node){
32477         var stop = this.el.dom;
32478         while(node && node != stop){
32479             if(this.findRE.test(node.className)){
32480                 return this.getCellIndex(node);
32481             }
32482             node = node.parentNode;
32483         }
32484         return false;
32485     },
32486
32487     getColumnId : function(index){
32488             return this.cm.getColumnId(index);
32489         },
32490
32491         getSplitters : function(){
32492             if(this.splitterSelector){
32493                return Roo.DomQuery.select(this.splitterSelector);
32494             }else{
32495                 return null;
32496             }
32497         },
32498
32499         getSplitter : function(index){
32500             return this.getSplitters()[index];
32501         },
32502
32503     onRowOver : function(e, t){
32504         var row;
32505         if((row = this.findRowIndex(t)) !== false){
32506             this.getRowComposite(row).addClass("x-grid-row-over");
32507         }
32508     },
32509
32510     onRowOut : function(e, t){
32511         var row;
32512         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
32513             this.getRowComposite(row).removeClass("x-grid-row-over");
32514         }
32515     },
32516
32517     renderHeaders : function(){
32518             var cm = this.cm;
32519         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
32520         var cb = [], lb = [], sb = [], lsb = [], p = {};
32521         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32522             p.cellId = "x-grid-hd-0-" + i;
32523             p.splitId = "x-grid-csplit-0-" + i;
32524             p.id = cm.getColumnId(i);
32525             p.title = cm.getColumnTooltip(i) || "";
32526             p.value = cm.getColumnHeader(i) || "";
32527             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
32528             if(!cm.isLocked(i)){
32529                 cb[cb.length] = ct.apply(p);
32530                 sb[sb.length] = st.apply(p);
32531             }else{
32532                 lb[lb.length] = ct.apply(p);
32533                 lsb[lsb.length] = st.apply(p);
32534             }
32535         }
32536         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
32537                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
32538         },
32539
32540         updateHeaders : function(){
32541         var html = this.renderHeaders();
32542         this.lockedHd.update(html[0]);
32543         this.mainHd.update(html[1]);
32544     },
32545
32546     /**
32547      * Focuses the specified row.
32548      * @param {Number} row The row index
32549      */
32550     focusRow : function(row){
32551         var x = this.scroller.dom.scrollLeft;
32552         this.focusCell(row, 0, false);
32553         this.scroller.dom.scrollLeft = x;
32554     },
32555
32556     /**
32557      * Focuses the specified cell.
32558      * @param {Number} row The row index
32559      * @param {Number} col The column index
32560      * @param {Boolean} hscroll false to disable horizontal scrolling
32561      */
32562     focusCell : function(row, col, hscroll){
32563         var el = this.ensureVisible(row, col, hscroll);
32564         this.focusEl.alignTo(el, "tl-tl");
32565         if(Roo.isGecko){
32566             this.focusEl.focus();
32567         }else{
32568             this.focusEl.focus.defer(1, this.focusEl);
32569         }
32570     },
32571
32572     /**
32573      * Scrolls the specified cell into view
32574      * @param {Number} row The row index
32575      * @param {Number} col The column index
32576      * @param {Boolean} hscroll false to disable horizontal scrolling
32577      */
32578     ensureVisible : function(row, col, hscroll){
32579         if(typeof row != "number"){
32580             row = row.rowIndex;
32581         }
32582         if(row < 0 && row >= this.ds.getCount()){
32583             return;
32584         }
32585         col = (col !== undefined ? col : 0);
32586         var cm = this.grid.colModel;
32587         while(cm.isHidden(col)){
32588             col++;
32589         }
32590
32591         var el = this.getCell(row, col);
32592         if(!el){
32593             return;
32594         }
32595         var c = this.scroller.dom;
32596
32597         var ctop = parseInt(el.offsetTop, 10);
32598         var cleft = parseInt(el.offsetLeft, 10);
32599         var cbot = ctop + el.offsetHeight;
32600         var cright = cleft + el.offsetWidth;
32601
32602         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
32603         var stop = parseInt(c.scrollTop, 10);
32604         var sleft = parseInt(c.scrollLeft, 10);
32605         var sbot = stop + ch;
32606         var sright = sleft + c.clientWidth;
32607
32608         if(ctop < stop){
32609                 c.scrollTop = ctop;
32610         }else if(cbot > sbot){
32611             c.scrollTop = cbot-ch;
32612         }
32613
32614         if(hscroll !== false){
32615             if(cleft < sleft){
32616                 c.scrollLeft = cleft;
32617             }else if(cright > sright){
32618                 c.scrollLeft = cright-c.clientWidth;
32619             }
32620         }
32621         return el;
32622     },
32623
32624     updateColumns : function(){
32625         this.grid.stopEditing();
32626         var cm = this.grid.colModel, colIds = this.getColumnIds();
32627         //var totalWidth = cm.getTotalWidth();
32628         var pos = 0;
32629         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32630             //if(cm.isHidden(i)) continue;
32631             var w = cm.getColumnWidth(i);
32632             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32633             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
32634         }
32635         this.updateSplitters();
32636     },
32637
32638     generateRules : function(cm){
32639         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
32640         Roo.util.CSS.removeStyleSheet(rulesId);
32641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32642             var cid = cm.getColumnId(i);
32643             var align = '';
32644             if(cm.config[i].align){
32645                 align = 'text-align:'+cm.config[i].align+';';
32646             }
32647             var hidden = '';
32648             if(cm.isHidden(i)){
32649                 hidden = 'display:none;';
32650             }
32651             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
32652             ruleBuf.push(
32653                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
32654                     this.hdSelector, cid, " {\n", align, width, "}\n",
32655                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
32656                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
32657         }
32658         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32659     },
32660
32661     updateSplitters : function(){
32662         var cm = this.cm, s = this.getSplitters();
32663         if(s){ // splitters not created yet
32664             var pos = 0, locked = true;
32665             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32666                 if(cm.isHidden(i)) continue;
32667                 var w = cm.getColumnWidth(i);
32668                 if(!cm.isLocked(i) && locked){
32669                     pos = 0;
32670                     locked = false;
32671                 }
32672                 pos += w;
32673                 s[i].style.left = (pos-this.splitOffset) + "px";
32674             }
32675         }
32676     },
32677
32678     handleHiddenChange : function(colModel, colIndex, hidden){
32679         if(hidden){
32680             this.hideColumn(colIndex);
32681         }else{
32682             this.unhideColumn(colIndex);
32683         }
32684     },
32685
32686     hideColumn : function(colIndex){
32687         var cid = this.getColumnId(colIndex);
32688         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
32689         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
32690         if(Roo.isSafari){
32691             this.updateHeaders();
32692         }
32693         this.updateSplitters();
32694         this.layout();
32695     },
32696
32697     unhideColumn : function(colIndex){
32698         var cid = this.getColumnId(colIndex);
32699         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
32700         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
32701
32702         if(Roo.isSafari){
32703             this.updateHeaders();
32704         }
32705         this.updateSplitters();
32706         this.layout();
32707     },
32708
32709     insertRows : function(dm, firstRow, lastRow, isUpdate){
32710         if(firstRow == 0 && lastRow == dm.getCount()-1){
32711             this.refresh();
32712         }else{
32713             if(!isUpdate){
32714                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
32715             }
32716             var s = this.getScrollState();
32717             var markup = this.renderRows(firstRow, lastRow);
32718             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
32719             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
32720             this.restoreScroll(s);
32721             if(!isUpdate){
32722                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
32723                 this.syncRowHeights(firstRow, lastRow);
32724                 this.stripeRows(firstRow);
32725                 this.layout();
32726             }
32727         }
32728     },
32729
32730     bufferRows : function(markup, target, index){
32731         var before = null, trows = target.rows, tbody = target.tBodies[0];
32732         if(index < trows.length){
32733             before = trows[index];
32734         }
32735         var b = document.createElement("div");
32736         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
32737         var rows = b.firstChild.rows;
32738         for(var i = 0, len = rows.length; i < len; i++){
32739             if(before){
32740                 tbody.insertBefore(rows[0], before);
32741             }else{
32742                 tbody.appendChild(rows[0]);
32743             }
32744         }
32745         b.innerHTML = "";
32746         b = null;
32747     },
32748
32749     deleteRows : function(dm, firstRow, lastRow){
32750         if(dm.getRowCount()<1){
32751             this.fireEvent("beforerefresh", this);
32752             this.mainBody.update("");
32753             this.lockedBody.update("");
32754             this.fireEvent("refresh", this);
32755         }else{
32756             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
32757             var bt = this.getBodyTable();
32758             var tbody = bt.firstChild;
32759             var rows = bt.rows;
32760             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
32761                 tbody.removeChild(rows[firstRow]);
32762             }
32763             this.stripeRows(firstRow);
32764             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
32765         }
32766     },
32767
32768     updateRows : function(dataSource, firstRow, lastRow){
32769         var s = this.getScrollState();
32770         this.refresh();
32771         this.restoreScroll(s);
32772     },
32773
32774     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
32775         if(!noRefresh){
32776            this.refresh();
32777         }
32778         this.updateHeaderSortState();
32779     },
32780
32781     getScrollState : function(){
32782         var sb = this.scroller.dom;
32783         return {left: sb.scrollLeft, top: sb.scrollTop};
32784     },
32785
32786     stripeRows : function(startRow){
32787         if(!this.grid.stripeRows || this.ds.getCount() < 1){
32788             return;
32789         }
32790         startRow = startRow || 0;
32791         var rows = this.getBodyTable().rows;
32792         var lrows = this.getLockedTable().rows;
32793         var cls = ' x-grid-row-alt ';
32794         for(var i = startRow, len = rows.length; i < len; i++){
32795             var row = rows[i], lrow = lrows[i];
32796             var isAlt = ((i+1) % 2 == 0);
32797             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
32798             if(isAlt == hasAlt){
32799                 continue;
32800             }
32801             if(isAlt){
32802                 row.className += " x-grid-row-alt";
32803             }else{
32804                 row.className = row.className.replace("x-grid-row-alt", "");
32805             }
32806             if(lrow){
32807                 lrow.className = row.className;
32808             }
32809         }
32810     },
32811
32812     restoreScroll : function(state){
32813         var sb = this.scroller.dom;
32814         sb.scrollLeft = state.left;
32815         sb.scrollTop = state.top;
32816         this.syncScroll();
32817     },
32818
32819     syncScroll : function(){
32820         var sb = this.scroller.dom;
32821         var sh = this.mainHd.dom;
32822         var bs = this.mainBody.dom;
32823         var lv = this.lockedBody.dom;
32824         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
32825         lv.scrollTop = bs.scrollTop = sb.scrollTop;
32826     },
32827
32828     handleScroll : function(e){
32829         this.syncScroll();
32830         var sb = this.scroller.dom;
32831         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
32832         e.stopEvent();
32833     },
32834
32835     handleWheel : function(e){
32836         var d = e.getWheelDelta();
32837         this.scroller.dom.scrollTop -= d*22;
32838         // set this here to prevent jumpy scrolling on large tables
32839         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
32840         e.stopEvent();
32841     },
32842
32843     renderRows : function(startRow, endRow){
32844         // pull in all the crap needed to render rows
32845         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
32846         var colCount = cm.getColumnCount();
32847
32848         if(ds.getCount() < 1){
32849             return ["", ""];
32850         }
32851
32852         // build a map for all the columns
32853         var cs = [];
32854         for(var i = 0; i < colCount; i++){
32855             var name = cm.getDataIndex(i);
32856             cs[i] = {
32857                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
32858                 renderer : cm.getRenderer(i),
32859                 id : cm.getColumnId(i),
32860                 locked : cm.isLocked(i)
32861             };
32862         }
32863
32864         startRow = startRow || 0;
32865         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
32866
32867         // records to render
32868         var rs = ds.getRange(startRow, endRow);
32869
32870         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
32871     },
32872
32873     // As much as I hate to duplicate code, this was branched because FireFox really hates
32874     // [].join("") on strings. The performance difference was substantial enough to
32875     // branch this function
32876     doRender : Roo.isGecko ?
32877             function(cs, rs, ds, startRow, colCount, stripe){
32878                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32879                 // buffers
32880                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32881                 for(var j = 0, len = rs.length; j < len; j++){
32882                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
32883                     for(var i = 0; i < colCount; i++){
32884                         c = cs[i];
32885                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32886                         p.id = c.id;
32887                         p.css = p.attr = "";
32888                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32889                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32890                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32891                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32892                         }
32893                         var markup = ct.apply(p);
32894                         if(!c.locked){
32895                             cb+= markup;
32896                         }else{
32897                             lcb+= markup;
32898                         }
32899                     }
32900                     var alt = [];
32901                     if(stripe && ((rowIndex+1) % 2 == 0)){
32902                         alt[0] = "x-grid-row-alt";
32903                     }
32904                     if(r.dirty){
32905                         alt[1] = " x-grid-dirty-row";
32906                     }
32907                     rp.cells = lcb;
32908                     if(this.getRowClass){
32909                         alt[2] = this.getRowClass(r, rowIndex);
32910                     }
32911                     rp.alt = alt.join(" ");
32912                     lbuf+= rt.apply(rp);
32913                     rp.cells = cb;
32914                     buf+=  rt.apply(rp);
32915                 }
32916                 return [lbuf, buf];
32917             } :
32918             function(cs, rs, ds, startRow, colCount, stripe){
32919                 var ts = this.templates, ct = ts.cell, rt = ts.row;
32920                 // buffers
32921                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
32922                 for(var j = 0, len = rs.length; j < len; j++){
32923                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
32924                     for(var i = 0; i < colCount; i++){
32925                         c = cs[i];
32926                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
32927                         p.id = c.id;
32928                         p.css = p.attr = "";
32929                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
32930                         if(p.value == undefined || p.value === "") p.value = "&#160;";
32931                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
32932                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
32933                         }
32934                         var markup = ct.apply(p);
32935                         if(!c.locked){
32936                             cb[cb.length] = markup;
32937                         }else{
32938                             lcb[lcb.length] = markup;
32939                         }
32940                     }
32941                     var alt = [];
32942                     if(stripe && ((rowIndex+1) % 2 == 0)){
32943                         alt[0] = "x-grid-row-alt";
32944                     }
32945                     if(r.dirty){
32946                         alt[1] = " x-grid-dirty-row";
32947                     }
32948                     rp.cells = lcb;
32949                     if(this.getRowClass){
32950                         alt[2] = this.getRowClass(r, rowIndex);
32951                     }
32952                     rp.alt = alt.join(" ");
32953                     rp.cells = lcb.join("");
32954                     lbuf[lbuf.length] = rt.apply(rp);
32955                     rp.cells = cb.join("");
32956                     buf[buf.length] =  rt.apply(rp);
32957                 }
32958                 return [lbuf.join(""), buf.join("")];
32959             },
32960
32961     renderBody : function(){
32962         var markup = this.renderRows();
32963         var bt = this.templates.body;
32964         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
32965     },
32966
32967     /**
32968      * Refreshes the grid
32969      * @param {Boolean} headersToo
32970      */
32971     refresh : function(headersToo){
32972         this.fireEvent("beforerefresh", this);
32973         this.grid.stopEditing();
32974         var result = this.renderBody();
32975         this.lockedBody.update(result[0]);
32976         this.mainBody.update(result[1]);
32977         if(headersToo === true){
32978             this.updateHeaders();
32979             this.updateColumns();
32980             this.updateSplitters();
32981             this.updateHeaderSortState();
32982         }
32983         this.syncRowHeights();
32984         this.layout();
32985         this.fireEvent("refresh", this);
32986     },
32987
32988     handleColumnMove : function(cm, oldIndex, newIndex){
32989         this.indexMap = null;
32990         var s = this.getScrollState();
32991         this.refresh(true);
32992         this.restoreScroll(s);
32993         this.afterMove(newIndex);
32994     },
32995
32996     afterMove : function(colIndex){
32997         if(this.enableMoveAnim && Roo.enableFx){
32998             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
32999         }
33000     },
33001
33002     updateCell : function(dm, rowIndex, dataIndex){
33003         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33004         if(typeof colIndex == "undefined"){ // not present in grid
33005             return;
33006         }
33007         var cm = this.grid.colModel;
33008         var cell = this.getCell(rowIndex, colIndex);
33009         var cellText = this.getCellText(rowIndex, colIndex);
33010
33011         var p = {
33012             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33013             id : cm.getColumnId(colIndex),
33014             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33015         };
33016         var renderer = cm.getRenderer(colIndex);
33017         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33018         if(typeof val == "undefined" || val === "") val = "&#160;";
33019         cellText.innerHTML = val;
33020         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33021         this.syncRowHeights(rowIndex, rowIndex);
33022     },
33023
33024     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33025         var maxWidth = 0;
33026         if(this.grid.autoSizeHeaders){
33027             var h = this.getHeaderCellMeasure(colIndex);
33028             maxWidth = Math.max(maxWidth, h.scrollWidth);
33029         }
33030         var tb, index;
33031         if(this.cm.isLocked(colIndex)){
33032             tb = this.getLockedTable();
33033             index = colIndex;
33034         }else{
33035             tb = this.getBodyTable();
33036             index = colIndex - this.cm.getLockedCount();
33037         }
33038         if(tb && tb.rows){
33039             var rows = tb.rows;
33040             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33041             for(var i = 0; i < stopIndex; i++){
33042                 var cell = rows[i].childNodes[index].firstChild;
33043                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33044             }
33045         }
33046         return maxWidth + /*margin for error in IE*/ 5;
33047     },
33048     /**
33049      * Autofit a column to its content.
33050      * @param {Number} colIndex
33051      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33052      */
33053      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33054          if(this.cm.isHidden(colIndex)){
33055              return; // can't calc a hidden column
33056          }
33057         if(forceMinSize){
33058             var cid = this.cm.getColumnId(colIndex);
33059             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33060            if(this.grid.autoSizeHeaders){
33061                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33062            }
33063         }
33064         var newWidth = this.calcColumnWidth(colIndex);
33065         this.cm.setColumnWidth(colIndex,
33066             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33067         if(!suppressEvent){
33068             this.grid.fireEvent("columnresize", colIndex, newWidth);
33069         }
33070     },
33071
33072     /**
33073      * Autofits all columns to their content and then expands to fit any extra space in the grid
33074      */
33075      autoSizeColumns : function(){
33076         var cm = this.grid.colModel;
33077         var colCount = cm.getColumnCount();
33078         for(var i = 0; i < colCount; i++){
33079             this.autoSizeColumn(i, true, true);
33080         }
33081         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33082             this.fitColumns();
33083         }else{
33084             this.updateColumns();
33085             this.layout();
33086         }
33087     },
33088
33089     /**
33090      * Autofits all columns to the grid's width proportionate with their current size
33091      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33092      */
33093     fitColumns : function(reserveScrollSpace){
33094         var cm = this.grid.colModel;
33095         var colCount = cm.getColumnCount();
33096         var cols = [];
33097         var width = 0;
33098         var i, w;
33099         for (i = 0; i < colCount; i++){
33100             if(!cm.isHidden(i) && !cm.isFixed(i)){
33101                 w = cm.getColumnWidth(i);
33102                 cols.push(i);
33103                 cols.push(w);
33104                 width += w;
33105             }
33106         }
33107         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33108         if(reserveScrollSpace){
33109             avail -= 17;
33110         }
33111         var frac = (avail - cm.getTotalWidth())/width;
33112         while (cols.length){
33113             w = cols.pop();
33114             i = cols.pop();
33115             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33116         }
33117         this.updateColumns();
33118         this.layout();
33119     },
33120
33121     onRowSelect : function(rowIndex){
33122         var row = this.getRowComposite(rowIndex);
33123         row.addClass("x-grid-row-selected");
33124     },
33125
33126     onRowDeselect : function(rowIndex){
33127         var row = this.getRowComposite(rowIndex);
33128         row.removeClass("x-grid-row-selected");
33129     },
33130
33131     onCellSelect : function(row, col){
33132         var cell = this.getCell(row, col);
33133         if(cell){
33134             Roo.fly(cell).addClass("x-grid-cell-selected");
33135         }
33136     },
33137
33138     onCellDeselect : function(row, col){
33139         var cell = this.getCell(row, col);
33140         if(cell){
33141             Roo.fly(cell).removeClass("x-grid-cell-selected");
33142         }
33143     },
33144
33145     updateHeaderSortState : function(){
33146         var state = this.ds.getSortState();
33147         if(!state){
33148             return;
33149         }
33150         this.sortState = state;
33151         var sortColumn = this.cm.findColumnIndex(state.field);
33152         if(sortColumn != -1){
33153             var sortDir = state.direction;
33154             var sc = this.sortClasses;
33155             var hds = this.el.select(this.headerSelector).removeClass(sc);
33156             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33157         }
33158     },
33159
33160     handleHeaderClick : function(g, index){
33161         if(this.headersDisabled){
33162             return;
33163         }
33164         var dm = g.dataSource, cm = g.colModel;
33165             if(!cm.isSortable(index)){
33166             return;
33167         }
33168             g.stopEditing();
33169         dm.sort(cm.getDataIndex(index));
33170     },
33171
33172
33173     destroy : function(){
33174         if(this.colMenu){
33175             this.colMenu.removeAll();
33176             Roo.menu.MenuMgr.unregister(this.colMenu);
33177             this.colMenu.getEl().remove();
33178             delete this.colMenu;
33179         }
33180         if(this.hmenu){
33181             this.hmenu.removeAll();
33182             Roo.menu.MenuMgr.unregister(this.hmenu);
33183             this.hmenu.getEl().remove();
33184             delete this.hmenu;
33185         }
33186         if(this.grid.enableColumnMove){
33187             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33188             if(dds){
33189                 for(var dd in dds){
33190                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33191                         var elid = dds[dd].dragElId;
33192                         dds[dd].unreg();
33193                         Roo.get(elid).remove();
33194                     } else if(dds[dd].config.isTarget){
33195                         dds[dd].proxyTop.remove();
33196                         dds[dd].proxyBottom.remove();
33197                         dds[dd].unreg();
33198                     }
33199                     if(Roo.dd.DDM.locationCache[dd]){
33200                         delete Roo.dd.DDM.locationCache[dd];
33201                     }
33202                 }
33203                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33204             }
33205         }
33206         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33207         this.bind(null, null);
33208         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33209     },
33210
33211     handleLockChange : function(){
33212         this.refresh(true);
33213     },
33214
33215     onDenyColumnLock : function(){
33216
33217     },
33218
33219     onDenyColumnHide : function(){
33220
33221     },
33222
33223     handleHdMenuClick : function(item){
33224         var index = this.hdCtxIndex;
33225         var cm = this.cm, ds = this.ds;
33226         switch(item.id){
33227             case "asc":
33228                 ds.sort(cm.getDataIndex(index), "ASC");
33229                 break;
33230             case "desc":
33231                 ds.sort(cm.getDataIndex(index), "DESC");
33232                 break;
33233             case "lock":
33234                 var lc = cm.getLockedCount();
33235                 if(cm.getColumnCount(true) <= lc+1){
33236                     this.onDenyColumnLock();
33237                     return;
33238                 }
33239                 if(lc != index){
33240                     cm.setLocked(index, true, true);
33241                     cm.moveColumn(index, lc);
33242                     this.grid.fireEvent("columnmove", index, lc);
33243                 }else{
33244                     cm.setLocked(index, true);
33245                 }
33246             break;
33247             case "unlock":
33248                 var lc = cm.getLockedCount();
33249                 if((lc-1) != index){
33250                     cm.setLocked(index, false, true);
33251                     cm.moveColumn(index, lc-1);
33252                     this.grid.fireEvent("columnmove", index, lc-1);
33253                 }else{
33254                     cm.setLocked(index, false);
33255                 }
33256             break;
33257             default:
33258                 index = cm.getIndexById(item.id.substr(4));
33259                 if(index != -1){
33260                     if(item.checked && cm.getColumnCount(true) <= 1){
33261                         this.onDenyColumnHide();
33262                         return false;
33263                     }
33264                     cm.setHidden(index, item.checked);
33265                 }
33266         }
33267         return true;
33268     },
33269
33270     beforeColMenuShow : function(){
33271         var cm = this.cm,  colCount = cm.getColumnCount();
33272         this.colMenu.removeAll();
33273         for(var i = 0; i < colCount; i++){
33274             this.colMenu.add(new Roo.menu.CheckItem({
33275                 id: "col-"+cm.getColumnId(i),
33276                 text: cm.getColumnHeader(i),
33277                 checked: !cm.isHidden(i),
33278                 hideOnClick:false
33279             }));
33280         }
33281     },
33282
33283     handleHdCtx : function(g, index, e){
33284         e.stopEvent();
33285         var hd = this.getHeaderCell(index);
33286         this.hdCtxIndex = index;
33287         var ms = this.hmenu.items, cm = this.cm;
33288         ms.get("asc").setDisabled(!cm.isSortable(index));
33289         ms.get("desc").setDisabled(!cm.isSortable(index));
33290         if(this.grid.enableColLock !== false){
33291             ms.get("lock").setDisabled(cm.isLocked(index));
33292             ms.get("unlock").setDisabled(!cm.isLocked(index));
33293         }
33294         this.hmenu.show(hd, "tl-bl");
33295     },
33296
33297     handleHdOver : function(e){
33298         var hd = this.findHeaderCell(e.getTarget());
33299         if(hd && !this.headersDisabled){
33300             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33301                this.fly(hd).addClass("x-grid-hd-over");
33302             }
33303         }
33304     },
33305
33306     handleHdOut : function(e){
33307         var hd = this.findHeaderCell(e.getTarget());
33308         if(hd){
33309             this.fly(hd).removeClass("x-grid-hd-over");
33310         }
33311     },
33312
33313     handleSplitDblClick : function(e, t){
33314         var i = this.getCellIndex(t);
33315         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33316             this.autoSizeColumn(i, true);
33317             this.layout();
33318         }
33319     },
33320
33321     render : function(){
33322
33323         var cm = this.cm;
33324         var colCount = cm.getColumnCount();
33325
33326         if(this.grid.monitorWindowResize === true){
33327             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33328         }
33329         var header = this.renderHeaders();
33330         var body = this.templates.body.apply({rows:""});
33331         var html = this.templates.master.apply({
33332             lockedBody: body,
33333             body: body,
33334             lockedHeader: header[0],
33335             header: header[1]
33336         });
33337
33338         //this.updateColumns();
33339
33340         this.grid.getGridEl().dom.innerHTML = html;
33341
33342         this.initElements();
33343
33344         this.scroller.on("scroll", this.handleScroll, this);
33345         this.lockedBody.on("mousewheel", this.handleWheel, this);
33346         this.mainBody.on("mousewheel", this.handleWheel, this);
33347
33348         this.mainHd.on("mouseover", this.handleHdOver, this);
33349         this.mainHd.on("mouseout", this.handleHdOut, this);
33350         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
33351                 {delegate: "."+this.splitClass});
33352
33353         this.lockedHd.on("mouseover", this.handleHdOver, this);
33354         this.lockedHd.on("mouseout", this.handleHdOut, this);
33355         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
33356                 {delegate: "."+this.splitClass});
33357
33358         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
33359             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33360         }
33361
33362         this.updateSplitters();
33363
33364         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
33365             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33366             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33367         }
33368
33369         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
33370             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
33371             this.hmenu.add(
33372                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
33373                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
33374             );
33375             if(this.grid.enableColLock !== false){
33376                 this.hmenu.add('-',
33377                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
33378                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
33379                 );
33380             }
33381             if(this.grid.enableColumnHide !== false){
33382
33383                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
33384                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
33385                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
33386
33387                 this.hmenu.add('-',
33388                     {id:"columns", text: this.columnsText, menu: this.colMenu}
33389                 );
33390             }
33391             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
33392
33393             this.grid.on("headercontextmenu", this.handleHdCtx, this);
33394         }
33395
33396         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
33397             this.dd = new Roo.grid.GridDragZone(this.grid, {
33398                 ddGroup : this.grid.ddGroup || 'GridDD'
33399             });
33400         }
33401
33402         /*
33403         for(var i = 0; i < colCount; i++){
33404             if(cm.isHidden(i)){
33405                 this.hideColumn(i);
33406             }
33407             if(cm.config[i].align){
33408                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
33409                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
33410             }
33411         }*/
33412         
33413         this.updateHeaderSortState();
33414
33415         this.beforeInitialResize();
33416         this.layout(true);
33417
33418         // two part rendering gives faster view to the user
33419         this.renderPhase2.defer(1, this);
33420     },
33421
33422     renderPhase2 : function(){
33423         // render the rows now
33424         this.refresh();
33425         if(this.grid.autoSizeColumns){
33426             this.autoSizeColumns();
33427         }
33428     },
33429
33430     beforeInitialResize : function(){
33431
33432     },
33433
33434     onColumnSplitterMoved : function(i, w){
33435         this.userResized = true;
33436         var cm = this.grid.colModel;
33437         cm.setColumnWidth(i, w, true);
33438         var cid = cm.getColumnId(i);
33439         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33440         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33441         this.updateSplitters();
33442         this.layout();
33443         this.grid.fireEvent("columnresize", i, w);
33444     },
33445
33446     syncRowHeights : function(startIndex, endIndex){
33447         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
33448             startIndex = startIndex || 0;
33449             var mrows = this.getBodyTable().rows;
33450             var lrows = this.getLockedTable().rows;
33451             var len = mrows.length-1;
33452             endIndex = Math.min(endIndex || len, len);
33453             for(var i = startIndex; i <= endIndex; i++){
33454                 var m = mrows[i], l = lrows[i];
33455                 var h = Math.max(m.offsetHeight, l.offsetHeight);
33456                 m.style.height = l.style.height = h + "px";
33457             }
33458         }
33459     },
33460
33461     layout : function(initialRender, is2ndPass){
33462         var g = this.grid;
33463         var auto = g.autoHeight;
33464         var scrollOffset = 16;
33465         var c = g.getGridEl(), cm = this.cm,
33466                 expandCol = g.autoExpandColumn,
33467                 gv = this;
33468         //c.beginMeasure();
33469
33470         if(!c.dom.offsetWidth){ // display:none?
33471             if(initialRender){
33472                 this.lockedWrap.show();
33473                 this.mainWrap.show();
33474             }
33475             return;
33476         }
33477
33478         var hasLock = this.cm.isLocked(0);
33479
33480         var tbh = this.headerPanel.getHeight();
33481         var bbh = this.footerPanel.getHeight();
33482
33483         if(auto){
33484             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
33485             var newHeight = ch + c.getBorderWidth("tb");
33486             if(g.maxHeight){
33487                 newHeight = Math.min(g.maxHeight, newHeight);
33488             }
33489             c.setHeight(newHeight);
33490         }
33491
33492         if(g.autoWidth){
33493             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
33494         }
33495
33496         var s = this.scroller;
33497
33498         var csize = c.getSize(true);
33499
33500         this.el.setSize(csize.width, csize.height);
33501
33502         this.headerPanel.setWidth(csize.width);
33503         this.footerPanel.setWidth(csize.width);
33504
33505         var hdHeight = this.mainHd.getHeight();
33506         var vw = csize.width;
33507         var vh = csize.height - (tbh + bbh);
33508
33509         s.setSize(vw, vh);
33510
33511         var bt = this.getBodyTable();
33512         var ltWidth = hasLock ?
33513                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
33514
33515         var scrollHeight = bt.offsetHeight;
33516         var scrollWidth = ltWidth + bt.offsetWidth;
33517         var vscroll = false, hscroll = false;
33518
33519         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
33520
33521         var lw = this.lockedWrap, mw = this.mainWrap;
33522         var lb = this.lockedBody, mb = this.mainBody;
33523
33524         setTimeout(function(){
33525             var t = s.dom.offsetTop;
33526             var w = s.dom.clientWidth,
33527                 h = s.dom.clientHeight;
33528
33529             lw.setTop(t);
33530             lw.setSize(ltWidth, h);
33531
33532             mw.setLeftTop(ltWidth, t);
33533             mw.setSize(w-ltWidth, h);
33534
33535             lb.setHeight(h-hdHeight);
33536             mb.setHeight(h-hdHeight);
33537
33538             if(is2ndPass !== true && !gv.userResized && expandCol){
33539                 // high speed resize without full column calculation
33540                 
33541                 var ci = cm.getIndexById(expandCol);
33542                 if (ci < 0) {
33543                     ci = cm.findColumnIndex(expandCol);
33544                 }
33545                 ci = Math.max(0, ci); // make sure it's got at least the first col.
33546                 var expandId = cm.getColumnId(ci);
33547                 var  tw = cm.getTotalWidth(false);
33548                 var currentWidth = cm.getColumnWidth(ci);
33549                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
33550                 if(currentWidth != cw){
33551                     cm.setColumnWidth(ci, cw, true);
33552                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33553                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
33554                     gv.updateSplitters();
33555                     gv.layout(false, true);
33556                 }
33557             }
33558
33559             if(initialRender){
33560                 lw.show();
33561                 mw.show();
33562             }
33563             //c.endMeasure();
33564         }, 10);
33565     },
33566
33567     onWindowResize : function(){
33568         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
33569             return;
33570         }
33571         this.layout();
33572     },
33573
33574     appendFooter : function(parentEl){
33575         return null;
33576     },
33577
33578     sortAscText : "Sort Ascending",
33579     sortDescText : "Sort Descending",
33580     lockText : "Lock Column",
33581     unlockText : "Unlock Column",
33582     columnsText : "Columns"
33583 });
33584
33585
33586 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
33587     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
33588     this.proxy.el.addClass('x-grid3-col-dd');
33589 };
33590
33591 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
33592     handleMouseDown : function(e){
33593
33594     },
33595
33596     callHandleMouseDown : function(e){
33597         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
33598     }
33599 });
33600 /*
33601  * Based on:
33602  * Ext JS Library 1.1.1
33603  * Copyright(c) 2006-2007, Ext JS, LLC.
33604  *
33605  * Originally Released Under LGPL - original licence link has changed is not relivant.
33606  *
33607  * Fork - LGPL
33608  * <script type="text/javascript">
33609  */
33610  
33611 // private
33612 // This is a support class used internally by the Grid components
33613 Roo.grid.SplitDragZone = function(grid, hd, hd2){
33614     this.grid = grid;
33615     this.view = grid.getView();
33616     this.proxy = this.view.resizeProxy;
33617     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
33618         "gridSplitters" + this.grid.getGridEl().id, {
33619         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
33620     });
33621     this.setHandleElId(Roo.id(hd));
33622     this.setOuterHandleElId(Roo.id(hd2));
33623     this.scroll = false;
33624 };
33625 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
33626     fly: Roo.Element.fly,
33627
33628     b4StartDrag : function(x, y){
33629         this.view.headersDisabled = true;
33630         this.proxy.setHeight(this.view.mainWrap.getHeight());
33631         var w = this.cm.getColumnWidth(this.cellIndex);
33632         var minw = Math.max(w-this.grid.minColumnWidth, 0);
33633         this.resetConstraints();
33634         this.setXConstraint(minw, 1000);
33635         this.setYConstraint(0, 0);
33636         this.minX = x - minw;
33637         this.maxX = x + 1000;
33638         this.startPos = x;
33639         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
33640     },
33641
33642
33643     handleMouseDown : function(e){
33644         ev = Roo.EventObject.setEvent(e);
33645         var t = this.fly(ev.getTarget());
33646         if(t.hasClass("x-grid-split")){
33647             this.cellIndex = this.view.getCellIndex(t.dom);
33648             this.split = t.dom;
33649             this.cm = this.grid.colModel;
33650             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
33651                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
33652             }
33653         }
33654     },
33655
33656     endDrag : function(e){
33657         this.view.headersDisabled = false;
33658         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
33659         var diff = endX - this.startPos;
33660         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
33661     },
33662
33663     autoOffset : function(){
33664         this.setDelta(0,0);
33665     }
33666 });/*
33667  * Based on:
33668  * Ext JS Library 1.1.1
33669  * Copyright(c) 2006-2007, Ext JS, LLC.
33670  *
33671  * Originally Released Under LGPL - original licence link has changed is not relivant.
33672  *
33673  * Fork - LGPL
33674  * <script type="text/javascript">
33675  */
33676  
33677 // private
33678 // This is a support class used internally by the Grid components
33679 Roo.grid.GridDragZone = function(grid, config){
33680     this.view = grid.getView();
33681     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
33682     if(this.view.lockedBody){
33683         this.setHandleElId(Roo.id(this.view.mainBody.dom));
33684         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
33685     }
33686     this.scroll = false;
33687     this.grid = grid;
33688     this.ddel = document.createElement('div');
33689     this.ddel.className = 'x-grid-dd-wrap';
33690 };
33691
33692 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
33693     ddGroup : "GridDD",
33694
33695     getDragData : function(e){
33696         var t = Roo.lib.Event.getTarget(e);
33697         var rowIndex = this.view.findRowIndex(t);
33698         if(rowIndex !== false){
33699             var sm = this.grid.selModel;
33700             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
33701               //  sm.mouseDown(e, t);
33702             //}
33703             if (e.hasModifier()){
33704                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
33705             }
33706             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
33707         }
33708         return false;
33709     },
33710
33711     onInitDrag : function(e){
33712         var data = this.dragData;
33713         this.ddel.innerHTML = this.grid.getDragDropText();
33714         this.proxy.update(this.ddel);
33715         // fire start drag?
33716     },
33717
33718     afterRepair : function(){
33719         this.dragging = false;
33720     },
33721
33722     getRepairXY : function(e, data){
33723         return false;
33724     },
33725
33726     onEndDrag : function(data, e){
33727         // fire end drag?
33728     },
33729
33730     onValidDrop : function(dd, e, id){
33731         // fire drag drop?
33732         this.hideProxy();
33733     },
33734
33735     beforeInvalidDrop : function(e, id){
33736
33737     }
33738 });/*
33739  * Based on:
33740  * Ext JS Library 1.1.1
33741  * Copyright(c) 2006-2007, Ext JS, LLC.
33742  *
33743  * Originally Released Under LGPL - original licence link has changed is not relivant.
33744  *
33745  * Fork - LGPL
33746  * <script type="text/javascript">
33747  */
33748  
33749
33750 /**
33751  * @class Roo.grid.ColumnModel
33752  * @extends Roo.util.Observable
33753  * This is the default implementation of a ColumnModel used by the Grid. It defines
33754  * the columns in the grid.
33755  * <br>Usage:<br>
33756  <pre><code>
33757  var colModel = new Roo.grid.ColumnModel([
33758         {header: "Ticker", width: 60, sortable: true, locked: true},
33759         {header: "Company Name", width: 150, sortable: true},
33760         {header: "Market Cap.", width: 100, sortable: true},
33761         {header: "$ Sales", width: 100, sortable: true, renderer: money},
33762         {header: "Employees", width: 100, sortable: true, resizable: false}
33763  ]);
33764  </code></pre>
33765  * <p>
33766  
33767  * The config options listed for this class are options which may appear in each
33768  * individual column definition.
33769  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
33770  * @constructor
33771  * @param {Object} config An Array of column config objects. See this class's
33772  * config objects for details.
33773 */
33774 Roo.grid.ColumnModel = function(config){
33775         /**
33776      * The config passed into the constructor
33777      */
33778     this.config = config;
33779     this.lookup = {};
33780
33781     // if no id, create one
33782     // if the column does not have a dataIndex mapping,
33783     // map it to the order it is in the config
33784     for(var i = 0, len = config.length; i < len; i++){
33785         var c = config[i];
33786         if(typeof c.dataIndex == "undefined"){
33787             c.dataIndex = i;
33788         }
33789         if(typeof c.renderer == "string"){
33790             c.renderer = Roo.util.Format[c.renderer];
33791         }
33792         if(typeof c.id == "undefined"){
33793             c.id = Roo.id();
33794         }
33795         if(c.editor && c.editor.xtype){
33796             c.editor  = Roo.factory(c.editor, Roo.grid);
33797         }
33798         if(c.editor && c.editor.isFormField){
33799             c.editor = new Roo.grid.GridEditor(c.editor);
33800         }
33801         this.lookup[c.id] = c;
33802     }
33803
33804     /**
33805      * The width of columns which have no width specified (defaults to 100)
33806      * @type Number
33807      */
33808     this.defaultWidth = 100;
33809
33810     /**
33811      * Default sortable of columns which have no sortable specified (defaults to false)
33812      * @type Boolean
33813      */
33814     this.defaultSortable = false;
33815
33816     this.addEvents({
33817         /**
33818              * @event widthchange
33819              * Fires when the width of a column changes.
33820              * @param {ColumnModel} this
33821              * @param {Number} columnIndex The column index
33822              * @param {Number} newWidth The new width
33823              */
33824             "widthchange": true,
33825         /**
33826              * @event headerchange
33827              * Fires when the text of a header changes.
33828              * @param {ColumnModel} this
33829              * @param {Number} columnIndex The column index
33830              * @param {Number} newText The new header text
33831              */
33832             "headerchange": true,
33833         /**
33834              * @event hiddenchange
33835              * Fires when a column is hidden or "unhidden".
33836              * @param {ColumnModel} this
33837              * @param {Number} columnIndex The column index
33838              * @param {Boolean} hidden true if hidden, false otherwise
33839              */
33840             "hiddenchange": true,
33841             /**
33842          * @event columnmoved
33843          * Fires when a column is moved.
33844          * @param {ColumnModel} this
33845          * @param {Number} oldIndex
33846          * @param {Number} newIndex
33847          */
33848         "columnmoved" : true,
33849         /**
33850          * @event columlockchange
33851          * Fires when a column's locked state is changed
33852          * @param {ColumnModel} this
33853          * @param {Number} colIndex
33854          * @param {Boolean} locked true if locked
33855          */
33856         "columnlockchange" : true
33857     });
33858     Roo.grid.ColumnModel.superclass.constructor.call(this);
33859 };
33860 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
33861     /**
33862      * @cfg {String} header The header text to display in the Grid view.
33863      */
33864     /**
33865      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
33866      * {@link Roo.data.Record} definition from which to draw the column's value. If not
33867      * specified, the column's index is used as an index into the Record's data Array.
33868      */
33869     /**
33870      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
33871      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
33872      */
33873     /**
33874      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
33875      * Defaults to the value of the {@link #defaultSortable} property.
33876      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
33877      */
33878     /**
33879      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
33880      */
33881     /**
33882      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
33883      */
33884     /**
33885      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
33886      */
33887     /**
33888      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
33889      */
33890     /**
33891      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
33892      * given the cell's data value. See {@link #setRenderer}. If not specified, the
33893      * default renderer uses the raw data value.
33894      */
33895        /**
33896      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
33897      */
33898     /**
33899      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
33900      */
33901
33902     /**
33903      * Returns the id of the column at the specified index.
33904      * @param {Number} index The column index
33905      * @return {String} the id
33906      */
33907     getColumnId : function(index){
33908         return this.config[index].id;
33909     },
33910
33911     /**
33912      * Returns the column for a specified id.
33913      * @param {String} id The column id
33914      * @return {Object} the column
33915      */
33916     getColumnById : function(id){
33917         return this.lookup[id];
33918     },
33919
33920     
33921     /**
33922      * Returns the column for a specified dataIndex.
33923      * @param {String} dataIndex The column dataIndex
33924      * @return {Object|Boolean} the column or false if not found
33925      */
33926     getColumnByDataIndex: function(dataIndex){
33927         var index = this.findColumnIndex(dataIndex);
33928         return index > -1 ? this.config[index] : false;
33929     },
33930     
33931     /**
33932      * Returns the index for a specified column id.
33933      * @param {String} id The column id
33934      * @return {Number} the index, or -1 if not found
33935      */
33936     getIndexById : function(id){
33937         for(var i = 0, len = this.config.length; i < len; i++){
33938             if(this.config[i].id == id){
33939                 return i;
33940             }
33941         }
33942         return -1;
33943     },
33944     
33945     /**
33946      * Returns the index for a specified column dataIndex.
33947      * @param {String} dataIndex The column dataIndex
33948      * @return {Number} the index, or -1 if not found
33949      */
33950     
33951     findColumnIndex : function(dataIndex){
33952         for(var i = 0, len = this.config.length; i < len; i++){
33953             if(this.config[i].dataIndex == dataIndex){
33954                 return i;
33955             }
33956         }
33957         return -1;
33958     },
33959     
33960     
33961     moveColumn : function(oldIndex, newIndex){
33962         var c = this.config[oldIndex];
33963         this.config.splice(oldIndex, 1);
33964         this.config.splice(newIndex, 0, c);
33965         this.dataMap = null;
33966         this.fireEvent("columnmoved", this, oldIndex, newIndex);
33967     },
33968
33969     isLocked : function(colIndex){
33970         return this.config[colIndex].locked === true;
33971     },
33972
33973     setLocked : function(colIndex, value, suppressEvent){
33974         if(this.isLocked(colIndex) == value){
33975             return;
33976         }
33977         this.config[colIndex].locked = value;
33978         if(!suppressEvent){
33979             this.fireEvent("columnlockchange", this, colIndex, value);
33980         }
33981     },
33982
33983     getTotalLockedWidth : function(){
33984         var totalWidth = 0;
33985         for(var i = 0; i < this.config.length; i++){
33986             if(this.isLocked(i) && !this.isHidden(i)){
33987                 this.totalWidth += this.getColumnWidth(i);
33988             }
33989         }
33990         return totalWidth;
33991     },
33992
33993     getLockedCount : function(){
33994         for(var i = 0, len = this.config.length; i < len; i++){
33995             if(!this.isLocked(i)){
33996                 return i;
33997             }
33998         }
33999     },
34000
34001     /**
34002      * Returns the number of columns.
34003      * @return {Number}
34004      */
34005     getColumnCount : function(visibleOnly){
34006         if(visibleOnly === true){
34007             var c = 0;
34008             for(var i = 0, len = this.config.length; i < len; i++){
34009                 if(!this.isHidden(i)){
34010                     c++;
34011                 }
34012             }
34013             return c;
34014         }
34015         return this.config.length;
34016     },
34017
34018     /**
34019      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34020      * @param {Function} fn
34021      * @param {Object} scope (optional)
34022      * @return {Array} result
34023      */
34024     getColumnsBy : function(fn, scope){
34025         var r = [];
34026         for(var i = 0, len = this.config.length; i < len; i++){
34027             var c = this.config[i];
34028             if(fn.call(scope||this, c, i) === true){
34029                 r[r.length] = c;
34030             }
34031         }
34032         return r;
34033     },
34034
34035     /**
34036      * Returns true if the specified column is sortable.
34037      * @param {Number} col The column index
34038      * @return {Boolean}
34039      */
34040     isSortable : function(col){
34041         if(typeof this.config[col].sortable == "undefined"){
34042             return this.defaultSortable;
34043         }
34044         return this.config[col].sortable;
34045     },
34046
34047     /**
34048      * Returns the rendering (formatting) function defined for the column.
34049      * @param {Number} col The column index.
34050      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34051      */
34052     getRenderer : function(col){
34053         if(!this.config[col].renderer){
34054             return Roo.grid.ColumnModel.defaultRenderer;
34055         }
34056         return this.config[col].renderer;
34057     },
34058
34059     /**
34060      * Sets the rendering (formatting) function for a column.
34061      * @param {Number} col The column index
34062      * @param {Function} fn The function to use to process the cell's raw data
34063      * to return HTML markup for the grid view. The render function is called with
34064      * the following parameters:<ul>
34065      * <li>Data value.</li>
34066      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34067      * <li>css A CSS style string to apply to the table cell.</li>
34068      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34069      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34070      * <li>Row index</li>
34071      * <li>Column index</li>
34072      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34073      */
34074     setRenderer : function(col, fn){
34075         this.config[col].renderer = fn;
34076     },
34077
34078     /**
34079      * Returns the width for the specified column.
34080      * @param {Number} col The column index
34081      * @return {Number}
34082      */
34083     getColumnWidth : function(col){
34084         return this.config[col].width || this.defaultWidth;
34085     },
34086
34087     /**
34088      * Sets the width for a column.
34089      * @param {Number} col The column index
34090      * @param {Number} width The new width
34091      */
34092     setColumnWidth : function(col, width, suppressEvent){
34093         this.config[col].width = width;
34094         this.totalWidth = null;
34095         if(!suppressEvent){
34096              this.fireEvent("widthchange", this, col, width);
34097         }
34098     },
34099
34100     /**
34101      * Returns the total width of all columns.
34102      * @param {Boolean} includeHidden True to include hidden column widths
34103      * @return {Number}
34104      */
34105     getTotalWidth : function(includeHidden){
34106         if(!this.totalWidth){
34107             this.totalWidth = 0;
34108             for(var i = 0, len = this.config.length; i < len; i++){
34109                 if(includeHidden || !this.isHidden(i)){
34110                     this.totalWidth += this.getColumnWidth(i);
34111                 }
34112             }
34113         }
34114         return this.totalWidth;
34115     },
34116
34117     /**
34118      * Returns the header for the specified column.
34119      * @param {Number} col The column index
34120      * @return {String}
34121      */
34122     getColumnHeader : function(col){
34123         return this.config[col].header;
34124     },
34125
34126     /**
34127      * Sets the header for a column.
34128      * @param {Number} col The column index
34129      * @param {String} header The new header
34130      */
34131     setColumnHeader : function(col, header){
34132         this.config[col].header = header;
34133         this.fireEvent("headerchange", this, col, header);
34134     },
34135
34136     /**
34137      * Returns the tooltip for the specified column.
34138      * @param {Number} col The column index
34139      * @return {String}
34140      */
34141     getColumnTooltip : function(col){
34142             return this.config[col].tooltip;
34143     },
34144     /**
34145      * Sets the tooltip for a column.
34146      * @param {Number} col The column index
34147      * @param {String} tooltip The new tooltip
34148      */
34149     setColumnTooltip : function(col, tooltip){
34150             this.config[col].tooltip = tooltip;
34151     },
34152
34153     /**
34154      * Returns the dataIndex for the specified column.
34155      * @param {Number} col The column index
34156      * @return {Number}
34157      */
34158     getDataIndex : function(col){
34159         return this.config[col].dataIndex;
34160     },
34161
34162     /**
34163      * Sets the dataIndex for a column.
34164      * @param {Number} col The column index
34165      * @param {Number} dataIndex The new dataIndex
34166      */
34167     setDataIndex : function(col, dataIndex){
34168         this.config[col].dataIndex = dataIndex;
34169     },
34170
34171     
34172     
34173     /**
34174      * Returns true if the cell is editable.
34175      * @param {Number} colIndex The column index
34176      * @param {Number} rowIndex The row index
34177      * @return {Boolean}
34178      */
34179     isCellEditable : function(colIndex, rowIndex){
34180         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34181     },
34182
34183     /**
34184      * Returns the editor defined for the cell/column.
34185      * return false or null to disable editing.
34186      * @param {Number} colIndex The column index
34187      * @param {Number} rowIndex The row index
34188      * @return {Object}
34189      */
34190     getCellEditor : function(colIndex, rowIndex){
34191         return this.config[colIndex].editor;
34192     },
34193
34194     /**
34195      * Sets if a column is editable.
34196      * @param {Number} col The column index
34197      * @param {Boolean} editable True if the column is editable
34198      */
34199     setEditable : function(col, editable){
34200         this.config[col].editable = editable;
34201     },
34202
34203
34204     /**
34205      * Returns true if the column is hidden.
34206      * @param {Number} colIndex The column index
34207      * @return {Boolean}
34208      */
34209     isHidden : function(colIndex){
34210         return this.config[colIndex].hidden;
34211     },
34212
34213
34214     /**
34215      * Returns true if the column width cannot be changed
34216      */
34217     isFixed : function(colIndex){
34218         return this.config[colIndex].fixed;
34219     },
34220
34221     /**
34222      * Returns true if the column can be resized
34223      * @return {Boolean}
34224      */
34225     isResizable : function(colIndex){
34226         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34227     },
34228     /**
34229      * Sets if a column is hidden.
34230      * @param {Number} colIndex The column index
34231      * @param {Boolean} hidden True if the column is hidden
34232      */
34233     setHidden : function(colIndex, hidden){
34234         this.config[colIndex].hidden = hidden;
34235         this.totalWidth = null;
34236         this.fireEvent("hiddenchange", this, colIndex, hidden);
34237     },
34238
34239     /**
34240      * Sets the editor for a column.
34241      * @param {Number} col The column index
34242      * @param {Object} editor The editor object
34243      */
34244     setEditor : function(col, editor){
34245         this.config[col].editor = editor;
34246     }
34247 });
34248
34249 Roo.grid.ColumnModel.defaultRenderer = function(value){
34250         if(typeof value == "string" && value.length < 1){
34251             return "&#160;";
34252         }
34253         return value;
34254 };
34255
34256 // Alias for backwards compatibility
34257 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34258 /*
34259  * Based on:
34260  * Ext JS Library 1.1.1
34261  * Copyright(c) 2006-2007, Ext JS, LLC.
34262  *
34263  * Originally Released Under LGPL - original licence link has changed is not relivant.
34264  *
34265  * Fork - LGPL
34266  * <script type="text/javascript">
34267  */
34268
34269 /**
34270  * @class Roo.grid.AbstractSelectionModel
34271  * @extends Roo.util.Observable
34272  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34273  * implemented by descendant classes.  This class should not be directly instantiated.
34274  * @constructor
34275  */
34276 Roo.grid.AbstractSelectionModel = function(){
34277     this.locked = false;
34278     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34279 };
34280
34281 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34282     /** @ignore Called by the grid automatically. Do not call directly. */
34283     init : function(grid){
34284         this.grid = grid;
34285         this.initEvents();
34286     },
34287
34288     /**
34289      * Locks the selections.
34290      */
34291     lock : function(){
34292         this.locked = true;
34293     },
34294
34295     /**
34296      * Unlocks the selections.
34297      */
34298     unlock : function(){
34299         this.locked = false;
34300     },
34301
34302     /**
34303      * Returns true if the selections are locked.
34304      * @return {Boolean}
34305      */
34306     isLocked : function(){
34307         return this.locked;
34308     }
34309 });/*
34310  * Based on:
34311  * Ext JS Library 1.1.1
34312  * Copyright(c) 2006-2007, Ext JS, LLC.
34313  *
34314  * Originally Released Under LGPL - original licence link has changed is not relivant.
34315  *
34316  * Fork - LGPL
34317  * <script type="text/javascript">
34318  */
34319 /**
34320  * @extends Roo.grid.AbstractSelectionModel
34321  * @class Roo.grid.RowSelectionModel
34322  * The default SelectionModel used by {@link Roo.grid.Grid}.
34323  * It supports multiple selections and keyboard selection/navigation. 
34324  * @constructor
34325  * @param {Object} config
34326  */
34327 Roo.grid.RowSelectionModel = function(config){
34328     Roo.apply(this, config);
34329     this.selections = new Roo.util.MixedCollection(false, function(o){
34330         return o.id;
34331     });
34332
34333     this.last = false;
34334     this.lastActive = false;
34335
34336     this.addEvents({
34337         /**
34338              * @event selectionchange
34339              * Fires when the selection changes
34340              * @param {SelectionModel} this
34341              */
34342             "selectionchange" : true,
34343         /**
34344              * @event afterselectionchange
34345              * Fires after the selection changes (eg. by key press or clicking)
34346              * @param {SelectionModel} this
34347              */
34348             "afterselectionchange" : true,
34349         /**
34350              * @event beforerowselect
34351              * Fires when a row is selected being selected, return false to cancel.
34352              * @param {SelectionModel} this
34353              * @param {Number} rowIndex The selected index
34354              * @param {Boolean} keepExisting False if other selections will be cleared
34355              */
34356             "beforerowselect" : true,
34357         /**
34358              * @event rowselect
34359              * Fires when a row is selected.
34360              * @param {SelectionModel} this
34361              * @param {Number} rowIndex The selected index
34362              * @param {Roo.data.Record} r The record
34363              */
34364             "rowselect" : true,
34365         /**
34366              * @event rowdeselect
34367              * Fires when a row is deselected.
34368              * @param {SelectionModel} this
34369              * @param {Number} rowIndex The selected index
34370              */
34371         "rowdeselect" : true
34372     });
34373     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
34374     this.locked = false;
34375 };
34376
34377 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
34378     /**
34379      * @cfg {Boolean} singleSelect
34380      * True to allow selection of only one row at a time (defaults to false)
34381      */
34382     singleSelect : false,
34383
34384     // private
34385     initEvents : function(){
34386
34387         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
34388             this.grid.on("mousedown", this.handleMouseDown, this);
34389         }else{ // allow click to work like normal
34390             this.grid.on("rowclick", this.handleDragableRowClick, this);
34391         }
34392
34393         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
34394             "up" : function(e){
34395                 if(!e.shiftKey){
34396                     this.selectPrevious(e.shiftKey);
34397                 }else if(this.last !== false && this.lastActive !== false){
34398                     var last = this.last;
34399                     this.selectRange(this.last,  this.lastActive-1);
34400                     this.grid.getView().focusRow(this.lastActive);
34401                     if(last !== false){
34402                         this.last = last;
34403                     }
34404                 }else{
34405                     this.selectFirstRow();
34406                 }
34407                 this.fireEvent("afterselectionchange", this);
34408             },
34409             "down" : function(e){
34410                 if(!e.shiftKey){
34411                     this.selectNext(e.shiftKey);
34412                 }else if(this.last !== false && this.lastActive !== false){
34413                     var last = this.last;
34414                     this.selectRange(this.last,  this.lastActive+1);
34415                     this.grid.getView().focusRow(this.lastActive);
34416                     if(last !== false){
34417                         this.last = last;
34418                     }
34419                 }else{
34420                     this.selectFirstRow();
34421                 }
34422                 this.fireEvent("afterselectionchange", this);
34423             },
34424             scope: this
34425         });
34426
34427         var view = this.grid.view;
34428         view.on("refresh", this.onRefresh, this);
34429         view.on("rowupdated", this.onRowUpdated, this);
34430         view.on("rowremoved", this.onRemove, this);
34431     },
34432
34433     // private
34434     onRefresh : function(){
34435         var ds = this.grid.dataSource, i, v = this.grid.view;
34436         var s = this.selections;
34437         s.each(function(r){
34438             if((i = ds.indexOfId(r.id)) != -1){
34439                 v.onRowSelect(i);
34440             }else{
34441                 s.remove(r);
34442             }
34443         });
34444     },
34445
34446     // private
34447     onRemove : function(v, index, r){
34448         this.selections.remove(r);
34449     },
34450
34451     // private
34452     onRowUpdated : function(v, index, r){
34453         if(this.isSelected(r)){
34454             v.onRowSelect(index);
34455         }
34456     },
34457
34458     /**
34459      * Select records.
34460      * @param {Array} records The records to select
34461      * @param {Boolean} keepExisting (optional) True to keep existing selections
34462      */
34463     selectRecords : function(records, keepExisting){
34464         if(!keepExisting){
34465             this.clearSelections();
34466         }
34467         var ds = this.grid.dataSource;
34468         for(var i = 0, len = records.length; i < len; i++){
34469             this.selectRow(ds.indexOf(records[i]), true);
34470         }
34471     },
34472
34473     /**
34474      * Gets the number of selected rows.
34475      * @return {Number}
34476      */
34477     getCount : function(){
34478         return this.selections.length;
34479     },
34480
34481     /**
34482      * Selects the first row in the grid.
34483      */
34484     selectFirstRow : function(){
34485         this.selectRow(0);
34486     },
34487
34488     /**
34489      * Select the last row.
34490      * @param {Boolean} keepExisting (optional) True to keep existing selections
34491      */
34492     selectLastRow : function(keepExisting){
34493         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
34494     },
34495
34496     /**
34497      * Selects the row immediately following the last selected row.
34498      * @param {Boolean} keepExisting (optional) True to keep existing selections
34499      */
34500     selectNext : function(keepExisting){
34501         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
34502             this.selectRow(this.last+1, keepExisting);
34503             this.grid.getView().focusRow(this.last);
34504         }
34505     },
34506
34507     /**
34508      * Selects the row that precedes the last selected row.
34509      * @param {Boolean} keepExisting (optional) True to keep existing selections
34510      */
34511     selectPrevious : function(keepExisting){
34512         if(this.last){
34513             this.selectRow(this.last-1, keepExisting);
34514             this.grid.getView().focusRow(this.last);
34515         }
34516     },
34517
34518     /**
34519      * Returns the selected records
34520      * @return {Array} Array of selected records
34521      */
34522     getSelections : function(){
34523         return [].concat(this.selections.items);
34524     },
34525
34526     /**
34527      * Returns the first selected record.
34528      * @return {Record}
34529      */
34530     getSelected : function(){
34531         return this.selections.itemAt(0);
34532     },
34533
34534
34535     /**
34536      * Clears all selections.
34537      */
34538     clearSelections : function(fast){
34539         if(this.locked) return;
34540         if(fast !== true){
34541             var ds = this.grid.dataSource;
34542             var s = this.selections;
34543             s.each(function(r){
34544                 this.deselectRow(ds.indexOfId(r.id));
34545             }, this);
34546             s.clear();
34547         }else{
34548             this.selections.clear();
34549         }
34550         this.last = false;
34551     },
34552
34553
34554     /**
34555      * Selects all rows.
34556      */
34557     selectAll : function(){
34558         if(this.locked) return;
34559         this.selections.clear();
34560         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
34561             this.selectRow(i, true);
34562         }
34563     },
34564
34565     /**
34566      * Returns True if there is a selection.
34567      * @return {Boolean}
34568      */
34569     hasSelection : function(){
34570         return this.selections.length > 0;
34571     },
34572
34573     /**
34574      * Returns True if the specified row is selected.
34575      * @param {Number/Record} record The record or index of the record to check
34576      * @return {Boolean}
34577      */
34578     isSelected : function(index){
34579         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
34580         return (r && this.selections.key(r.id) ? true : false);
34581     },
34582
34583     /**
34584      * Returns True if the specified record id is selected.
34585      * @param {String} id The id of record to check
34586      * @return {Boolean}
34587      */
34588     isIdSelected : function(id){
34589         return (this.selections.key(id) ? true : false);
34590     },
34591
34592     // private
34593     handleMouseDown : function(e, t){
34594         var view = this.grid.getView(), rowIndex;
34595         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
34596             return;
34597         };
34598         if(e.shiftKey && this.last !== false){
34599             var last = this.last;
34600             this.selectRange(last, rowIndex, e.ctrlKey);
34601             this.last = last; // reset the last
34602             view.focusRow(rowIndex);
34603         }else{
34604             var isSelected = this.isSelected(rowIndex);
34605             if(e.button !== 0 && isSelected){
34606                 view.focusRow(rowIndex);
34607             }else if(e.ctrlKey && isSelected){
34608                 this.deselectRow(rowIndex);
34609             }else if(!isSelected){
34610                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
34611                 view.focusRow(rowIndex);
34612             }
34613         }
34614         this.fireEvent("afterselectionchange", this);
34615     },
34616     // private
34617     handleDragableRowClick :  function(grid, rowIndex, e) 
34618     {
34619         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
34620             this.selectRow(rowIndex, false);
34621             grid.view.focusRow(rowIndex);
34622              this.fireEvent("afterselectionchange", this);
34623         }
34624     },
34625     
34626     /**
34627      * Selects multiple rows.
34628      * @param {Array} rows Array of the indexes of the row to select
34629      * @param {Boolean} keepExisting (optional) True to keep existing selections
34630      */
34631     selectRows : function(rows, keepExisting){
34632         if(!keepExisting){
34633             this.clearSelections();
34634         }
34635         for(var i = 0, len = rows.length; i < len; i++){
34636             this.selectRow(rows[i], true);
34637         }
34638     },
34639
34640     /**
34641      * Selects a range of rows. All rows in between startRow and endRow are also selected.
34642      * @param {Number} startRow The index of the first row in the range
34643      * @param {Number} endRow The index of the last row in the range
34644      * @param {Boolean} keepExisting (optional) True to retain existing selections
34645      */
34646     selectRange : function(startRow, endRow, keepExisting){
34647         if(this.locked) return;
34648         if(!keepExisting){
34649             this.clearSelections();
34650         }
34651         if(startRow <= endRow){
34652             for(var i = startRow; i <= endRow; i++){
34653                 this.selectRow(i, true);
34654             }
34655         }else{
34656             for(var i = startRow; i >= endRow; i--){
34657                 this.selectRow(i, true);
34658             }
34659         }
34660     },
34661
34662     /**
34663      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
34664      * @param {Number} startRow The index of the first row in the range
34665      * @param {Number} endRow The index of the last row in the range
34666      */
34667     deselectRange : function(startRow, endRow, preventViewNotify){
34668         if(this.locked) return;
34669         for(var i = startRow; i <= endRow; i++){
34670             this.deselectRow(i, preventViewNotify);
34671         }
34672     },
34673
34674     /**
34675      * Selects a row.
34676      * @param {Number} row The index of the row to select
34677      * @param {Boolean} keepExisting (optional) True to keep existing selections
34678      */
34679     selectRow : function(index, keepExisting, preventViewNotify){
34680         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
34681         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
34682             if(!keepExisting || this.singleSelect){
34683                 this.clearSelections();
34684             }
34685             var r = this.grid.dataSource.getAt(index);
34686             this.selections.add(r);
34687             this.last = this.lastActive = index;
34688             if(!preventViewNotify){
34689                 this.grid.getView().onRowSelect(index);
34690             }
34691             this.fireEvent("rowselect", this, index, r);
34692             this.fireEvent("selectionchange", this);
34693         }
34694     },
34695
34696     /**
34697      * Deselects a row.
34698      * @param {Number} row The index of the row to deselect
34699      */
34700     deselectRow : function(index, preventViewNotify){
34701         if(this.locked) return;
34702         if(this.last == index){
34703             this.last = false;
34704         }
34705         if(this.lastActive == index){
34706             this.lastActive = false;
34707         }
34708         var r = this.grid.dataSource.getAt(index);
34709         this.selections.remove(r);
34710         if(!preventViewNotify){
34711             this.grid.getView().onRowDeselect(index);
34712         }
34713         this.fireEvent("rowdeselect", this, index);
34714         this.fireEvent("selectionchange", this);
34715     },
34716
34717     // private
34718     restoreLast : function(){
34719         if(this._last){
34720             this.last = this._last;
34721         }
34722     },
34723
34724     // private
34725     acceptsNav : function(row, col, cm){
34726         return !cm.isHidden(col) && cm.isCellEditable(col, row);
34727     },
34728
34729     // private
34730     onEditorKey : function(field, e){
34731         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
34732         if(k == e.TAB){
34733             e.stopEvent();
34734             ed.completeEdit();
34735             if(e.shiftKey){
34736                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
34737             }else{
34738                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
34739             }
34740         }else if(k == e.ENTER && !e.ctrlKey){
34741             e.stopEvent();
34742             ed.completeEdit();
34743             if(e.shiftKey){
34744                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
34745             }else{
34746                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
34747             }
34748         }else if(k == e.ESC){
34749             ed.cancelEdit();
34750         }
34751         if(newCell){
34752             g.startEditing(newCell[0], newCell[1]);
34753         }
34754     }
34755 });/*
34756  * Based on:
34757  * Ext JS Library 1.1.1
34758  * Copyright(c) 2006-2007, Ext JS, LLC.
34759  *
34760  * Originally Released Under LGPL - original licence link has changed is not relivant.
34761  *
34762  * Fork - LGPL
34763  * <script type="text/javascript">
34764  */
34765 /**
34766  * @class Roo.grid.CellSelectionModel
34767  * @extends Roo.grid.AbstractSelectionModel
34768  * This class provides the basic implementation for cell selection in a grid.
34769  * @constructor
34770  * @param {Object} config The object containing the configuration of this model.
34771  */
34772 Roo.grid.CellSelectionModel = function(config){
34773     Roo.apply(this, config);
34774
34775     this.selection = null;
34776
34777     this.addEvents({
34778         /**
34779              * @event beforerowselect
34780              * Fires before a cell is selected.
34781              * @param {SelectionModel} this
34782              * @param {Number} rowIndex The selected row index
34783              * @param {Number} colIndex The selected cell index
34784              */
34785             "beforecellselect" : true,
34786         /**
34787              * @event cellselect
34788              * Fires when a cell is selected.
34789              * @param {SelectionModel} this
34790              * @param {Number} rowIndex The selected row index
34791              * @param {Number} colIndex The selected cell index
34792              */
34793             "cellselect" : true,
34794         /**
34795              * @event selectionchange
34796              * Fires when the active selection changes.
34797              * @param {SelectionModel} this
34798              * @param {Object} selection null for no selection or an object (o) with two properties
34799                 <ul>
34800                 <li>o.record: the record object for the row the selection is in</li>
34801                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
34802                 </ul>
34803              */
34804             "selectionchange" : true
34805     });
34806     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
34807 };
34808
34809 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
34810
34811     /** @ignore */
34812     initEvents : function(){
34813         this.grid.on("mousedown", this.handleMouseDown, this);
34814         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
34815         var view = this.grid.view;
34816         view.on("refresh", this.onViewChange, this);
34817         view.on("rowupdated", this.onRowUpdated, this);
34818         view.on("beforerowremoved", this.clearSelections, this);
34819         view.on("beforerowsinserted", this.clearSelections, this);
34820         if(this.grid.isEditor){
34821             this.grid.on("beforeedit", this.beforeEdit,  this);
34822         }
34823     },
34824
34825         //private
34826     beforeEdit : function(e){
34827         this.select(e.row, e.column, false, true, e.record);
34828     },
34829
34830         //private
34831     onRowUpdated : function(v, index, r){
34832         if(this.selection && this.selection.record == r){
34833             v.onCellSelect(index, this.selection.cell[1]);
34834         }
34835     },
34836
34837         //private
34838     onViewChange : function(){
34839         this.clearSelections(true);
34840     },
34841
34842         /**
34843          * Returns the currently selected cell,.
34844          * @return {Array} The selected cell (row, column) or null if none selected.
34845          */
34846     getSelectedCell : function(){
34847         return this.selection ? this.selection.cell : null;
34848     },
34849
34850     /**
34851      * Clears all selections.
34852      * @param {Boolean} true to prevent the gridview from being notified about the change.
34853      */
34854     clearSelections : function(preventNotify){
34855         var s = this.selection;
34856         if(s){
34857             if(preventNotify !== true){
34858                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
34859             }
34860             this.selection = null;
34861             this.fireEvent("selectionchange", this, null);
34862         }
34863     },
34864
34865     /**
34866      * Returns true if there is a selection.
34867      * @return {Boolean}
34868      */
34869     hasSelection : function(){
34870         return this.selection ? true : false;
34871     },
34872
34873     /** @ignore */
34874     handleMouseDown : function(e, t){
34875         var v = this.grid.getView();
34876         if(this.isLocked()){
34877             return;
34878         };
34879         var row = v.findRowIndex(t);
34880         var cell = v.findCellIndex(t);
34881         if(row !== false && cell !== false){
34882             this.select(row, cell);
34883         }
34884     },
34885
34886     /**
34887      * Selects a cell.
34888      * @param {Number} rowIndex
34889      * @param {Number} collIndex
34890      */
34891     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
34892         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
34893             this.clearSelections();
34894             r = r || this.grid.dataSource.getAt(rowIndex);
34895             this.selection = {
34896                 record : r,
34897                 cell : [rowIndex, colIndex]
34898             };
34899             if(!preventViewNotify){
34900                 var v = this.grid.getView();
34901                 v.onCellSelect(rowIndex, colIndex);
34902                 if(preventFocus !== true){
34903                     v.focusCell(rowIndex, colIndex);
34904                 }
34905             }
34906             this.fireEvent("cellselect", this, rowIndex, colIndex);
34907             this.fireEvent("selectionchange", this, this.selection);
34908         }
34909     },
34910
34911         //private
34912     isSelectable : function(rowIndex, colIndex, cm){
34913         return !cm.isHidden(colIndex);
34914     },
34915
34916     /** @ignore */
34917     handleKeyDown : function(e){
34918         if(!e.isNavKeyPress()){
34919             return;
34920         }
34921         var g = this.grid, s = this.selection;
34922         if(!s){
34923             e.stopEvent();
34924             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
34925             if(cell){
34926                 this.select(cell[0], cell[1]);
34927             }
34928             return;
34929         }
34930         var sm = this;
34931         var walk = function(row, col, step){
34932             return g.walkCells(row, col, step, sm.isSelectable,  sm);
34933         };
34934         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
34935         var newCell;
34936
34937         switch(k){
34938              case e.TAB:
34939                  if(e.shiftKey){
34940                      newCell = walk(r, c-1, -1);
34941                  }else{
34942                      newCell = walk(r, c+1, 1);
34943                  }
34944              break;
34945              case e.DOWN:
34946                  newCell = walk(r+1, c, 1);
34947              break;
34948              case e.UP:
34949                  newCell = walk(r-1, c, -1);
34950              break;
34951              case e.RIGHT:
34952                  newCell = walk(r, c+1, 1);
34953              break;
34954              case e.LEFT:
34955                  newCell = walk(r, c-1, -1);
34956              break;
34957              case e.ENTER:
34958                  if(g.isEditor && !g.editing){
34959                     g.startEditing(r, c);
34960                     e.stopEvent();
34961                     return;
34962                 }
34963              break;
34964         };
34965         if(newCell){
34966             this.select(newCell[0], newCell[1]);
34967             e.stopEvent();
34968         }
34969     },
34970
34971     acceptsNav : function(row, col, cm){
34972         return !cm.isHidden(col) && cm.isCellEditable(col, row);
34973     },
34974
34975     onEditorKey : function(field, e){
34976         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
34977         if(k == e.TAB){
34978             if(e.shiftKey){
34979                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
34980             }else{
34981                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
34982             }
34983             e.stopEvent();
34984         }else if(k == e.ENTER && !e.ctrlKey){
34985             ed.completeEdit();
34986             e.stopEvent();
34987         }else if(k == e.ESC){
34988             ed.cancelEdit();
34989         }
34990         if(newCell){
34991             g.startEditing(newCell[0], newCell[1]);
34992         }
34993     }
34994 });/*
34995  * Based on:
34996  * Ext JS Library 1.1.1
34997  * Copyright(c) 2006-2007, Ext JS, LLC.
34998  *
34999  * Originally Released Under LGPL - original licence link has changed is not relivant.
35000  *
35001  * Fork - LGPL
35002  * <script type="text/javascript">
35003  */
35004  
35005 /**
35006  * @class Roo.grid.EditorGrid
35007  * @extends Roo.grid.Grid
35008  * Class for creating and editable grid.
35009  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35010  * The container MUST have some type of size defined for the grid to fill. The container will be 
35011  * automatically set to position relative if it isn't already.
35012  * @param {Object} dataSource The data model to bind to
35013  * @param {Object} colModel The column model with info about this grid's columns
35014  */
35015 Roo.grid.EditorGrid = function(container, config){
35016     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35017     this.getGridEl().addClass("xedit-grid");
35018
35019     if(!this.selModel){
35020         this.selModel = new Roo.grid.CellSelectionModel();
35021     }
35022
35023     this.activeEditor = null;
35024
35025         this.addEvents({
35026             /**
35027              * @event beforeedit
35028              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35029              * <ul style="padding:5px;padding-left:16px;">
35030              * <li>grid - This grid</li>
35031              * <li>record - The record being edited</li>
35032              * <li>field - The field name being edited</li>
35033              * <li>value - The value for the field being edited.</li>
35034              * <li>row - The grid row index</li>
35035              * <li>column - The grid column index</li>
35036              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35037              * </ul>
35038              * @param {Object} e An edit event (see above for description)
35039              */
35040             "beforeedit" : true,
35041             /**
35042              * @event afteredit
35043              * Fires after a cell is edited. <br />
35044              * <ul style="padding:5px;padding-left:16px;">
35045              * <li>grid - This grid</li>
35046              * <li>record - The record being edited</li>
35047              * <li>field - The field name being edited</li>
35048              * <li>value - The value being set</li>
35049              * <li>originalValue - The original value for the field, before the edit.</li>
35050              * <li>row - The grid row index</li>
35051              * <li>column - The grid column index</li>
35052              * </ul>
35053              * @param {Object} e An edit event (see above for description)
35054              */
35055             "afteredit" : true,
35056             /**
35057              * @event validateedit
35058              * Fires after a cell is edited, but before the value is set in the record. 
35059          * You can use this to modify the value being set in the field, Return false
35060              * to cancel the change. The edit event object has the following properties <br />
35061              * <ul style="padding:5px;padding-left:16px;">
35062          * <li>editor - This editor</li>
35063              * <li>grid - This grid</li>
35064              * <li>record - The record being edited</li>
35065              * <li>field - The field name being edited</li>
35066              * <li>value - The value being set</li>
35067              * <li>originalValue - The original value for the field, before the edit.</li>
35068              * <li>row - The grid row index</li>
35069              * <li>column - The grid column index</li>
35070              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35071              * </ul>
35072              * @param {Object} e An edit event (see above for description)
35073              */
35074             "validateedit" : true
35075         });
35076     this.on("bodyscroll", this.stopEditing,  this);
35077     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35078 };
35079
35080 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35081     /**
35082      * @cfg {Number} clicksToEdit
35083      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35084      */
35085     clicksToEdit: 2,
35086
35087     // private
35088     isEditor : true,
35089     // private
35090     trackMouseOver: false, // causes very odd FF errors
35091
35092     onCellDblClick : function(g, row, col){
35093         this.startEditing(row, col);
35094     },
35095
35096     onEditComplete : function(ed, value, startValue){
35097         this.editing = false;
35098         this.activeEditor = null;
35099         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35100         var r = ed.record;
35101         var field = this.colModel.getDataIndex(ed.col);
35102         var e = {
35103             grid: this,
35104             record: r,
35105             field: field,
35106             originalValue: startValue,
35107             value: value,
35108             row: ed.row,
35109             column: ed.col,
35110             cancel:false,
35111             editor: ed
35112         };
35113         if(String(value) !== String(startValue)){
35114             
35115             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35116                 r.set(field, e.value);
35117                 delete e.cancel; //?? why!!!
35118                 this.fireEvent("afteredit", e);
35119             }
35120         } else {
35121             this.fireEvent("afteredit", e); // always fir it!
35122         }
35123         this.view.focusCell(ed.row, ed.col);
35124     },
35125
35126     /**
35127      * Starts editing the specified for the specified row/column
35128      * @param {Number} rowIndex
35129      * @param {Number} colIndex
35130      */
35131     startEditing : function(row, col){
35132         this.stopEditing();
35133         if(this.colModel.isCellEditable(col, row)){
35134             this.view.ensureVisible(row, col, true);
35135             var r = this.dataSource.getAt(row);
35136             var field = this.colModel.getDataIndex(col);
35137             var e = {
35138                 grid: this,
35139                 record: r,
35140                 field: field,
35141                 value: r.data[field],
35142                 row: row,
35143                 column: col,
35144                 cancel:false
35145             };
35146             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35147                 this.editing = true;
35148                 var ed = this.colModel.getCellEditor(col, row);
35149                 
35150                 if (!ed) {
35151                     return;
35152                 }
35153                 if(!ed.rendered){
35154                     ed.render(ed.parentEl || document.body);
35155                 }
35156                 ed.field.reset();
35157                 (function(){ // complex but required for focus issues in safari, ie and opera
35158                     ed.row = row;
35159                     ed.col = col;
35160                     ed.record = r;
35161                     ed.on("complete", this.onEditComplete, this, {single: true});
35162                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35163                     this.activeEditor = ed;
35164                     var v = r.data[field];
35165                     ed.startEdit(this.view.getCell(row, col), v);
35166                 }).defer(50, this);
35167             }
35168         }
35169     },
35170         
35171     /**
35172      * Stops any active editing
35173      */
35174     stopEditing : function(){
35175         if(this.activeEditor){
35176             this.activeEditor.completeEdit();
35177         }
35178         this.activeEditor = null;
35179     }
35180 });/*
35181  * Based on:
35182  * Ext JS Library 1.1.1
35183  * Copyright(c) 2006-2007, Ext JS, LLC.
35184  *
35185  * Originally Released Under LGPL - original licence link has changed is not relivant.
35186  *
35187  * Fork - LGPL
35188  * <script type="text/javascript">
35189  */
35190
35191 // private - not really -- you end up using it !
35192 // This is a support class used internally by the Grid components
35193
35194 /**
35195  * @class Roo.grid.GridEditor
35196  * @extends Roo.Editor
35197  * Class for creating and editable grid elements.
35198  * @param {Object} config any settings (must include field)
35199  */
35200 Roo.grid.GridEditor = function(field, config){
35201     if (!config && field.field) {
35202         config = field;
35203         field = Roo.factory(config.field, Roo.form);
35204     }
35205     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35206     field.monitorTab = false;
35207 };
35208
35209 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35210     
35211     /**
35212      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35213      */
35214     
35215     alignment: "tl-tl",
35216     autoSize: "width",
35217     hideEl : false,
35218     cls: "x-small-editor x-grid-editor",
35219     shim:false,
35220     shadow:"frame"
35221 });/*
35222  * Based on:
35223  * Ext JS Library 1.1.1
35224  * Copyright(c) 2006-2007, Ext JS, LLC.
35225  *
35226  * Originally Released Under LGPL - original licence link has changed is not relivant.
35227  *
35228  * Fork - LGPL
35229  * <script type="text/javascript">
35230  */
35231   
35232
35233   
35234 Roo.grid.PropertyRecord = Roo.data.Record.create([
35235     {name:'name',type:'string'},  'value'
35236 ]);
35237
35238
35239 Roo.grid.PropertyStore = function(grid, source){
35240     this.grid = grid;
35241     this.store = new Roo.data.Store({
35242         recordType : Roo.grid.PropertyRecord
35243     });
35244     this.store.on('update', this.onUpdate,  this);
35245     if(source){
35246         this.setSource(source);
35247     }
35248     Roo.grid.PropertyStore.superclass.constructor.call(this);
35249 };
35250
35251
35252
35253 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35254     setSource : function(o){
35255         this.source = o;
35256         this.store.removeAll();
35257         var data = [];
35258         for(var k in o){
35259             if(this.isEditableValue(o[k])){
35260                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35261             }
35262         }
35263         this.store.loadRecords({records: data}, {}, true);
35264     },
35265
35266     onUpdate : function(ds, record, type){
35267         if(type == Roo.data.Record.EDIT){
35268             var v = record.data['value'];
35269             var oldValue = record.modified['value'];
35270             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35271                 this.source[record.id] = v;
35272                 record.commit();
35273                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35274             }else{
35275                 record.reject();
35276             }
35277         }
35278     },
35279
35280     getProperty : function(row){
35281        return this.store.getAt(row);
35282     },
35283
35284     isEditableValue: function(val){
35285         if(val && val instanceof Date){
35286             return true;
35287         }else if(typeof val == 'object' || typeof val == 'function'){
35288             return false;
35289         }
35290         return true;
35291     },
35292
35293     setValue : function(prop, value){
35294         this.source[prop] = value;
35295         this.store.getById(prop).set('value', value);
35296     },
35297
35298     getSource : function(){
35299         return this.source;
35300     }
35301 });
35302
35303 Roo.grid.PropertyColumnModel = function(grid, store){
35304     this.grid = grid;
35305     var g = Roo.grid;
35306     g.PropertyColumnModel.superclass.constructor.call(this, [
35307         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35308         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35309     ]);
35310     this.store = store;
35311     this.bselect = Roo.DomHelper.append(document.body, {
35312         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
35313             {tag: 'option', value: 'true', html: 'true'},
35314             {tag: 'option', value: 'false', html: 'false'}
35315         ]
35316     });
35317     Roo.id(this.bselect);
35318     var f = Roo.form;
35319     this.editors = {
35320         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
35321         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
35322         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
35323         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
35324         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
35325     };
35326     this.renderCellDelegate = this.renderCell.createDelegate(this);
35327     this.renderPropDelegate = this.renderProp.createDelegate(this);
35328 };
35329
35330 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
35331     
35332     
35333     nameText : 'Name',
35334     valueText : 'Value',
35335     
35336     dateFormat : 'm/j/Y',
35337     
35338     
35339     renderDate : function(dateVal){
35340         return dateVal.dateFormat(this.dateFormat);
35341     },
35342
35343     renderBool : function(bVal){
35344         return bVal ? 'true' : 'false';
35345     },
35346
35347     isCellEditable : function(colIndex, rowIndex){
35348         return colIndex == 1;
35349     },
35350
35351     getRenderer : function(col){
35352         return col == 1 ?
35353             this.renderCellDelegate : this.renderPropDelegate;
35354     },
35355
35356     renderProp : function(v){
35357         return this.getPropertyName(v);
35358     },
35359
35360     renderCell : function(val){
35361         var rv = val;
35362         if(val instanceof Date){
35363             rv = this.renderDate(val);
35364         }else if(typeof val == 'boolean'){
35365             rv = this.renderBool(val);
35366         }
35367         return Roo.util.Format.htmlEncode(rv);
35368     },
35369
35370     getPropertyName : function(name){
35371         var pn = this.grid.propertyNames;
35372         return pn && pn[name] ? pn[name] : name;
35373     },
35374
35375     getCellEditor : function(colIndex, rowIndex){
35376         var p = this.store.getProperty(rowIndex);
35377         var n = p.data['name'], val = p.data['value'];
35378         
35379         if(typeof(this.grid.customEditors[n]) == 'string'){
35380             return this.editors[this.grid.customEditors[n]];
35381         }
35382         if(typeof(this.grid.customEditors[n]) != 'undefined'){
35383             return this.grid.customEditors[n];
35384         }
35385         if(val instanceof Date){
35386             return this.editors['date'];
35387         }else if(typeof val == 'number'){
35388             return this.editors['number'];
35389         }else if(typeof val == 'boolean'){
35390             return this.editors['boolean'];
35391         }else{
35392             return this.editors['string'];
35393         }
35394     }
35395 });
35396
35397 /**
35398  * @class Roo.grid.PropertyGrid
35399  * @extends Roo.grid.EditorGrid
35400  * This class represents the  interface of a component based property grid control.
35401  * <br><br>Usage:<pre><code>
35402  var grid = new Roo.grid.PropertyGrid("my-container-id", {
35403       
35404  });
35405  // set any options
35406  grid.render();
35407  * </code></pre>
35408   
35409  * @constructor
35410  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35411  * The container MUST have some type of size defined for the grid to fill. The container will be
35412  * automatically set to position relative if it isn't already.
35413  * @param {Object} config A config object that sets properties on this grid.
35414  */
35415 Roo.grid.PropertyGrid = function(container, config){
35416     config = config || {};
35417     var store = new Roo.grid.PropertyStore(this);
35418     this.store = store;
35419     var cm = new Roo.grid.PropertyColumnModel(this, store);
35420     store.store.sort('name', 'ASC');
35421     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
35422         ds: store.store,
35423         cm: cm,
35424         enableColLock:false,
35425         enableColumnMove:false,
35426         stripeRows:false,
35427         trackMouseOver: false,
35428         clicksToEdit:1
35429     }, config));
35430     this.getGridEl().addClass('x-props-grid');
35431     this.lastEditRow = null;
35432     this.on('columnresize', this.onColumnResize, this);
35433     this.addEvents({
35434          /**
35435              * @event beforepropertychange
35436              * Fires before a property changes (return false to stop?)
35437              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35438              * @param {String} id Record Id
35439              * @param {String} newval New Value
35440          * @param {String} oldval Old Value
35441              */
35442         "beforepropertychange": true,
35443         /**
35444              * @event propertychange
35445              * Fires after a property changes
35446              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
35447              * @param {String} id Record Id
35448              * @param {String} newval New Value
35449          * @param {String} oldval Old Value
35450              */
35451         "propertychange": true
35452     });
35453     this.customEditors = this.customEditors || {};
35454 };
35455 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
35456     
35457      /**
35458      * @cfg {Object} customEditors map of colnames=> custom editors.
35459      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
35460      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
35461      * false disables editing of the field.
35462          */
35463     
35464       /**
35465      * @cfg {Object} propertyNames map of property Names to their displayed value
35466          */
35467     
35468     render : function(){
35469         Roo.grid.PropertyGrid.superclass.render.call(this);
35470         this.autoSize.defer(100, this);
35471     },
35472
35473     autoSize : function(){
35474         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
35475         if(this.view){
35476             this.view.fitColumns();
35477         }
35478     },
35479
35480     onColumnResize : function(){
35481         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
35482         this.autoSize();
35483     },
35484     /**
35485      * Sets the data for the Grid
35486      * accepts a Key => Value object of all the elements avaiable.
35487      * @param {Object} data  to appear in grid.
35488      */
35489     setSource : function(source){
35490         this.store.setSource(source);
35491         //this.autoSize();
35492     },
35493     /**
35494      * Gets all the data from the grid.
35495      * @return {Object} data  data stored in grid
35496      */
35497     getSource : function(){
35498         return this.store.getSource();
35499     }
35500 });/*
35501  * Based on:
35502  * Ext JS Library 1.1.1
35503  * Copyright(c) 2006-2007, Ext JS, LLC.
35504  *
35505  * Originally Released Under LGPL - original licence link has changed is not relivant.
35506  *
35507  * Fork - LGPL
35508  * <script type="text/javascript">
35509  */
35510  
35511 /**
35512  * @class Roo.LoadMask
35513  * A simple utility class for generically masking elements while loading data.  If the element being masked has
35514  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
35515  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
35516  * element's UpdateManager load indicator and will be destroyed after the initial load.
35517  * @constructor
35518  * Create a new LoadMask
35519  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
35520  * @param {Object} config The config object
35521  */
35522 Roo.LoadMask = function(el, config){
35523     this.el = Roo.get(el);
35524     Roo.apply(this, config);
35525     if(this.store){
35526         this.store.on('beforeload', this.onBeforeLoad, this);
35527         this.store.on('load', this.onLoad, this);
35528         this.store.on('loadexception', this.onLoad, this);
35529         this.removeMask = false;
35530     }else{
35531         var um = this.el.getUpdateManager();
35532         um.showLoadIndicator = false; // disable the default indicator
35533         um.on('beforeupdate', this.onBeforeLoad, this);
35534         um.on('update', this.onLoad, this);
35535         um.on('failure', this.onLoad, this);
35536         this.removeMask = true;
35537     }
35538 };
35539
35540 Roo.LoadMask.prototype = {
35541     /**
35542      * @cfg {Boolean} removeMask
35543      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
35544      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
35545      */
35546     /**
35547      * @cfg {String} msg
35548      * The text to display in a centered loading message box (defaults to 'Loading...')
35549      */
35550     msg : 'Loading...',
35551     /**
35552      * @cfg {String} msgCls
35553      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
35554      */
35555     msgCls : 'x-mask-loading',
35556
35557     /**
35558      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
35559      * @type Boolean
35560      */
35561     disabled: false,
35562
35563     /**
35564      * Disables the mask to prevent it from being displayed
35565      */
35566     disable : function(){
35567        this.disabled = true;
35568     },
35569
35570     /**
35571      * Enables the mask so that it can be displayed
35572      */
35573     enable : function(){
35574         this.disabled = false;
35575     },
35576
35577     // private
35578     onLoad : function(){
35579         this.el.unmask(this.removeMask);
35580     },
35581
35582     // private
35583     onBeforeLoad : function(){
35584         if(!this.disabled){
35585             this.el.mask(this.msg, this.msgCls);
35586         }
35587     },
35588
35589     // private
35590     destroy : function(){
35591         if(this.store){
35592             this.store.un('beforeload', this.onBeforeLoad, this);
35593             this.store.un('load', this.onLoad, this);
35594             this.store.un('loadexception', this.onLoad, this);
35595         }else{
35596             var um = this.el.getUpdateManager();
35597             um.un('beforeupdate', this.onBeforeLoad, this);
35598             um.un('update', this.onLoad, this);
35599             um.un('failure', this.onLoad, this);
35600         }
35601     }
35602 };/*
35603  * Based on:
35604  * Ext JS Library 1.1.1
35605  * Copyright(c) 2006-2007, Ext JS, LLC.
35606  *
35607  * Originally Released Under LGPL - original licence link has changed is not relivant.
35608  *
35609  * Fork - LGPL
35610  * <script type="text/javascript">
35611  */
35612 Roo.XTemplate = function(){
35613     Roo.XTemplate.superclass.constructor.apply(this, arguments);
35614     var s = this.html;
35615
35616     s = ['<tpl>', s, '</tpl>'].join('');
35617
35618     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
35619
35620     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
35621     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
35622     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
35623     var m, id = 0;
35624     var tpls = [];
35625
35626     while(m = s.match(re)){
35627        var m2 = m[0].match(nameRe);
35628        var m3 = m[0].match(ifRe);
35629        var m4 = m[0].match(execRe);
35630        var exp = null, fn = null, exec = null;
35631        var name = m2 && m2[1] ? m2[1] : '';
35632        if(m3){
35633            exp = m3 && m3[1] ? m3[1] : null;
35634            if(exp){
35635                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
35636            }
35637        }
35638        if(m4){
35639            exp = m4 && m4[1] ? m4[1] : null;
35640            if(exp){
35641                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
35642            }
35643        }
35644        if(name){
35645            switch(name){
35646                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
35647                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
35648                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
35649            }
35650        }
35651        tpls.push({
35652             id: id,
35653             target: name,
35654             exec: exec,
35655             test: fn,
35656             body: m[1]||''
35657         });
35658        s = s.replace(m[0], '{xtpl'+ id + '}');
35659        ++id;
35660     }
35661     for(var i = tpls.length-1; i >= 0; --i){
35662         this.compileTpl(tpls[i]);
35663     }
35664     this.master = tpls[tpls.length-1];
35665     this.tpls = tpls;
35666 };
35667 Roo.extend(Roo.XTemplate, Roo.Template, {
35668
35669     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
35670
35671     applySubTemplate : function(id, values, parent){
35672         var t = this.tpls[id];
35673         if(t.test && !t.test.call(this, values, parent)){
35674             return '';
35675         }
35676         if(t.exec && t.exec.call(this, values, parent)){
35677             return '';
35678         }
35679         var vs = t.target ? t.target.call(this, values, parent) : values;
35680         parent = t.target ? values : parent;
35681         if(t.target && vs instanceof Array){
35682             var buf = [];
35683             for(var i = 0, len = vs.length; i < len; i++){
35684                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
35685             }
35686             return buf.join('');
35687         }
35688         return t.compiled.call(this, vs, parent);
35689     },
35690
35691     compileTpl : function(tpl){
35692         var fm = Roo.util.Format;
35693         var useF = this.disableFormats !== true;
35694         var sep = Roo.isGecko ? "+" : ",";
35695         var fn = function(m, name, format, args){
35696             if(name.substr(0, 4) == 'xtpl'){
35697                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
35698             }
35699             var v;
35700             if(name.indexOf('.') != -1){
35701                 v = name;
35702             }else{
35703                 v = "values['" + name + "']";
35704             }
35705             if(format && useF){
35706                 args = args ? ',' + args : "";
35707                 if(format.substr(0, 5) != "this."){
35708                     format = "fm." + format + '(';
35709                 }else{
35710                     format = 'this.call("'+ format.substr(5) + '", ';
35711                     args = ", values";
35712                 }
35713             }else{
35714                 args= ''; format = "("+v+" === undefined ? '' : ";
35715             }
35716             return "'"+ sep + format + v + args + ")"+sep+"'";
35717         };
35718         var body;
35719         // branched to use + in gecko and [].join() in others
35720         if(Roo.isGecko){
35721             body = "tpl.compiled = function(values, parent){ return '" +
35722                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
35723                     "';};";
35724         }else{
35725             body = ["tpl.compiled = function(values, parent){ return ['"];
35726             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
35727             body.push("'].join('');};");
35728             body = body.join('');
35729         }
35730         /** eval:var:zzzzzzz */
35731         eval(body);
35732         return this;
35733     },
35734
35735     applyTemplate : function(values){
35736         return this.master.compiled.call(this, values, {});
35737         var s = this.subs;
35738     },
35739
35740     apply : function(){
35741         return this.applyTemplate.apply(this, arguments);
35742     },
35743
35744     compile : function(){return this;}
35745 });
35746
35747 Roo.XTemplate.from = function(el){
35748     el = Roo.getDom(el);
35749     return new Roo.XTemplate(el.value || el.innerHTML);
35750 };/*
35751  * Original code for Roojs - LGPL
35752  * <script type="text/javascript">
35753  */
35754  
35755 /**
35756  * @class Roo.XComponent
35757  * A delayed Element creator...
35758  * 
35759  * Mypart.xyx = new Roo.XComponent({
35760
35761     parent : 'Mypart.xyz', // empty == document.element.!!
35762     order : '001',
35763     name : 'xxxx'
35764     region : 'xxxx'
35765     disabled : function() {} 
35766      
35767     tree : function() { // return an tree of xtype declared components
35768         var MODULE = this;
35769         return 
35770         {
35771             xtype : 'NestedLayoutPanel',
35772             // technicall
35773         }
35774      ]
35775  *})
35776  * @extends Roo.util.Observable
35777  * @constructor
35778  * @param cfg {Object} configuration of component
35779  * 
35780  */
35781 Roo.XComponent = function(cfg) {
35782     Roo.apply(this, cfg);
35783     this.addEvents({ 
35784         /**
35785              * @event built
35786              * Fires when this the componnt is built
35787              * @param {Roo.XComponent} c the component
35788              */
35789         'built' : true,
35790         /**
35791              * @event buildcomplete
35792              * Fires on the top level element when all elements have been built
35793              * @param {Roo.XComponent} c the top level component.
35794          */
35795         'buildcomplete' : true
35796         
35797     });
35798     
35799     Roo.XComponent.register(this);
35800     this.modules = false;
35801     this.el = false; // where the layout goes..
35802     
35803     
35804 }
35805 Roo.extend(Roo.XComponent, Roo.util.Observable, {
35806     /**
35807      * @property el
35808      * The created element (with Roo.factory())
35809      * @type {Roo.Layout}
35810      */
35811     el  : false,
35812     
35813     /**
35814      * @property el
35815      * for BC  - use el in new code
35816      * @type {Roo.Layout}
35817      */
35818     panel : false,
35819     
35820     /**
35821      * @property layout
35822      * for BC  - use el in new code
35823      * @type {Roo.Layout}
35824      */
35825     layout : false,
35826     
35827      /**
35828      * @cfg {Function|boolean} disabled
35829      * If this module is disabled by some rule, return true from the funtion
35830      */
35831     disabled : false,
35832     
35833     /**
35834      * @cfg {String} parent 
35835      * Name of parent element which it get xtype added to..
35836      */
35837     parent: false,
35838     
35839     /**
35840      * @cfg {String} order
35841      * Used to set the order in which elements are created (usefull for multiple tabs)
35842      */
35843     
35844     order : false,
35845     /**
35846      * @cfg {String} name
35847      * String to display while loading.
35848      */
35849     name : false,
35850     /**
35851      * @cfg {Array} items
35852      * A single item array - the first element is the root of the tree..
35853      * It's done this way to stay compatible with the Xtype system...
35854      */
35855     items : false
35856      
35857      
35858     
35859 });
35860
35861 Roo.apply(Roo.XComponent, {
35862     
35863     /**
35864      * @property  buildCompleted
35865      * True when the builder has completed building the interface.
35866      * @type Boolean
35867      */
35868     buildCompleted : false,
35869      
35870     /**
35871      * @property  topModule
35872      * the upper most module - uses document.element as it's constructor.
35873      * @type Object
35874      */
35875      
35876     topModule  : false,
35877       
35878     /**
35879      * @property  modules
35880      * array of modules to be created by registration system.
35881      * @type Roo.XComponent
35882      */
35883     
35884     modules : [],
35885       
35886     
35887     /**
35888      * Register components to be built later.
35889      *
35890      * This solves the following issues
35891      * - Building is not done on page load, but after an authentication process has occured.
35892      * - Interface elements are registered on page load
35893      * - Parent Interface elements may not be loaded before child, so this handles that..
35894      * 
35895      *
35896      * example:
35897      * 
35898      * MyApp.register({
35899           order : '000001',
35900           module : 'Pman.Tab.projectMgr',
35901           region : 'center',
35902           parent : 'Pman.layout',
35903           disabled : false,  // or use a function..
35904         })
35905      
35906      * * @param {Object} details about module
35907      */
35908     register : function(obj) {
35909         this.modules.push(obj);
35910          
35911     },
35912     /**
35913      * convert a string to an object..
35914      * 
35915      */
35916     
35917     toObject : function(str)
35918     {
35919         if (!str || typeof(str) == 'object') {
35920             return str;
35921         }
35922         var ar = str.split('.');
35923         var rt, o;
35924         rt = ar.shift();
35925             /** eval:var:o */
35926         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
35927         if (o === false) {
35928             throw "Module not found : " + str;
35929         }
35930         Roo.each(ar, function(e) {
35931             if (typeof(o[e]) == 'undefined') {
35932                 throw "Module not found : " + str;
35933             }
35934             o = o[e];
35935         });
35936         return o;
35937         
35938     },
35939     
35940     
35941     /**
35942      * move modules into their correct place in the tree..
35943      * 
35944      */
35945     preBuild : function ()
35946     {
35947         
35948         Roo.each(this.modules , function (obj)
35949         {
35950             obj.parent = this.toObject(obj.parent);
35951             
35952             if (!obj.parent) {
35953                 this.topModule = obj;
35954                 return;
35955             }
35956             
35957             if (!obj.parent.modules) {
35958                 obj.parent.modules = new Roo.util.MixedCollection(false, 
35959                     function(o) { return o.order + '' }
35960                 );
35961             }
35962             
35963             obj.parent.modules.add(obj);
35964         }, this);
35965     },
35966     
35967      /**
35968      * make a list of modules to build.
35969      * @return {Array} list of modules. 
35970      */ 
35971     
35972     buildOrder : function()
35973     {
35974         var _this = this;
35975         var cmp = function(a,b) {   
35976             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
35977         };
35978         
35979         if (!this.topModule || !this.topModule.modules) {
35980             throw "No top level modules to build";
35981         }
35982        
35983         // make a flat list in order of modules to build.
35984         var mods = [ this.topModule ];
35985         
35986         
35987         // add modules to their parents..
35988         var addMod = function(m) {
35989            // Roo.debug && Roo.log(m.modKey);
35990             
35991             mods.push(m);
35992             if (m.modules) {
35993                 m.modules.keySort('ASC',  cmp );
35994                 m.modules.each(addMod);
35995             }
35996             // not sure if this is used any more..
35997             if (m.finalize) {
35998                 m.finalize.name = m.name + " (clean up) ";
35999                 mods.push(m.finalize);
36000             }
36001             
36002         }
36003         this.topModule.modules.keySort('ASC',  cmp );
36004         this.topModule.modules.each(addMod);
36005         return mods;
36006     },
36007     
36008      /**
36009      * Build the registered modules.
36010      * @param {Object} parent element.
36011      * @param {Function} optional method to call after module has been added.
36012      * 
36013      */ 
36014    
36015     build : function() 
36016     {
36017         
36018         this.preBuild();
36019         var mods = this.buildOrder();
36020       
36021         //this.allmods = mods;
36022         //Roo.debug && Roo.log(mods);
36023         //return;
36024         if (!mods.length) { // should not happen
36025             throw "NO modules!!!";
36026         }
36027         
36028         
36029         
36030         // flash it up as modal - so we store the mask!?
36031         Roo.MessageBox.show({ title: 'loading' });
36032         Roo.MessageBox.show({
36033            title: "Please wait...",
36034            msg: "Building Interface...",
36035            width:450,
36036            progress:true,
36037            closable:false,
36038            modal: false
36039           
36040         });
36041         var total = mods.length;
36042         
36043         var _this = this;
36044         var progressRun = function() {
36045             if (!mods.length) {
36046                 Roo.debug && Roo.log('hide?');
36047                 Roo.MessageBox.hide();
36048                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36049                 return;    
36050             }
36051             
36052             var m = mods.shift();
36053             Roo.debug && Roo.log(m);
36054             if (typeof(m) == 'function') { // not sure if this is supported any more..
36055                 m.call(this);
36056                 return progressRun.defer(10, _this);
36057             } 
36058             
36059             Roo.MessageBox.updateProgress(
36060                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36061                     " of " + total + 
36062                     (m.name ? (' - ' + m.name) : '')
36063                     );
36064             
36065          
36066             
36067             var disabled = (typeof(m.disabled) == 'function') ?
36068                 m.disabled.call(m.module.disabled) : m.disabled;    
36069             
36070             
36071             if (disabled) {
36072                 return progressRun(); // we do not update the display!
36073             }
36074             
36075             if (!m.parent) {
36076                 // it's a top level one..
36077                 var layoutbase = new Ext.BorderLayout(document.body, {
36078                
36079                     center: {
36080                          titlebar: false,
36081                          autoScroll:false,
36082                          closeOnTab: true,
36083                          tabPosition: 'top',
36084                          //resizeTabs: true,
36085                          alwaysShowTabs: true,
36086                          minTabWidth: 140
36087                     }
36088                 });
36089                 var tree = m.tree();
36090                 tree.region = 'center';
36091                 m.el = layoutbase.addxtype(tree);
36092                 m.panel = m.el;
36093                 m.layout = m.panel.layout;    
36094                 return progressRun.defer(10, _this);
36095             }
36096             
36097             var tree = m.tree();
36098             tree.region = tree.region || m.region;
36099             m.el = m.parent.el.addxtype(tree);
36100             m.fireEvent('built', m);
36101             m.panel = m.el;
36102             m.layout = m.panel.layout;    
36103             progressRun.defer(10, _this); 
36104             
36105         }
36106         progressRun.defer(1, _this);
36107      
36108         
36109         
36110     }
36111      
36112    
36113     
36114     
36115 });
36116  //<script type="text/javascript">
36117
36118
36119 /**
36120  * @class Roo.Login
36121  * @extends Roo.LayoutDialog
36122  * A generic Login Dialog..... - only one needed in theory!?!?
36123  *
36124  * Fires XComponent builder on success...
36125  * 
36126  * Sends 
36127  *    username,password, lang = for login actions.
36128  *    check = 1 for periodic checking that sesion is valid.
36129  *    passwordRequest = email request password
36130  *    logout = 1 = to logout
36131  * 
36132  * Affects: (this id="????" elements)
36133  *   loading  (removed) (used to indicate application is loading)
36134  *   loading-mask (hides) (used to hide application when it's building loading)
36135  *   
36136  * 
36137  * Usage: 
36138  *    
36139  * 
36140  * Myapp.login = Roo.Login({
36141      url: xxxx,
36142    
36143      realm : 'Myapp', 
36144      
36145      
36146      method : 'POST',
36147      
36148      
36149      * 
36150  })
36151  * 
36152  * 
36153  * 
36154  **/
36155  
36156 Roo.Login = function(cfg)
36157 {
36158     this.addEvents({
36159         'refreshed' : true
36160     });
36161     
36162     Roo.apply(this,cfg);
36163     
36164     Roo.onReady(function() {
36165         this.onLoad();
36166     }, this);
36167     // call parent..
36168     
36169    
36170     Roo.Login.superclass.constructor.call(this, this);
36171     //this.addxtype(this.items[0]);
36172     
36173     
36174 }
36175
36176
36177 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36178     
36179     /**
36180      * @cfg {String} method
36181      * Method used to query for login details.
36182      */
36183     
36184     method : 'POST',
36185     /**
36186      * @cfg {String} url
36187      * URL to query login data. - eg. baseURL + '/Login.php'
36188      */
36189     url : '',
36190     
36191     /**
36192      * @property user
36193      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36194      * @type {Object} 
36195      */
36196     user : false,
36197     /**
36198      * @property checkFails
36199      * Number of times we have attempted to get authentication check, and failed.
36200      * @type {Number} 
36201      */
36202     checkFails : 0,
36203       /**
36204      * @property intervalID
36205      * The window interval that does the constant login checking.
36206      * @type {Number} 
36207      */
36208     intervalID : 0,
36209     
36210     
36211     onLoad : function() // called on page load...
36212     {
36213         // load 
36214          
36215         if (Roo.get('loading')) { // clear any loading indicator..
36216             Roo.get('loading').remove();
36217         }
36218         
36219         //this.switchLang('en'); // set the language to english..
36220        
36221         this.check({
36222             success:  function(response, opts)  {  // check successfull...
36223             
36224                 var res = this.processResponse(response);
36225                 this.checkFails =0;
36226                 if (!res.success) { // error!
36227                     this.checkFails = 5;
36228                     //console.log('call failure');
36229                     return this.failure(response,opts);
36230                 }
36231                 
36232                 if (!res.data.id) { // id=0 == login failure.
36233                     return this.show();
36234                 }
36235                 
36236                               
36237                         //console.log(success);
36238                 this.fillAuth(res.data);   
36239                 this.checkFails =0;
36240                 Roo.XComponent.build();
36241             },
36242             failure : this.show
36243         });
36244         
36245     }, 
36246     
36247     
36248     check: function(cfg) // called every so often to refresh cookie etc..
36249     {
36250         if (cfg.again) { // could be undefined..
36251             this.checkFails++;
36252         } else {
36253             this.checkFails = 0;
36254         }
36255         var _this = this;
36256         if (this.sending) {
36257             if ( this.checkFails > 4) {
36258                 Roo.MessageBox.alert("Error",  
36259                     "Error getting authentication status. - try reloading, or wait a while", function() {
36260                         _this.sending = false;
36261                     }); 
36262                 return;
36263             }
36264             cfg.again = true;
36265             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36266             return;
36267         }
36268         this.sending = true;
36269         
36270         Roo.Ajax.request({  
36271             url: this.url,
36272             params: {
36273                 getAuthUser: true
36274             },  
36275             method: this.method,
36276             success:  cfg.success || this.success,
36277             failure : cfg.failure || this.failure,
36278             scope : this,
36279             callCfg : cfg
36280               
36281         });  
36282     }, 
36283     
36284     
36285     logout: function()
36286     {
36287         window.onbeforeunload = function() { }; // false does not work for IE..
36288         this.user = false;
36289         var _this = this;
36290         
36291         Roo.Ajax.request({  
36292             url: this.url,
36293             params: {
36294                 logout: 1
36295             },  
36296             method: 'GET',
36297             failure : function() {
36298                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36299                     document.location = document.location.toString() + '?ts=' + Math.random();
36300                 });
36301                 
36302             },
36303             success : function() {
36304                 _this.user = false;
36305                 this.checkFails =0;
36306                 // fixme..
36307                 document.location = document.location.toString() + '?ts=' + Math.random();
36308             }
36309               
36310               
36311         }); 
36312     },
36313     
36314     processResponse : function (response)
36315     {
36316         var res = '';
36317         try {
36318             res = Roo.decode(response.responseText);
36319             // oops...
36320             if (typeof(res) != 'object') {
36321                 res = { success : false, errorMsg : res, errors : true };
36322             }
36323             if (typeof(res.success) == 'undefined') {
36324                 res.success = false;
36325             }
36326             
36327         } catch(e) {
36328             res = { success : false,  errorMsg : response.responseText, errors : true };
36329         }
36330         return res;
36331     },
36332     
36333     success : function(response, opts)  // check successfull...
36334     {  
36335         this.sending = false;
36336         var res = this.processResponse(response);
36337         if (!res.success) {
36338             return this.failure(response, opts);
36339         }
36340         if (!res.data || !res.data.id) {
36341             return this.failure(response,opts);
36342         }
36343         //console.log(res);
36344         this.fillAuth(res.data);
36345         
36346         this.checkFails =0;
36347         
36348     },
36349     
36350     
36351     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
36352     {
36353         this.authUser = -1;
36354         this.sending = false;
36355         var res = this.processResponse(response);
36356         //console.log(res);
36357         if ( this.checkFails > 2) {
36358         
36359             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
36360                 "Error getting authentication status. - try reloading"); 
36361             return;
36362         }
36363         opts.callCfg.again = true;
36364         this.check.defer(1000, this, [ opts.callCfg ]);
36365         return;  
36366     },
36367     
36368     
36369     
36370     fillAuth: function(au) {
36371         this.startAuthCheck();
36372         this.authUserId = au.id;
36373         this.authUser = au;
36374         this.lastChecked = new Date();
36375         this.fireEvent('refreshed', au);
36376         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
36377         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
36378         au.lang = au.lang || 'en';
36379         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
36380         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
36381         this.switchLang(au.lang );
36382         
36383      
36384         // open system... - -on setyp..
36385         if (this.authUserId  < 0) {
36386             Roo.MessageBox.alert("Warning", 
36387                 "This is an open system - please set up a admin user with a password.");  
36388         }
36389          
36390         //Pman.onload(); // which should do nothing if it's a re-auth result...
36391         
36392              
36393     },
36394     
36395     startAuthCheck : function() // starter for timeout checking..
36396     {
36397         if (this.intervalID) { // timer already in place...
36398             return false;
36399         }
36400         var _this = this;
36401         this.intervalID =  window.setInterval(function() {
36402               _this.check(false);
36403             }, 120000); // every 120 secs = 2mins..
36404         
36405         
36406     },
36407          
36408     
36409     switchLang : function (lang) 
36410     {
36411         _T = typeof(_T) == 'undefined' ? false : _T;
36412           if (!_T || !lang.length) {
36413             return;
36414         }
36415         
36416         if (!_T && lang != 'en') {
36417             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36418             return;
36419         }
36420         
36421         if (typeof(_T.en) == 'undefined') {
36422             _T.en = {};
36423             Roo.apply(_T.en, _T);
36424         }
36425         
36426         if (typeof(_T[lang]) == 'undefined') {
36427             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36428             return;
36429         }
36430         
36431         
36432         Roo.apply(_T, _T[lang]);
36433         // just need to set the text values for everything...
36434         var _this = this;
36435         /* this will not work ...
36436         if (this.form) { 
36437             
36438                
36439             function formLabel(name, val) {
36440                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
36441             }
36442             
36443             formLabel('password', "Password"+':');
36444             formLabel('username', "Email Address"+':');
36445             formLabel('lang', "Language"+':');
36446             this.dialog.setTitle("Login");
36447             this.dialog.buttons[0].setText("Forgot Password");
36448             this.dialog.buttons[1].setText("Login");
36449         }
36450         */
36451         
36452         
36453     },
36454     
36455     
36456     title: "Login",
36457     modal: true,
36458     width:  350,
36459     //height: 230,
36460     height: 180,
36461     shadow: true,
36462     minWidth:200,
36463     minHeight:180,
36464     //proxyDrag: true,
36465     closable: false,
36466     draggable: false,
36467     collapsible: false,
36468     resizable: false,
36469     center: {  // needed??
36470         autoScroll:false,
36471         titlebar: false,
36472        // tabPosition: 'top',
36473         hideTabs: true,
36474         closeOnTab: true,
36475         alwaysShowTabs: false
36476     } ,
36477     listeners : {
36478         
36479         show  : function(dlg)
36480         {
36481             //console.log(this);
36482             this.form = this.layout.getRegion('center').activePanel.form;
36483             this.form.dialog = dlg;
36484             this.buttons[0].form = this.form;
36485             this.buttons[0].dialog = dlg
36486             this.buttons[1].form = this.form;
36487             this.buttons[1].dialog = dlg;
36488            
36489            //this.resizeToLogo.defer(1000,this);
36490             // this is all related to resizing for logos..
36491             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
36492            //// if (!sz) {
36493              //   this.resizeToLogo.defer(1000,this);
36494              //   return;
36495            // }
36496             //var w = Ext.lib.Dom.getViewWidth() - 100;
36497             //var h = Ext.lib.Dom.getViewHeight() - 100;
36498             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
36499             //this.center();
36500             if (this.disabled) {
36501                 this.hide();
36502                 return;
36503             }
36504             
36505             if (this.user.id < 0) { // used for inital setup situations.
36506                 return;
36507             }
36508             
36509             if (this.intervalID) {
36510                 // remove the timer
36511                 window.clearInterval(this.intervalID);
36512                 this.intervalID = false;
36513             }
36514             
36515             
36516             if (Roo.get('loading')) {
36517                 Roo.get('loading').remove();
36518             }
36519             if (Roo.get('loading-mask')) {
36520                 Roo.get('loading-mask').hide();
36521             }
36522             
36523             //incomming._node = tnode;
36524             this.form.reset();
36525             //this.dialog.modal = !modal;
36526             //this.dialog.show();
36527             this.el.unmask(); 
36528             
36529             
36530             this.form.setValues({
36531                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
36532                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
36533             });
36534             
36535             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
36536             if (this.form.findField('username').getValue().length > 0 ){
36537                 this.form.findField('password').focus();
36538             } else {
36539                this.form.findField('username').focus();
36540             }
36541     
36542         }
36543     },
36544     items : [
36545          {
36546        
36547             xtype : 'ContentPanel',
36548             xns : Roo,
36549             region: 'center',
36550             fitToFrame : true,
36551             
36552             items : [
36553     
36554                 {
36555                
36556                     xtype : 'Form',
36557                     xns : Roo.form,
36558                     labelWidth: 100,
36559                     style : 'margin: 10px;',
36560                     
36561                     listeners : {
36562                         actionfailed : function(f, act) {
36563                             // form can return { errors: .... }
36564                                 
36565                             //act.result.errors // invalid form element list...
36566                             //act.result.errorMsg// invalid form element list...
36567                             
36568                             this.dialog.el.unmask();
36569                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
36570                                         "Login failed - communication error - try again.");
36571                                       
36572                         },
36573                         actioncomplete: function(re, act) {
36574                              
36575                             Roo.state.Manager.set(
36576                                 this.dialog.realm + '.username',  
36577                                     this.findField('username').getValue()
36578                             );
36579                             Roo.state.Manager.set(
36580                                 this.dialog.realm + '.lang',  
36581                                 this.findField('lang').getValue() 
36582                             );
36583                             
36584                             this.dialog.fillAuth(act.result.data);
36585                               
36586                             this.dialog.hide();
36587                             
36588                             if (Roo.get('loading-mask')) {
36589                                 Roo.get('loading-mask').show();
36590                             }
36591                             Roo.XComponent.build();
36592                             
36593                              
36594                             
36595                         }
36596                     },
36597                     items : [
36598                         {
36599                             xtype : 'TextField',
36600                             xns : Roo.form,
36601                             fieldLabel: "Email Address",
36602                             name: 'username',
36603                             width:200,
36604                             autoCreate : {tag: "input", type: "text", size: "20"}
36605                         },
36606                         {
36607                             xtype : 'TextField',
36608                             xns : Roo.form,
36609                             fieldLabel: "Password",
36610                             inputType: 'password',
36611                             name: 'password',
36612                             width:200,
36613                             autoCreate : {tag: "input", type: "text", size: "20"},
36614                             listeners : {
36615                                 specialkey : function(e,ev) {
36616                                     if (ev.keyCode == 13) {
36617                                         this.form.dialog.el.mask("Logging in");
36618                                         this.form.doAction('submit', {
36619                                             url: this.form.dialog.url,
36620                                             method: this.form.dialog.method
36621                                         });
36622                                     }
36623                                 }
36624                             }  
36625                         },
36626                         {
36627                             xtype : 'ComboBox',
36628                             xns : Roo.form,
36629                             fieldLabel: "Language",
36630                             name : 'langdisp',
36631                             store: {
36632                                 xtype : 'SimpleStore',
36633                                 fields: ['lang', 'ldisp'],
36634                                 data : [
36635                                     [ 'en', 'English' ],
36636                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
36637                                     [ 'zh_CN', '\u7C21\u4E2D' ]
36638                                 ]
36639                             },
36640                             
36641                             valueField : 'lang',
36642                             hiddenName:  'lang',
36643                             width: 200,
36644                             displayField:'ldisp',
36645                             typeAhead: false,
36646                             editable: false,
36647                             mode: 'local',
36648                             triggerAction: 'all',
36649                             emptyText:'Select a Language...',
36650                             selectOnFocus:true,
36651                             listeners : {
36652                                 select :  function(cb, rec, ix) {
36653                                     this.form.switchLang(rec.data.lang);
36654                                 }
36655                             }
36656                         
36657                         }
36658                     ]
36659                 }
36660                   
36661                 
36662             ]
36663         }
36664     ],
36665     buttons : [
36666         {
36667             xtype : 'Button',
36668             xns : 'Roo',
36669             text : "Forgot Password",
36670             listeners : {
36671                 click : function() {
36672                     //console.log(this);
36673                     var n = this.form.findField('username').getValue();
36674                     if (!n.length) {
36675                         Roo.MessageBox.alert("Error", "Fill in your email address");
36676                         return;
36677                     }
36678                     Roo.Ajax.request({
36679                         url: this.dialog.url,
36680                         params: {
36681                             passwordRequest: n
36682                         },
36683                         method: this.dialog.method,
36684                         success:  function(response, opts)  {  // check successfull...
36685                         
36686                             var res = this.dialog.processResponse(response);
36687                             if (!res.success) { // error!
36688                                Roo.MessageBox.alert("Error" ,
36689                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
36690                                return;
36691                             }
36692                             Roo.MessageBox.alert("Notice" ,
36693                                 "Please check you email for the Password Reset message");
36694                         },
36695                         failure : function() {
36696                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
36697                         }
36698                         
36699                     });
36700                 }
36701             }
36702         },
36703         {
36704             xtype : 'Button',
36705             xns : 'Roo',
36706             text : "Login",
36707             listeners : {
36708                 
36709                 click : function () {
36710                         
36711                     this.dialog.el.mask("Logging in");
36712                     this.form.doAction('submit', {
36713                             url: this.dialog.url,
36714                             method: this.dialog.method
36715                     });
36716                 }
36717             }
36718         }
36719     ]
36720   
36721   
36722 })
36723  
36724
36725
36726