roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75 };
76
77 Roo.dd.DragDrop.prototype = {
78
79     /**
80      * The id of the element associated with this object.  This is what we
81      * refer to as the "linked element" because the size and position of
82      * this element is used to determine when the drag and drop objects have
83      * interacted.
84      * @property id
85      * @type String
86      */
87     id: null,
88
89     /**
90      * Configuration attributes passed into the constructor
91      * @property config
92      * @type object
93      */
94     config: null,
95
96     /**
97      * The id of the element that will be dragged.  By default this is same
98      * as the linked element , but could be changed to another element. Ex:
99      * Roo.dd.DDProxy
100      * @property dragElId
101      * @type String
102      * @private
103      */
104     dragElId: null,
105
106     /**
107      * the id of the element that initiates the drag operation.  By default
108      * this is the linked element, but could be changed to be a child of this
109      * element.  This lets us do things like only starting the drag when the
110      * header element within the linked html element is clicked.
111      * @property handleElId
112      * @type String
113      * @private
114      */
115     handleElId: null,
116
117     /**
118      * An associative array of HTML tags that will be ignored if clicked.
119      * @property invalidHandleTypes
120      * @type {string: string}
121      */
122     invalidHandleTypes: null,
123
124     /**
125      * An associative array of ids for elements that will be ignored if clicked
126      * @property invalidHandleIds
127      * @type {string: string}
128      */
129     invalidHandleIds: null,
130
131     /**
132      * An indexted array of css class names for elements that will be ignored
133      * if clicked.
134      * @property invalidHandleClasses
135      * @type string[]
136      */
137     invalidHandleClasses: null,
138
139     /**
140      * The linked element's absolute X position at the time the drag was
141      * started
142      * @property startPageX
143      * @type int
144      * @private
145      */
146     startPageX: 0,
147
148     /**
149      * The linked element's absolute X position at the time the drag was
150      * started
151      * @property startPageY
152      * @type int
153      * @private
154      */
155     startPageY: 0,
156
157     /**
158      * The group defines a logical collection of DragDrop objects that are
159      * related.  Instances only get events when interacting with other
160      * DragDrop object in the same group.  This lets us define multiple
161      * groups using a single DragDrop subclass if we want.
162      * @property groups
163      * @type {string: string}
164      */
165     groups: null,
166
167     /**
168      * Individual drag/drop instances can be locked.  This will prevent
169      * onmousedown start drag.
170      * @property locked
171      * @type boolean
172      * @private
173      */
174     locked: false,
175
176     /**
177      * Lock this instance
178      * @method lock
179      */
180     lock: function() { this.locked = true; },
181
182     /**
183      * Unlock this instace
184      * @method unlock
185      */
186     unlock: function() { this.locked = false; },
187
188     /**
189      * By default, all insances can be a drop target.  This can be disabled by
190      * setting isTarget to false.
191      * @method isTarget
192      * @type boolean
193      */
194     isTarget: true,
195
196     /**
197      * The padding configured for this drag and drop object for calculating
198      * the drop zone intersection with this object.
199      * @method padding
200      * @type int[]
201      */
202     padding: null,
203
204     /**
205      * Cached reference to the linked element
206      * @property _domRef
207      * @private
208      */
209     _domRef: null,
210
211     /**
212      * Internal typeof flag
213      * @property __ygDragDrop
214      * @private
215      */
216     __ygDragDrop: true,
217
218     /**
219      * Set to true when horizontal contraints are applied
220      * @property constrainX
221      * @type boolean
222      * @private
223      */
224     constrainX: false,
225
226     /**
227      * Set to true when vertical contraints are applied
228      * @property constrainY
229      * @type boolean
230      * @private
231      */
232     constrainY: false,
233
234     /**
235      * The left constraint
236      * @property minX
237      * @type int
238      * @private
239      */
240     minX: 0,
241
242     /**
243      * The right constraint
244      * @property maxX
245      * @type int
246      * @private
247      */
248     maxX: 0,
249
250     /**
251      * The up constraint
252      * @property minY
253      * @type int
254      * @type int
255      * @private
256      */
257     minY: 0,
258
259     /**
260      * The down constraint
261      * @property maxY
262      * @type int
263      * @private
264      */
265     maxY: 0,
266
267     /**
268      * Maintain offsets when we resetconstraints.  Set to true when you want
269      * the position of the element relative to its parent to stay the same
270      * when the page changes
271      *
272      * @property maintainOffset
273      * @type boolean
274      */
275     maintainOffset: false,
276
277     /**
278      * Array of pixel locations the element will snap to if we specified a
279      * horizontal graduation/interval.  This array is generated automatically
280      * when you define a tick interval.
281      * @property xTicks
282      * @type int[]
283      */
284     xTicks: null,
285
286     /**
287      * Array of pixel locations the element will snap to if we specified a
288      * vertical graduation/interval.  This array is generated automatically
289      * when you define a tick interval.
290      * @property yTicks
291      * @type int[]
292      */
293     yTicks: null,
294
295     /**
296      * By default the drag and drop instance will only respond to the primary
297      * button click (left button for a right-handed mouse).  Set to true to
298      * allow drag and drop to start with any mouse click that is propogated
299      * by the browser
300      * @property primaryButtonOnly
301      * @type boolean
302      */
303     primaryButtonOnly: true,
304
305     /**
306      * The availabe property is false until the linked dom element is accessible.
307      * @property available
308      * @type boolean
309      */
310     available: false,
311
312     /**
313      * By default, drags can only be initiated if the mousedown occurs in the
314      * region the linked element is.  This is done in part to work around a
315      * bug in some browsers that mis-report the mousedown if the previous
316      * mouseup happened outside of the window.  This property is set to true
317      * if outer handles are defined.
318      *
319      * @property hasOuterHandles
320      * @type boolean
321      * @default false
322      */
323     hasOuterHandles: false,
324
325     /**
326      * Code that executes immediately before the startDrag event
327      * @method b4StartDrag
328      * @private
329      */
330     b4StartDrag: function(x, y) { },
331
332     /**
333      * Abstract method called after a drag/drop object is clicked
334      * and the drag or mousedown time thresholds have beeen met.
335      * @method startDrag
336      * @param {int} X click location
337      * @param {int} Y click location
338      */
339     startDrag: function(x, y) { /* override this */ },
340
341     /**
342      * Code that executes immediately before the onDrag event
343      * @method b4Drag
344      * @private
345      */
346     b4Drag: function(e) { },
347
348     /**
349      * Abstract method called during the onMouseMove event while dragging an
350      * object.
351      * @method onDrag
352      * @param {Event} e the mousemove event
353      */
354     onDrag: function(e) { /* override this */ },
355
356     /**
357      * Abstract method called when this element fist begins hovering over
358      * another DragDrop obj
359      * @method onDragEnter
360      * @param {Event} e the mousemove event
361      * @param {String|DragDrop[]} id In POINT mode, the element
362      * id this is hovering over.  In INTERSECT mode, an array of one or more
363      * dragdrop items being hovered over.
364      */
365     onDragEnter: function(e, id) { /* override this */ },
366
367     /**
368      * Code that executes immediately before the onDragOver event
369      * @method b4DragOver
370      * @private
371      */
372     b4DragOver: function(e) { },
373
374     /**
375      * Abstract method called when this element is hovering over another
376      * DragDrop obj
377      * @method onDragOver
378      * @param {Event} e the mousemove event
379      * @param {String|DragDrop[]} id In POINT mode, the element
380      * id this is hovering over.  In INTERSECT mode, an array of dd items
381      * being hovered over.
382      */
383     onDragOver: function(e, id) { /* override this */ },
384
385     /**
386      * Code that executes immediately before the onDragOut event
387      * @method b4DragOut
388      * @private
389      */
390     b4DragOut: function(e) { },
391
392     /**
393      * Abstract method called when we are no longer hovering over an element
394      * @method onDragOut
395      * @param {Event} e the mousemove event
396      * @param {String|DragDrop[]} id In POINT mode, the element
397      * id this was hovering over.  In INTERSECT mode, an array of dd items
398      * that the mouse is no longer over.
399      */
400     onDragOut: function(e, id) { /* override this */ },
401
402     /**
403      * Code that executes immediately before the onDragDrop event
404      * @method b4DragDrop
405      * @private
406      */
407     b4DragDrop: function(e) { },
408
409     /**
410      * Abstract method called when this item is dropped on another DragDrop
411      * obj
412      * @method onDragDrop
413      * @param {Event} e the mouseup event
414      * @param {String|DragDrop[]} id In POINT mode, the element
415      * id this was dropped on.  In INTERSECT mode, an array of dd items this
416      * was dropped on.
417      */
418     onDragDrop: function(e, id) { /* override this */ },
419
420     /**
421      * Abstract method called when this item is dropped on an area with no
422      * drop target
423      * @method onInvalidDrop
424      * @param {Event} e the mouseup event
425      */
426     onInvalidDrop: function(e) { /* override this */ },
427
428     /**
429      * Code that executes immediately before the endDrag event
430      * @method b4EndDrag
431      * @private
432      */
433     b4EndDrag: function(e) { },
434
435     /**
436      * Fired when we are done dragging the object
437      * @method endDrag
438      * @param {Event} e the mouseup event
439      */
440     endDrag: function(e) { /* override this */ },
441
442     /**
443      * Code executed immediately before the onMouseDown event
444      * @method b4MouseDown
445      * @param {Event} e the mousedown event
446      * @private
447      */
448     b4MouseDown: function(e) {  },
449
450     /**
451      * Event handler that fires when a drag/drop obj gets a mousedown
452      * @method onMouseDown
453      * @param {Event} e the mousedown event
454      */
455     onMouseDown: function(e) { /* override this */ },
456
457     /**
458      * Event handler that fires when a drag/drop obj gets a mouseup
459      * @method onMouseUp
460      * @param {Event} e the mouseup event
461      */
462     onMouseUp: function(e) { /* override this */ },
463
464     /**
465      * Override the onAvailable method to do what is needed after the initial
466      * position was determined.
467      * @method onAvailable
468      */
469     onAvailable: function () {
470     },
471
472     /*
473      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
474      * @type Object
475      */
476     defaultPadding : {left:0, right:0, top:0, bottom:0},
477
478     /*
479      * Initializes the drag drop object's constraints to restrict movement to a certain element.
480  *
481  * Usage:
482  <pre><code>
483  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
484                 { dragElId: "existingProxyDiv" });
485  dd.startDrag = function(){
486      this.constrainTo("parent-id");
487  };
488  </code></pre>
489  * Or you can initalize it using the {@link Roo.Element} object:
490  <pre><code>
491  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
492      startDrag : function(){
493          this.constrainTo("parent-id");
494      }
495  });
496  </code></pre>
497      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
498      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
499      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
500      * an object containing the sides to pad. For example: {right:10, bottom:10}
501      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
502      */
503     constrainTo : function(constrainTo, pad, inContent){
504         if(typeof pad == "number"){
505             pad = {left: pad, right:pad, top:pad, bottom:pad};
506         }
507         pad = pad || this.defaultPadding;
508         var b = Roo.get(this.getEl()).getBox();
509         var ce = Roo.get(constrainTo);
510         var s = ce.getScroll();
511         var c, cd = ce.dom;
512         if(cd == document.body){
513             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
514         }else{
515             xy = ce.getXY();
516             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
517         }
518
519
520         var topSpace = b.y - c.y;
521         var leftSpace = b.x - c.x;
522
523         this.resetConstraints();
524         this.setXConstraint(leftSpace - (pad.left||0), // left
525                 c.width - leftSpace - b.width - (pad.right||0) //right
526         );
527         this.setYConstraint(topSpace - (pad.top||0), //top
528                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
529         );
530     },
531
532     /**
533      * Returns a reference to the linked element
534      * @method getEl
535      * @return {HTMLElement} the html element
536      */
537     getEl: function() {
538         if (!this._domRef) {
539             this._domRef = Roo.getDom(this.id);
540         }
541
542         return this._domRef;
543     },
544
545     /**
546      * Returns a reference to the actual element to drag.  By default this is
547      * the same as the html element, but it can be assigned to another
548      * element. An example of this can be found in Roo.dd.DDProxy
549      * @method getDragEl
550      * @return {HTMLElement} the html element
551      */
552     getDragEl: function() {
553         return Roo.getDom(this.dragElId);
554     },
555
556     /**
557      * Sets up the DragDrop object.  Must be called in the constructor of any
558      * Roo.dd.DragDrop subclass
559      * @method init
560      * @param id the id of the linked element
561      * @param {String} sGroup the group of related items
562      * @param {object} config configuration attributes
563      */
564     init: function(id, sGroup, config) {
565         this.initTarget(id, sGroup, config);
566         Event.on(this.id, "mousedown", this.handleMouseDown, this);
567         // Event.on(this.id, "selectstart", Event.preventDefault);
568     },
569
570     /**
571      * Initializes Targeting functionality only... the object does not
572      * get a mousedown handler.
573      * @method initTarget
574      * @param id the id of the linked element
575      * @param {String} sGroup the group of related items
576      * @param {object} config configuration attributes
577      */
578     initTarget: function(id, sGroup, config) {
579
580         // configuration attributes
581         this.config = config || {};
582
583         // create a local reference to the drag and drop manager
584         this.DDM = Roo.dd.DDM;
585         // initialize the groups array
586         this.groups = {};
587
588         // assume that we have an element reference instead of an id if the
589         // parameter is not a string
590         if (typeof id !== "string") {
591             id = Roo.id(id);
592         }
593
594         // set the id
595         this.id = id;
596
597         // add to an interaction group
598         this.addToGroup((sGroup) ? sGroup : "default");
599
600         // We don't want to register this as the handle with the manager
601         // so we just set the id rather than calling the setter.
602         this.handleElId = id;
603
604         // the linked element is the element that gets dragged by default
605         this.setDragElId(id);
606
607         // by default, clicked anchors will not start drag operations.
608         this.invalidHandleTypes = { A: "A" };
609         this.invalidHandleIds = {};
610         this.invalidHandleClasses = [];
611
612         this.applyConfig();
613
614         this.handleOnAvailable();
615     },
616
617     /**
618      * Applies the configuration parameters that were passed into the constructor.
619      * This is supposed to happen at each level through the inheritance chain.  So
620      * a DDProxy implentation will execute apply config on DDProxy, DD, and
621      * DragDrop in order to get all of the parameters that are available in
622      * each object.
623      * @method applyConfig
624      */
625     applyConfig: function() {
626
627         // configurable properties:
628         //    padding, isTarget, maintainOffset, primaryButtonOnly
629         this.padding           = this.config.padding || [0, 0, 0, 0];
630         this.isTarget          = (this.config.isTarget !== false);
631         this.maintainOffset    = (this.config.maintainOffset);
632         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
633
634     },
635
636     /**
637      * Executed when the linked element is available
638      * @method handleOnAvailable
639      * @private
640      */
641     handleOnAvailable: function() {
642         this.available = true;
643         this.resetConstraints();
644         this.onAvailable();
645     },
646
647      /**
648      * Configures the padding for the target zone in px.  Effectively expands
649      * (or reduces) the virtual object size for targeting calculations.
650      * Supports css-style shorthand; if only one parameter is passed, all sides
651      * will have that padding, and if only two are passed, the top and bottom
652      * will have the first param, the left and right the second.
653      * @method setPadding
654      * @param {int} iTop    Top pad
655      * @param {int} iRight  Right pad
656      * @param {int} iBot    Bot pad
657      * @param {int} iLeft   Left pad
658      */
659     setPadding: function(iTop, iRight, iBot, iLeft) {
660         // this.padding = [iLeft, iRight, iTop, iBot];
661         if (!iRight && 0 !== iRight) {
662             this.padding = [iTop, iTop, iTop, iTop];
663         } else if (!iBot && 0 !== iBot) {
664             this.padding = [iTop, iRight, iTop, iRight];
665         } else {
666             this.padding = [iTop, iRight, iBot, iLeft];
667         }
668     },
669
670     /**
671      * Stores the initial placement of the linked element.
672      * @method setInitialPosition
673      * @param {int} diffX   the X offset, default 0
674      * @param {int} diffY   the Y offset, default 0
675      */
676     setInitPosition: function(diffX, diffY) {
677         var el = this.getEl();
678
679         if (!this.DDM.verifyEl(el)) {
680             return;
681         }
682
683         var dx = diffX || 0;
684         var dy = diffY || 0;
685
686         var p = Dom.getXY( el );
687
688         this.initPageX = p[0] - dx;
689         this.initPageY = p[1] - dy;
690
691         this.lastPageX = p[0];
692         this.lastPageY = p[1];
693
694
695         this.setStartPosition(p);
696     },
697
698     /**
699      * Sets the start position of the element.  This is set when the obj
700      * is initialized, the reset when a drag is started.
701      * @method setStartPosition
702      * @param pos current position (from previous lookup)
703      * @private
704      */
705     setStartPosition: function(pos) {
706         var p = pos || Dom.getXY( this.getEl() );
707         this.deltaSetXY = null;
708
709         this.startPageX = p[0];
710         this.startPageY = p[1];
711     },
712
713     /**
714      * Add this instance to a group of related drag/drop objects.  All
715      * instances belong to at least one group, and can belong to as many
716      * groups as needed.
717      * @method addToGroup
718      * @param sGroup {string} the name of the group
719      */
720     addToGroup: function(sGroup) {
721         this.groups[sGroup] = true;
722         this.DDM.regDragDrop(this, sGroup);
723     },
724
725     /**
726      * Remove's this instance from the supplied interaction group
727      * @method removeFromGroup
728      * @param {string}  sGroup  The group to drop
729      */
730     removeFromGroup: function(sGroup) {
731         if (this.groups[sGroup]) {
732             delete this.groups[sGroup];
733         }
734
735         this.DDM.removeDDFromGroup(this, sGroup);
736     },
737
738     /**
739      * Allows you to specify that an element other than the linked element
740      * will be moved with the cursor during a drag
741      * @method setDragElId
742      * @param id {string} the id of the element that will be used to initiate the drag
743      */
744     setDragElId: function(id) {
745         this.dragElId = id;
746     },
747
748     /**
749      * Allows you to specify a child of the linked element that should be
750      * used to initiate the drag operation.  An example of this would be if
751      * you have a content div with text and links.  Clicking anywhere in the
752      * content area would normally start the drag operation.  Use this method
753      * to specify that an element inside of the content div is the element
754      * that starts the drag operation.
755      * @method setHandleElId
756      * @param id {string} the id of the element that will be used to
757      * initiate the drag.
758      */
759     setHandleElId: function(id) {
760         if (typeof id !== "string") {
761             id = Roo.id(id);
762         }
763         this.handleElId = id;
764         this.DDM.regHandle(this.id, id);
765     },
766
767     /**
768      * Allows you to set an element outside of the linked element as a drag
769      * handle
770      * @method setOuterHandleElId
771      * @param id the id of the element that will be used to initiate the drag
772      */
773     setOuterHandleElId: function(id) {
774         if (typeof id !== "string") {
775             id = Roo.id(id);
776         }
777         Event.on(id, "mousedown",
778                 this.handleMouseDown, this);
779         this.setHandleElId(id);
780
781         this.hasOuterHandles = true;
782     },
783
784     /**
785      * Remove all drag and drop hooks for this element
786      * @method unreg
787      */
788     unreg: function() {
789         Event.un(this.id, "mousedown",
790                 this.handleMouseDown);
791         this._domRef = null;
792         this.DDM._remove(this);
793     },
794
795     destroy : function(){
796         this.unreg();
797     },
798
799     /**
800      * Returns true if this instance is locked, or the drag drop mgr is locked
801      * (meaning that all drag/drop is disabled on the page.)
802      * @method isLocked
803      * @return {boolean} true if this obj or all drag/drop is locked, else
804      * false
805      */
806     isLocked: function() {
807         return (this.DDM.isLocked() || this.locked);
808     },
809
810     /**
811      * Fired when this object is clicked
812      * @method handleMouseDown
813      * @param {Event} e
814      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
815      * @private
816      */
817     handleMouseDown: function(e, oDD){
818         if (this.primaryButtonOnly && e.button != 0) {
819             return;
820         }
821
822         if (this.isLocked()) {
823             return;
824         }
825
826         this.DDM.refreshCache(this.groups);
827
828         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
829         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
830         } else {
831             if (this.clickValidator(e)) {
832
833                 // set the initial element position
834                 this.setStartPosition();
835
836
837                 this.b4MouseDown(e);
838                 this.onMouseDown(e);
839
840                 this.DDM.handleMouseDown(e, this);
841
842                 this.DDM.stopEvent(e);
843             } else {
844
845
846             }
847         }
848     },
849
850     clickValidator: function(e) {
851         var target = e.getTarget();
852         return ( this.isValidHandleChild(target) &&
853                     (this.id == this.handleElId ||
854                         this.DDM.handleWasClicked(target, this.id)) );
855     },
856
857     /**
858      * Allows you to specify a tag name that should not start a drag operation
859      * when clicked.  This is designed to facilitate embedding links within a
860      * drag handle that do something other than start the drag.
861      * @method addInvalidHandleType
862      * @param {string} tagName the type of element to exclude
863      */
864     addInvalidHandleType: function(tagName) {
865         var type = tagName.toUpperCase();
866         this.invalidHandleTypes[type] = type;
867     },
868
869     /**
870      * Lets you to specify an element id for a child of a drag handle
871      * that should not initiate a drag
872      * @method addInvalidHandleId
873      * @param {string} id the element id of the element you wish to ignore
874      */
875     addInvalidHandleId: function(id) {
876         if (typeof id !== "string") {
877             id = Roo.id(id);
878         }
879         this.invalidHandleIds[id] = id;
880     },
881
882     /**
883      * Lets you specify a css class of elements that will not initiate a drag
884      * @method addInvalidHandleClass
885      * @param {string} cssClass the class of the elements you wish to ignore
886      */
887     addInvalidHandleClass: function(cssClass) {
888         this.invalidHandleClasses.push(cssClass);
889     },
890
891     /**
892      * Unsets an excluded tag name set by addInvalidHandleType
893      * @method removeInvalidHandleType
894      * @param {string} tagName the type of element to unexclude
895      */
896     removeInvalidHandleType: function(tagName) {
897         var type = tagName.toUpperCase();
898         // this.invalidHandleTypes[type] = null;
899         delete this.invalidHandleTypes[type];
900     },
901
902     /**
903      * Unsets an invalid handle id
904      * @method removeInvalidHandleId
905      * @param {string} id the id of the element to re-enable
906      */
907     removeInvalidHandleId: function(id) {
908         if (typeof id !== "string") {
909             id = Roo.id(id);
910         }
911         delete this.invalidHandleIds[id];
912     },
913
914     /**
915      * Unsets an invalid css class
916      * @method removeInvalidHandleClass
917      * @param {string} cssClass the class of the element(s) you wish to
918      * re-enable
919      */
920     removeInvalidHandleClass: function(cssClass) {
921         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
922             if (this.invalidHandleClasses[i] == cssClass) {
923                 delete this.invalidHandleClasses[i];
924             }
925         }
926     },
927
928     /**
929      * Checks the tag exclusion list to see if this click should be ignored
930      * @method isValidHandleChild
931      * @param {HTMLElement} node the HTMLElement to evaluate
932      * @return {boolean} true if this is a valid tag type, false if not
933      */
934     isValidHandleChild: function(node) {
935
936         var valid = true;
937         // var n = (node.nodeName == "#text") ? node.parentNode : node;
938         var nodeName;
939         try {
940             nodeName = node.nodeName.toUpperCase();
941         } catch(e) {
942             nodeName = node.nodeName;
943         }
944         valid = valid && !this.invalidHandleTypes[nodeName];
945         valid = valid && !this.invalidHandleIds[node.id];
946
947         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
948             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
949         }
950
951
952         return valid;
953
954     },
955
956     /**
957      * Create the array of horizontal tick marks if an interval was specified
958      * in setXConstraint().
959      * @method setXTicks
960      * @private
961      */
962     setXTicks: function(iStartX, iTickSize) {
963         this.xTicks = [];
964         this.xTickSize = iTickSize;
965
966         var tickMap = {};
967
968         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
969             if (!tickMap[i]) {
970                 this.xTicks[this.xTicks.length] = i;
971                 tickMap[i] = true;
972             }
973         }
974
975         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
976             if (!tickMap[i]) {
977                 this.xTicks[this.xTicks.length] = i;
978                 tickMap[i] = true;
979             }
980         }
981
982         this.xTicks.sort(this.DDM.numericSort) ;
983     },
984
985     /**
986      * Create the array of vertical tick marks if an interval was specified in
987      * setYConstraint().
988      * @method setYTicks
989      * @private
990      */
991     setYTicks: function(iStartY, iTickSize) {
992         this.yTicks = [];
993         this.yTickSize = iTickSize;
994
995         var tickMap = {};
996
997         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
998             if (!tickMap[i]) {
999                 this.yTicks[this.yTicks.length] = i;
1000                 tickMap[i] = true;
1001             }
1002         }
1003
1004         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1005             if (!tickMap[i]) {
1006                 this.yTicks[this.yTicks.length] = i;
1007                 tickMap[i] = true;
1008             }
1009         }
1010
1011         this.yTicks.sort(this.DDM.numericSort) ;
1012     },
1013
1014     /**
1015      * By default, the element can be dragged any place on the screen.  Use
1016      * this method to limit the horizontal travel of the element.  Pass in
1017      * 0,0 for the parameters if you want to lock the drag to the y axis.
1018      * @method setXConstraint
1019      * @param {int} iLeft the number of pixels the element can move to the left
1020      * @param {int} iRight the number of pixels the element can move to the
1021      * right
1022      * @param {int} iTickSize optional parameter for specifying that the
1023      * element
1024      * should move iTickSize pixels at a time.
1025      */
1026     setXConstraint: function(iLeft, iRight, iTickSize) {
1027         this.leftConstraint = iLeft;
1028         this.rightConstraint = iRight;
1029
1030         this.minX = this.initPageX - iLeft;
1031         this.maxX = this.initPageX + iRight;
1032         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1033
1034         this.constrainX = true;
1035     },
1036
1037     /**
1038      * Clears any constraints applied to this instance.  Also clears ticks
1039      * since they can't exist independent of a constraint at this time.
1040      * @method clearConstraints
1041      */
1042     clearConstraints: function() {
1043         this.constrainX = false;
1044         this.constrainY = false;
1045         this.clearTicks();
1046     },
1047
1048     /**
1049      * Clears any tick interval defined for this instance
1050      * @method clearTicks
1051      */
1052     clearTicks: function() {
1053         this.xTicks = null;
1054         this.yTicks = null;
1055         this.xTickSize = 0;
1056         this.yTickSize = 0;
1057     },
1058
1059     /**
1060      * By default, the element can be dragged any place on the screen.  Set
1061      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1062      * parameters if you want to lock the drag to the x axis.
1063      * @method setYConstraint
1064      * @param {int} iUp the number of pixels the element can move up
1065      * @param {int} iDown the number of pixels the element can move down
1066      * @param {int} iTickSize optional parameter for specifying that the
1067      * element should move iTickSize pixels at a time.
1068      */
1069     setYConstraint: function(iUp, iDown, iTickSize) {
1070         this.topConstraint = iUp;
1071         this.bottomConstraint = iDown;
1072
1073         this.minY = this.initPageY - iUp;
1074         this.maxY = this.initPageY + iDown;
1075         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1076
1077         this.constrainY = true;
1078
1079     },
1080
1081     /**
1082      * resetConstraints must be called if you manually reposition a dd element.
1083      * @method resetConstraints
1084      * @param {boolean} maintainOffset
1085      */
1086     resetConstraints: function() {
1087
1088
1089         // Maintain offsets if necessary
1090         if (this.initPageX || this.initPageX === 0) {
1091             // figure out how much this thing has moved
1092             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1093             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1094
1095             this.setInitPosition(dx, dy);
1096
1097         // This is the first time we have detected the element's position
1098         } else {
1099             this.setInitPosition();
1100         }
1101
1102         if (this.constrainX) {
1103             this.setXConstraint( this.leftConstraint,
1104                                  this.rightConstraint,
1105                                  this.xTickSize        );
1106         }
1107
1108         if (this.constrainY) {
1109             this.setYConstraint( this.topConstraint,
1110                                  this.bottomConstraint,
1111                                  this.yTickSize         );
1112         }
1113     },
1114
1115     /**
1116      * Normally the drag element is moved pixel by pixel, but we can specify
1117      * that it move a number of pixels at a time.  This method resolves the
1118      * location when we have it set up like this.
1119      * @method getTick
1120      * @param {int} val where we want to place the object
1121      * @param {int[]} tickArray sorted array of valid points
1122      * @return {int} the closest tick
1123      * @private
1124      */
1125     getTick: function(val, tickArray) {
1126
1127         if (!tickArray) {
1128             // If tick interval is not defined, it is effectively 1 pixel,
1129             // so we return the value passed to us.
1130             return val;
1131         } else if (tickArray[0] >= val) {
1132             // The value is lower than the first tick, so we return the first
1133             // tick.
1134             return tickArray[0];
1135         } else {
1136             for (var i=0, len=tickArray.length; i<len; ++i) {
1137                 var next = i + 1;
1138                 if (tickArray[next] && tickArray[next] >= val) {
1139                     var diff1 = val - tickArray[i];
1140                     var diff2 = tickArray[next] - val;
1141                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1142                 }
1143             }
1144
1145             // The value is larger than the last tick, so we return the last
1146             // tick.
1147             return tickArray[tickArray.length - 1];
1148         }
1149     },
1150
1151     /**
1152      * toString method
1153      * @method toString
1154      * @return {string} string representation of the dd obj
1155      */
1156     toString: function() {
1157         return ("DragDrop " + this.id);
1158     }
1159
1160 };
1161
1162 })();
1163 /*
1164  * Based on:
1165  * Ext JS Library 1.1.1
1166  * Copyright(c) 2006-2007, Ext JS, LLC.
1167  *
1168  * Originally Released Under LGPL - original licence link has changed is not relivant.
1169  *
1170  * Fork - LGPL
1171  * <script type="text/javascript">
1172  */
1173
1174
1175 /**
1176  * The drag and drop utility provides a framework for building drag and drop
1177  * applications.  In addition to enabling drag and drop for specific elements,
1178  * the drag and drop elements are tracked by the manager class, and the
1179  * interactions between the various elements are tracked during the drag and
1180  * the implementing code is notified about these important moments.
1181  */
1182
1183 // Only load the library once.  Rewriting the manager class would orphan
1184 // existing drag and drop instances.
1185 if (!Roo.dd.DragDropMgr) {
1186
1187 /**
1188  * @class Roo.dd.DragDropMgr
1189  * DragDropMgr is a singleton that tracks the element interaction for
1190  * all DragDrop items in the window.  Generally, you will not call
1191  * this class directly, but it does have helper methods that could
1192  * be useful in your DragDrop implementations.
1193  * @singleton
1194  */
1195 Roo.dd.DragDropMgr = function() {
1196
1197     var Event = Roo.EventManager;
1198
1199     return {
1200
1201         /**
1202          * Two dimensional Array of registered DragDrop objects.  The first
1203          * dimension is the DragDrop item group, the second the DragDrop
1204          * object.
1205          * @property ids
1206          * @type {string: string}
1207          * @private
1208          * @static
1209          */
1210         ids: {},
1211
1212         /**
1213          * Array of element ids defined as drag handles.  Used to determine
1214          * if the element that generated the mousedown event is actually the
1215          * handle and not the html element itself.
1216          * @property handleIds
1217          * @type {string: string}
1218          * @private
1219          * @static
1220          */
1221         handleIds: {},
1222
1223         /**
1224          * the DragDrop object that is currently being dragged
1225          * @property dragCurrent
1226          * @type DragDrop
1227          * @private
1228          * @static
1229          **/
1230         dragCurrent: null,
1231
1232         /**
1233          * the DragDrop object(s) that are being hovered over
1234          * @property dragOvers
1235          * @type Array
1236          * @private
1237          * @static
1238          */
1239         dragOvers: {},
1240
1241         /**
1242          * the X distance between the cursor and the object being dragged
1243          * @property deltaX
1244          * @type int
1245          * @private
1246          * @static
1247          */
1248         deltaX: 0,
1249
1250         /**
1251          * the Y distance between the cursor and the object being dragged
1252          * @property deltaY
1253          * @type int
1254          * @private
1255          * @static
1256          */
1257         deltaY: 0,
1258
1259         /**
1260          * Flag to determine if we should prevent the default behavior of the
1261          * events we define. By default this is true, but this can be set to
1262          * false if you need the default behavior (not recommended)
1263          * @property preventDefault
1264          * @type boolean
1265          * @static
1266          */
1267         preventDefault: true,
1268
1269         /**
1270          * Flag to determine if we should stop the propagation of the events
1271          * we generate. This is true by default but you may want to set it to
1272          * false if the html element contains other features that require the
1273          * mouse click.
1274          * @property stopPropagation
1275          * @type boolean
1276          * @static
1277          */
1278         stopPropagation: true,
1279
1280         /**
1281          * Internal flag that is set to true when drag and drop has been
1282          * intialized
1283          * @property initialized
1284          * @private
1285          * @static
1286          */
1287         initalized: false,
1288
1289         /**
1290          * All drag and drop can be disabled.
1291          * @property locked
1292          * @private
1293          * @static
1294          */
1295         locked: false,
1296
1297         /**
1298          * Called the first time an element is registered.
1299          * @method init
1300          * @private
1301          * @static
1302          */
1303         init: function() {
1304             this.initialized = true;
1305         },
1306
1307         /**
1308          * In point mode, drag and drop interaction is defined by the
1309          * location of the cursor during the drag/drop
1310          * @property POINT
1311          * @type int
1312          * @static
1313          */
1314         POINT: 0,
1315
1316         /**
1317          * In intersect mode, drag and drop interactio nis defined by the
1318          * overlap of two or more drag and drop objects.
1319          * @property INTERSECT
1320          * @type int
1321          * @static
1322          */
1323         INTERSECT: 1,
1324
1325         /**
1326          * The current drag and drop mode.  Default: POINT
1327          * @property mode
1328          * @type int
1329          * @static
1330          */
1331         mode: 0,
1332
1333         /**
1334          * Runs method on all drag and drop objects
1335          * @method _execOnAll
1336          * @private
1337          * @static
1338          */
1339         _execOnAll: function(sMethod, args) {
1340             for (var i in this.ids) {
1341                 for (var j in this.ids[i]) {
1342                     var oDD = this.ids[i][j];
1343                     if (! this.isTypeOfDD(oDD)) {
1344                         continue;
1345                     }
1346                     oDD[sMethod].apply(oDD, args);
1347                 }
1348             }
1349         },
1350
1351         /**
1352          * Drag and drop initialization.  Sets up the global event handlers
1353          * @method _onLoad
1354          * @private
1355          * @static
1356          */
1357         _onLoad: function() {
1358
1359             this.init();
1360
1361
1362             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1363             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1364             Event.on(window,   "unload",    this._onUnload, this, true);
1365             Event.on(window,   "resize",    this._onResize, this, true);
1366             // Event.on(window,   "mouseout",    this._test);
1367
1368         },
1369
1370         /**
1371          * Reset constraints on all drag and drop objs
1372          * @method _onResize
1373          * @private
1374          * @static
1375          */
1376         _onResize: function(e) {
1377             this._execOnAll("resetConstraints", []);
1378         },
1379
1380         /**
1381          * Lock all drag and drop functionality
1382          * @method lock
1383          * @static
1384          */
1385         lock: function() { this.locked = true; },
1386
1387         /**
1388          * Unlock all drag and drop functionality
1389          * @method unlock
1390          * @static
1391          */
1392         unlock: function() { this.locked = false; },
1393
1394         /**
1395          * Is drag and drop locked?
1396          * @method isLocked
1397          * @return {boolean} True if drag and drop is locked, false otherwise.
1398          * @static
1399          */
1400         isLocked: function() { return this.locked; },
1401
1402         /**
1403          * Location cache that is set for all drag drop objects when a drag is
1404          * initiated, cleared when the drag is finished.
1405          * @property locationCache
1406          * @private
1407          * @static
1408          */
1409         locationCache: {},
1410
1411         /**
1412          * Set useCache to false if you want to force object the lookup of each
1413          * drag and drop linked element constantly during a drag.
1414          * @property useCache
1415          * @type boolean
1416          * @static
1417          */
1418         useCache: true,
1419
1420         /**
1421          * The number of pixels that the mouse needs to move after the
1422          * mousedown before the drag is initiated.  Default=3;
1423          * @property clickPixelThresh
1424          * @type int
1425          * @static
1426          */
1427         clickPixelThresh: 3,
1428
1429         /**
1430          * The number of milliseconds after the mousedown event to initiate the
1431          * drag if we don't get a mouseup event. Default=1000
1432          * @property clickTimeThresh
1433          * @type int
1434          * @static
1435          */
1436         clickTimeThresh: 350,
1437
1438         /**
1439          * Flag that indicates that either the drag pixel threshold or the
1440          * mousdown time threshold has been met
1441          * @property dragThreshMet
1442          * @type boolean
1443          * @private
1444          * @static
1445          */
1446         dragThreshMet: false,
1447
1448         /**
1449          * Timeout used for the click time threshold
1450          * @property clickTimeout
1451          * @type Object
1452          * @private
1453          * @static
1454          */
1455         clickTimeout: null,
1456
1457         /**
1458          * The X position of the mousedown event stored for later use when a
1459          * drag threshold is met.
1460          * @property startX
1461          * @type int
1462          * @private
1463          * @static
1464          */
1465         startX: 0,
1466
1467         /**
1468          * The Y position of the mousedown event stored for later use when a
1469          * drag threshold is met.
1470          * @property startY
1471          * @type int
1472          * @private
1473          * @static
1474          */
1475         startY: 0,
1476
1477         /**
1478          * Each DragDrop instance must be registered with the DragDropMgr.
1479          * This is executed in DragDrop.init()
1480          * @method regDragDrop
1481          * @param {DragDrop} oDD the DragDrop object to register
1482          * @param {String} sGroup the name of the group this element belongs to
1483          * @static
1484          */
1485         regDragDrop: function(oDD, sGroup) {
1486             if (!this.initialized) { this.init(); }
1487
1488             if (!this.ids[sGroup]) {
1489                 this.ids[sGroup] = {};
1490             }
1491             this.ids[sGroup][oDD.id] = oDD;
1492         },
1493
1494         /**
1495          * Removes the supplied dd instance from the supplied group. Executed
1496          * by DragDrop.removeFromGroup, so don't call this function directly.
1497          * @method removeDDFromGroup
1498          * @private
1499          * @static
1500          */
1501         removeDDFromGroup: function(oDD, sGroup) {
1502             if (!this.ids[sGroup]) {
1503                 this.ids[sGroup] = {};
1504             }
1505
1506             var obj = this.ids[sGroup];
1507             if (obj && obj[oDD.id]) {
1508                 delete obj[oDD.id];
1509             }
1510         },
1511
1512         /**
1513          * Unregisters a drag and drop item.  This is executed in
1514          * DragDrop.unreg, use that method instead of calling this directly.
1515          * @method _remove
1516          * @private
1517          * @static
1518          */
1519         _remove: function(oDD) {
1520             for (var g in oDD.groups) {
1521                 if (g && this.ids[g][oDD.id]) {
1522                     delete this.ids[g][oDD.id];
1523                 }
1524             }
1525             delete this.handleIds[oDD.id];
1526         },
1527
1528         /**
1529          * Each DragDrop handle element must be registered.  This is done
1530          * automatically when executing DragDrop.setHandleElId()
1531          * @method regHandle
1532          * @param {String} sDDId the DragDrop id this element is a handle for
1533          * @param {String} sHandleId the id of the element that is the drag
1534          * handle
1535          * @static
1536          */
1537         regHandle: function(sDDId, sHandleId) {
1538             if (!this.handleIds[sDDId]) {
1539                 this.handleIds[sDDId] = {};
1540             }
1541             this.handleIds[sDDId][sHandleId] = sHandleId;
1542         },
1543
1544         /**
1545          * Utility function to determine if a given element has been
1546          * registered as a drag drop item.
1547          * @method isDragDrop
1548          * @param {String} id the element id to check
1549          * @return {boolean} true if this element is a DragDrop item,
1550          * false otherwise
1551          * @static
1552          */
1553         isDragDrop: function(id) {
1554             return ( this.getDDById(id) ) ? true : false;
1555         },
1556
1557         /**
1558          * Returns the drag and drop instances that are in all groups the
1559          * passed in instance belongs to.
1560          * @method getRelated
1561          * @param {DragDrop} p_oDD the obj to get related data for
1562          * @param {boolean} bTargetsOnly if true, only return targetable objs
1563          * @return {DragDrop[]} the related instances
1564          * @static
1565          */
1566         getRelated: function(p_oDD, bTargetsOnly) {
1567             var oDDs = [];
1568             for (var i in p_oDD.groups) {
1569                 for (j in this.ids[i]) {
1570                     var dd = this.ids[i][j];
1571                     if (! this.isTypeOfDD(dd)) {
1572                         continue;
1573                     }
1574                     if (!bTargetsOnly || dd.isTarget) {
1575                         oDDs[oDDs.length] = dd;
1576                     }
1577                 }
1578             }
1579
1580             return oDDs;
1581         },
1582
1583         /**
1584          * Returns true if the specified dd target is a legal target for
1585          * the specifice drag obj
1586          * @method isLegalTarget
1587          * @param {DragDrop} the drag obj
1588          * @param {DragDrop} the target
1589          * @return {boolean} true if the target is a legal target for the
1590          * dd obj
1591          * @static
1592          */
1593         isLegalTarget: function (oDD, oTargetDD) {
1594             var targets = this.getRelated(oDD, true);
1595             for (var i=0, len=targets.length;i<len;++i) {
1596                 if (targets[i].id == oTargetDD.id) {
1597                     return true;
1598                 }
1599             }
1600
1601             return false;
1602         },
1603
1604         /**
1605          * My goal is to be able to transparently determine if an object is
1606          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1607          * returns "object", oDD.constructor.toString() always returns
1608          * "DragDrop" and not the name of the subclass.  So for now it just
1609          * evaluates a well-known variable in DragDrop.
1610          * @method isTypeOfDD
1611          * @param {Object} the object to evaluate
1612          * @return {boolean} true if typeof oDD = DragDrop
1613          * @static
1614          */
1615         isTypeOfDD: function (oDD) {
1616             return (oDD && oDD.__ygDragDrop);
1617         },
1618
1619         /**
1620          * Utility function to determine if a given element has been
1621          * registered as a drag drop handle for the given Drag Drop object.
1622          * @method isHandle
1623          * @param {String} id the element id to check
1624          * @return {boolean} true if this element is a DragDrop handle, false
1625          * otherwise
1626          * @static
1627          */
1628         isHandle: function(sDDId, sHandleId) {
1629             return ( this.handleIds[sDDId] &&
1630                             this.handleIds[sDDId][sHandleId] );
1631         },
1632
1633         /**
1634          * Returns the DragDrop instance for a given id
1635          * @method getDDById
1636          * @param {String} id the id of the DragDrop object
1637          * @return {DragDrop} the drag drop object, null if it is not found
1638          * @static
1639          */
1640         getDDById: function(id) {
1641             for (var i in this.ids) {
1642                 if (this.ids[i][id]) {
1643                     return this.ids[i][id];
1644                 }
1645             }
1646             return null;
1647         },
1648
1649         /**
1650          * Fired after a registered DragDrop object gets the mousedown event.
1651          * Sets up the events required to track the object being dragged
1652          * @method handleMouseDown
1653          * @param {Event} e the event
1654          * @param oDD the DragDrop object being dragged
1655          * @private
1656          * @static
1657          */
1658         handleMouseDown: function(e, oDD) {
1659             if(Roo.QuickTips){
1660                 Roo.QuickTips.disable();
1661             }
1662             this.currentTarget = e.getTarget();
1663
1664             this.dragCurrent = oDD;
1665
1666             var el = oDD.getEl();
1667
1668             // track start position
1669             this.startX = e.getPageX();
1670             this.startY = e.getPageY();
1671
1672             this.deltaX = this.startX - el.offsetLeft;
1673             this.deltaY = this.startY - el.offsetTop;
1674
1675             this.dragThreshMet = false;
1676
1677             this.clickTimeout = setTimeout(
1678                     function() {
1679                         var DDM = Roo.dd.DDM;
1680                         DDM.startDrag(DDM.startX, DDM.startY);
1681                     },
1682                     this.clickTimeThresh );
1683         },
1684
1685         /**
1686          * Fired when either the drag pixel threshol or the mousedown hold
1687          * time threshold has been met.
1688          * @method startDrag
1689          * @param x {int} the X position of the original mousedown
1690          * @param y {int} the Y position of the original mousedown
1691          * @static
1692          */
1693         startDrag: function(x, y) {
1694             clearTimeout(this.clickTimeout);
1695             if (this.dragCurrent) {
1696                 this.dragCurrent.b4StartDrag(x, y);
1697                 this.dragCurrent.startDrag(x, y);
1698             }
1699             this.dragThreshMet = true;
1700         },
1701
1702         /**
1703          * Internal function to handle the mouseup event.  Will be invoked
1704          * from the context of the document.
1705          * @method handleMouseUp
1706          * @param {Event} e the event
1707          * @private
1708          * @static
1709          */
1710         handleMouseUp: function(e) {
1711
1712             if(Roo.QuickTips){
1713                 Roo.QuickTips.enable();
1714             }
1715             if (! this.dragCurrent) {
1716                 return;
1717             }
1718
1719             clearTimeout(this.clickTimeout);
1720
1721             if (this.dragThreshMet) {
1722                 this.fireEvents(e, true);
1723             } else {
1724             }
1725
1726             this.stopDrag(e);
1727
1728             this.stopEvent(e);
1729         },
1730
1731         /**
1732          * Utility to stop event propagation and event default, if these
1733          * features are turned on.
1734          * @method stopEvent
1735          * @param {Event} e the event as returned by this.getEvent()
1736          * @static
1737          */
1738         stopEvent: function(e){
1739             if(this.stopPropagation) {
1740                 e.stopPropagation();
1741             }
1742
1743             if (this.preventDefault) {
1744                 e.preventDefault();
1745             }
1746         },
1747
1748         /**
1749          * Internal function to clean up event handlers after the drag
1750          * operation is complete
1751          * @method stopDrag
1752          * @param {Event} e the event
1753          * @private
1754          * @static
1755          */
1756         stopDrag: function(e) {
1757             // Fire the drag end event for the item that was dragged
1758             if (this.dragCurrent) {
1759                 if (this.dragThreshMet) {
1760                     this.dragCurrent.b4EndDrag(e);
1761                     this.dragCurrent.endDrag(e);
1762                 }
1763
1764                 this.dragCurrent.onMouseUp(e);
1765             }
1766
1767             this.dragCurrent = null;
1768             this.dragOvers = {};
1769         },
1770
1771         /**
1772          * Internal function to handle the mousemove event.  Will be invoked
1773          * from the context of the html element.
1774          *
1775          * @TODO figure out what we can do about mouse events lost when the
1776          * user drags objects beyond the window boundary.  Currently we can
1777          * detect this in internet explorer by verifying that the mouse is
1778          * down during the mousemove event.  Firefox doesn't give us the
1779          * button state on the mousemove event.
1780          * @method handleMouseMove
1781          * @param {Event} e the event
1782          * @private
1783          * @static
1784          */
1785         handleMouseMove: function(e) {
1786             if (! this.dragCurrent) {
1787                 return true;
1788             }
1789
1790             // var button = e.which || e.button;
1791
1792             // check for IE mouseup outside of page boundary
1793             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1794                 this.stopEvent(e);
1795                 return this.handleMouseUp(e);
1796             }
1797
1798             if (!this.dragThreshMet) {
1799                 var diffX = Math.abs(this.startX - e.getPageX());
1800                 var diffY = Math.abs(this.startY - e.getPageY());
1801                 if (diffX > this.clickPixelThresh ||
1802                             diffY > this.clickPixelThresh) {
1803                     this.startDrag(this.startX, this.startY);
1804                 }
1805             }
1806
1807             if (this.dragThreshMet) {
1808                 this.dragCurrent.b4Drag(e);
1809                 this.dragCurrent.onDrag(e);
1810                 if(!this.dragCurrent.moveOnly){
1811                     this.fireEvents(e, false);
1812                 }
1813             }
1814
1815             this.stopEvent(e);
1816
1817             return true;
1818         },
1819
1820         /**
1821          * Iterates over all of the DragDrop elements to find ones we are
1822          * hovering over or dropping on
1823          * @method fireEvents
1824          * @param {Event} e the event
1825          * @param {boolean} isDrop is this a drop op or a mouseover op?
1826          * @private
1827          * @static
1828          */
1829         fireEvents: function(e, isDrop) {
1830             var dc = this.dragCurrent;
1831
1832             // If the user did the mouse up outside of the window, we could
1833             // get here even though we have ended the drag.
1834             if (!dc || dc.isLocked()) {
1835                 return;
1836             }
1837
1838             var pt = e.getPoint();
1839
1840             // cache the previous dragOver array
1841             var oldOvers = [];
1842
1843             var outEvts   = [];
1844             var overEvts  = [];
1845             var dropEvts  = [];
1846             var enterEvts = [];
1847
1848             // Check to see if the object(s) we were hovering over is no longer
1849             // being hovered over so we can fire the onDragOut event
1850             for (var i in this.dragOvers) {
1851
1852                 var ddo = this.dragOvers[i];
1853
1854                 if (! this.isTypeOfDD(ddo)) {
1855                     continue;
1856                 }
1857
1858                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1859                     outEvts.push( ddo );
1860                 }
1861
1862                 oldOvers[i] = true;
1863                 delete this.dragOvers[i];
1864             }
1865
1866             for (var sGroup in dc.groups) {
1867
1868                 if ("string" != typeof sGroup) {
1869                     continue;
1870                 }
1871
1872                 for (i in this.ids[sGroup]) {
1873                     var oDD = this.ids[sGroup][i];
1874                     if (! this.isTypeOfDD(oDD)) {
1875                         continue;
1876                     }
1877
1878                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1879                         if (this.isOverTarget(pt, oDD, this.mode)) {
1880                             // look for drop interactions
1881                             if (isDrop) {
1882                                 dropEvts.push( oDD );
1883                             // look for drag enter and drag over interactions
1884                             } else {
1885
1886                                 // initial drag over: dragEnter fires
1887                                 if (!oldOvers[oDD.id]) {
1888                                     enterEvts.push( oDD );
1889                                 // subsequent drag overs: dragOver fires
1890                                 } else {
1891                                     overEvts.push( oDD );
1892                                 }
1893
1894                                 this.dragOvers[oDD.id] = oDD;
1895                             }
1896                         }
1897                     }
1898                 }
1899             }
1900
1901             if (this.mode) {
1902                 if (outEvts.length) {
1903                     dc.b4DragOut(e, outEvts);
1904                     dc.onDragOut(e, outEvts);
1905                 }
1906
1907                 if (enterEvts.length) {
1908                     dc.onDragEnter(e, enterEvts);
1909                 }
1910
1911                 if (overEvts.length) {
1912                     dc.b4DragOver(e, overEvts);
1913                     dc.onDragOver(e, overEvts);
1914                 }
1915
1916                 if (dropEvts.length) {
1917                     dc.b4DragDrop(e, dropEvts);
1918                     dc.onDragDrop(e, dropEvts);
1919                 }
1920
1921             } else {
1922                 // fire dragout events
1923                 var len = 0;
1924                 for (i=0, len=outEvts.length; i<len; ++i) {
1925                     dc.b4DragOut(e, outEvts[i].id);
1926                     dc.onDragOut(e, outEvts[i].id);
1927                 }
1928
1929                 // fire enter events
1930                 for (i=0,len=enterEvts.length; i<len; ++i) {
1931                     // dc.b4DragEnter(e, oDD.id);
1932                     dc.onDragEnter(e, enterEvts[i].id);
1933                 }
1934
1935                 // fire over events
1936                 for (i=0,len=overEvts.length; i<len; ++i) {
1937                     dc.b4DragOver(e, overEvts[i].id);
1938                     dc.onDragOver(e, overEvts[i].id);
1939                 }
1940
1941                 // fire drop events
1942                 for (i=0, len=dropEvts.length; i<len; ++i) {
1943                     dc.b4DragDrop(e, dropEvts[i].id);
1944                     dc.onDragDrop(e, dropEvts[i].id);
1945                 }
1946
1947             }
1948
1949             // notify about a drop that did not find a target
1950             if (isDrop && !dropEvts.length) {
1951                 dc.onInvalidDrop(e);
1952             }
1953
1954         },
1955
1956         /**
1957          * Helper function for getting the best match from the list of drag
1958          * and drop objects returned by the drag and drop events when we are
1959          * in INTERSECT mode.  It returns either the first object that the
1960          * cursor is over, or the object that has the greatest overlap with
1961          * the dragged element.
1962          * @method getBestMatch
1963          * @param  {DragDrop[]} dds The array of drag and drop objects
1964          * targeted
1965          * @return {DragDrop}       The best single match
1966          * @static
1967          */
1968         getBestMatch: function(dds) {
1969             var winner = null;
1970             // Return null if the input is not what we expect
1971             //if (!dds || !dds.length || dds.length == 0) {
1972                // winner = null;
1973             // If there is only one item, it wins
1974             //} else if (dds.length == 1) {
1975
1976             var len = dds.length;
1977
1978             if (len == 1) {
1979                 winner = dds[0];
1980             } else {
1981                 // Loop through the targeted items
1982                 for (var i=0; i<len; ++i) {
1983                     var dd = dds[i];
1984                     // If the cursor is over the object, it wins.  If the
1985                     // cursor is over multiple matches, the first one we come
1986                     // to wins.
1987                     if (dd.cursorIsOver) {
1988                         winner = dd;
1989                         break;
1990                     // Otherwise the object with the most overlap wins
1991                     } else {
1992                         if (!winner ||
1993                             winner.overlap.getArea() < dd.overlap.getArea()) {
1994                             winner = dd;
1995                         }
1996                     }
1997                 }
1998             }
1999
2000             return winner;
2001         },
2002
2003         /**
2004          * Refreshes the cache of the top-left and bottom-right points of the
2005          * drag and drop objects in the specified group(s).  This is in the
2006          * format that is stored in the drag and drop instance, so typical
2007          * usage is:
2008          * <code>
2009          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2010          * </code>
2011          * Alternatively:
2012          * <code>
2013          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2014          * </code>
2015          * @TODO this really should be an indexed array.  Alternatively this
2016          * method could accept both.
2017          * @method refreshCache
2018          * @param {Object} groups an associative array of groups to refresh
2019          * @static
2020          */
2021         refreshCache: function(groups) {
2022             for (var sGroup in groups) {
2023                 if ("string" != typeof sGroup) {
2024                     continue;
2025                 }
2026                 for (var i in this.ids[sGroup]) {
2027                     var oDD = this.ids[sGroup][i];
2028
2029                     if (this.isTypeOfDD(oDD)) {
2030                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2031                         var loc = this.getLocation(oDD);
2032                         if (loc) {
2033                             this.locationCache[oDD.id] = loc;
2034                         } else {
2035                             delete this.locationCache[oDD.id];
2036                             // this will unregister the drag and drop object if
2037                             // the element is not in a usable state
2038                             // oDD.unreg();
2039                         }
2040                     }
2041                 }
2042             }
2043         },
2044
2045         /**
2046          * This checks to make sure an element exists and is in the DOM.  The
2047          * main purpose is to handle cases where innerHTML is used to remove
2048          * drag and drop objects from the DOM.  IE provides an 'unspecified
2049          * error' when trying to access the offsetParent of such an element
2050          * @method verifyEl
2051          * @param {HTMLElement} el the element to check
2052          * @return {boolean} true if the element looks usable
2053          * @static
2054          */
2055         verifyEl: function(el) {
2056             if (el) {
2057                 var parent;
2058                 if(Roo.isIE){
2059                     try{
2060                         parent = el.offsetParent;
2061                     }catch(e){}
2062                 }else{
2063                     parent = el.offsetParent;
2064                 }
2065                 if (parent) {
2066                     return true;
2067                 }
2068             }
2069
2070             return false;
2071         },
2072
2073         /**
2074          * Returns a Region object containing the drag and drop element's position
2075          * and size, including the padding configured for it
2076          * @method getLocation
2077          * @param {DragDrop} oDD the drag and drop object to get the
2078          *                       location for
2079          * @return {Roo.lib.Region} a Region object representing the total area
2080          *                             the element occupies, including any padding
2081          *                             the instance is configured for.
2082          * @static
2083          */
2084         getLocation: function(oDD) {
2085             if (! this.isTypeOfDD(oDD)) {
2086                 return null;
2087             }
2088
2089             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2090
2091             try {
2092                 pos= Roo.lib.Dom.getXY(el);
2093             } catch (e) { }
2094
2095             if (!pos) {
2096                 return null;
2097             }
2098
2099             x1 = pos[0];
2100             x2 = x1 + el.offsetWidth;
2101             y1 = pos[1];
2102             y2 = y1 + el.offsetHeight;
2103
2104             t = y1 - oDD.padding[0];
2105             r = x2 + oDD.padding[1];
2106             b = y2 + oDD.padding[2];
2107             l = x1 - oDD.padding[3];
2108
2109             return new Roo.lib.Region( t, r, b, l );
2110         },
2111
2112         /**
2113          * Checks the cursor location to see if it over the target
2114          * @method isOverTarget
2115          * @param {Roo.lib.Point} pt The point to evaluate
2116          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2117          * @return {boolean} true if the mouse is over the target
2118          * @private
2119          * @static
2120          */
2121         isOverTarget: function(pt, oTarget, intersect) {
2122             // use cache if available
2123             var loc = this.locationCache[oTarget.id];
2124             if (!loc || !this.useCache) {
2125                 loc = this.getLocation(oTarget);
2126                 this.locationCache[oTarget.id] = loc;
2127
2128             }
2129
2130             if (!loc) {
2131                 return false;
2132             }
2133
2134             oTarget.cursorIsOver = loc.contains( pt );
2135
2136             // DragDrop is using this as a sanity check for the initial mousedown
2137             // in this case we are done.  In POINT mode, if the drag obj has no
2138             // contraints, we are also done. Otherwise we need to evaluate the
2139             // location of the target as related to the actual location of the
2140             // dragged element.
2141             var dc = this.dragCurrent;
2142             if (!dc || !dc.getTargetCoord ||
2143                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2144                 return oTarget.cursorIsOver;
2145             }
2146
2147             oTarget.overlap = null;
2148
2149             // Get the current location of the drag element, this is the
2150             // location of the mouse event less the delta that represents
2151             // where the original mousedown happened on the element.  We
2152             // need to consider constraints and ticks as well.
2153             var pos = dc.getTargetCoord(pt.x, pt.y);
2154
2155             var el = dc.getDragEl();
2156             var curRegion = new Roo.lib.Region( pos.y,
2157                                                    pos.x + el.offsetWidth,
2158                                                    pos.y + el.offsetHeight,
2159                                                    pos.x );
2160
2161             var overlap = curRegion.intersect(loc);
2162
2163             if (overlap) {
2164                 oTarget.overlap = overlap;
2165                 return (intersect) ? true : oTarget.cursorIsOver;
2166             } else {
2167                 return false;
2168             }
2169         },
2170
2171         /**
2172          * unload event handler
2173          * @method _onUnload
2174          * @private
2175          * @static
2176          */
2177         _onUnload: function(e, me) {
2178             Roo.dd.DragDropMgr.unregAll();
2179         },
2180
2181         /**
2182          * Cleans up the drag and drop events and objects.
2183          * @method unregAll
2184          * @private
2185          * @static
2186          */
2187         unregAll: function() {
2188
2189             if (this.dragCurrent) {
2190                 this.stopDrag();
2191                 this.dragCurrent = null;
2192             }
2193
2194             this._execOnAll("unreg", []);
2195
2196             for (i in this.elementCache) {
2197                 delete this.elementCache[i];
2198             }
2199
2200             this.elementCache = {};
2201             this.ids = {};
2202         },
2203
2204         /**
2205          * A cache of DOM elements
2206          * @property elementCache
2207          * @private
2208          * @static
2209          */
2210         elementCache: {},
2211
2212         /**
2213          * Get the wrapper for the DOM element specified
2214          * @method getElWrapper
2215          * @param {String} id the id of the element to get
2216          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2217          * @private
2218          * @deprecated This wrapper isn't that useful
2219          * @static
2220          */
2221         getElWrapper: function(id) {
2222             var oWrapper = this.elementCache[id];
2223             if (!oWrapper || !oWrapper.el) {
2224                 oWrapper = this.elementCache[id] =
2225                     new this.ElementWrapper(Roo.getDom(id));
2226             }
2227             return oWrapper;
2228         },
2229
2230         /**
2231          * Returns the actual DOM element
2232          * @method getElement
2233          * @param {String} id the id of the elment to get
2234          * @return {Object} The element
2235          * @deprecated use Roo.getDom instead
2236          * @static
2237          */
2238         getElement: function(id) {
2239             return Roo.getDom(id);
2240         },
2241
2242         /**
2243          * Returns the style property for the DOM element (i.e.,
2244          * document.getElById(id).style)
2245          * @method getCss
2246          * @param {String} id the id of the elment to get
2247          * @return {Object} The style property of the element
2248          * @deprecated use Roo.getDom instead
2249          * @static
2250          */
2251         getCss: function(id) {
2252             var el = Roo.getDom(id);
2253             return (el) ? el.style : null;
2254         },
2255
2256         /**
2257          * Inner class for cached elements
2258          * @class DragDropMgr.ElementWrapper
2259          * @for DragDropMgr
2260          * @private
2261          * @deprecated
2262          */
2263         ElementWrapper: function(el) {
2264                 /**
2265                  * The element
2266                  * @property el
2267                  */
2268                 this.el = el || null;
2269                 /**
2270                  * The element id
2271                  * @property id
2272                  */
2273                 this.id = this.el && el.id;
2274                 /**
2275                  * A reference to the style property
2276                  * @property css
2277                  */
2278                 this.css = this.el && el.style;
2279             },
2280
2281         /**
2282          * Returns the X position of an html element
2283          * @method getPosX
2284          * @param el the element for which to get the position
2285          * @return {int} the X coordinate
2286          * @for DragDropMgr
2287          * @deprecated use Roo.lib.Dom.getX instead
2288          * @static
2289          */
2290         getPosX: function(el) {
2291             return Roo.lib.Dom.getX(el);
2292         },
2293
2294         /**
2295          * Returns the Y position of an html element
2296          * @method getPosY
2297          * @param el the element for which to get the position
2298          * @return {int} the Y coordinate
2299          * @deprecated use Roo.lib.Dom.getY instead
2300          * @static
2301          */
2302         getPosY: function(el) {
2303             return Roo.lib.Dom.getY(el);
2304         },
2305
2306         /**
2307          * Swap two nodes.  In IE, we use the native method, for others we
2308          * emulate the IE behavior
2309          * @method swapNode
2310          * @param n1 the first node to swap
2311          * @param n2 the other node to swap
2312          * @static
2313          */
2314         swapNode: function(n1, n2) {
2315             if (n1.swapNode) {
2316                 n1.swapNode(n2);
2317             } else {
2318                 var p = n2.parentNode;
2319                 var s = n2.nextSibling;
2320
2321                 if (s == n1) {
2322                     p.insertBefore(n1, n2);
2323                 } else if (n2 == n1.nextSibling) {
2324                     p.insertBefore(n2, n1);
2325                 } else {
2326                     n1.parentNode.replaceChild(n2, n1);
2327                     p.insertBefore(n1, s);
2328                 }
2329             }
2330         },
2331
2332         /**
2333          * Returns the current scroll position
2334          * @method getScroll
2335          * @private
2336          * @static
2337          */
2338         getScroll: function () {
2339             var t, l, dde=document.documentElement, db=document.body;
2340             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2341                 t = dde.scrollTop;
2342                 l = dde.scrollLeft;
2343             } else if (db) {
2344                 t = db.scrollTop;
2345                 l = db.scrollLeft;
2346             } else {
2347
2348             }
2349             return { top: t, left: l };
2350         },
2351
2352         /**
2353          * Returns the specified element style property
2354          * @method getStyle
2355          * @param {HTMLElement} el          the element
2356          * @param {string}      styleProp   the style property
2357          * @return {string} The value of the style property
2358          * @deprecated use Roo.lib.Dom.getStyle
2359          * @static
2360          */
2361         getStyle: function(el, styleProp) {
2362             return Roo.fly(el).getStyle(styleProp);
2363         },
2364
2365         /**
2366          * Gets the scrollTop
2367          * @method getScrollTop
2368          * @return {int} the document's scrollTop
2369          * @static
2370          */
2371         getScrollTop: function () { return this.getScroll().top; },
2372
2373         /**
2374          * Gets the scrollLeft
2375          * @method getScrollLeft
2376          * @return {int} the document's scrollTop
2377          * @static
2378          */
2379         getScrollLeft: function () { return this.getScroll().left; },
2380
2381         /**
2382          * Sets the x/y position of an element to the location of the
2383          * target element.
2384          * @method moveToEl
2385          * @param {HTMLElement} moveEl      The element to move
2386          * @param {HTMLElement} targetEl    The position reference element
2387          * @static
2388          */
2389         moveToEl: function (moveEl, targetEl) {
2390             var aCoord = Roo.lib.Dom.getXY(targetEl);
2391             Roo.lib.Dom.setXY(moveEl, aCoord);
2392         },
2393
2394         /**
2395          * Numeric array sort function
2396          * @method numericSort
2397          * @static
2398          */
2399         numericSort: function(a, b) { return (a - b); },
2400
2401         /**
2402          * Internal counter
2403          * @property _timeoutCount
2404          * @private
2405          * @static
2406          */
2407         _timeoutCount: 0,
2408
2409         /**
2410          * Trying to make the load order less important.  Without this we get
2411          * an error if this file is loaded before the Event Utility.
2412          * @method _addListeners
2413          * @private
2414          * @static
2415          */
2416         _addListeners: function() {
2417             var DDM = Roo.dd.DDM;
2418             if ( Roo.lib.Event && document ) {
2419                 DDM._onLoad();
2420             } else {
2421                 if (DDM._timeoutCount > 2000) {
2422                 } else {
2423                     setTimeout(DDM._addListeners, 10);
2424                     if (document && document.body) {
2425                         DDM._timeoutCount += 1;
2426                     }
2427                 }
2428             }
2429         },
2430
2431         /**
2432          * Recursively searches the immediate parent and all child nodes for
2433          * the handle element in order to determine wheter or not it was
2434          * clicked.
2435          * @method handleWasClicked
2436          * @param node the html element to inspect
2437          * @static
2438          */
2439         handleWasClicked: function(node, id) {
2440             if (this.isHandle(id, node.id)) {
2441                 return true;
2442             } else {
2443                 // check to see if this is a text node child of the one we want
2444                 var p = node.parentNode;
2445
2446                 while (p) {
2447                     if (this.isHandle(id, p.id)) {
2448                         return true;
2449                     } else {
2450                         p = p.parentNode;
2451                     }
2452                 }
2453             }
2454
2455             return false;
2456         }
2457
2458     };
2459
2460 }();
2461
2462 // shorter alias, save a few bytes
2463 Roo.dd.DDM = Roo.dd.DragDropMgr;
2464 Roo.dd.DDM._addListeners();
2465
2466 }/*
2467  * Based on:
2468  * Ext JS Library 1.1.1
2469  * Copyright(c) 2006-2007, Ext JS, LLC.
2470  *
2471  * Originally Released Under LGPL - original licence link has changed is not relivant.
2472  *
2473  * Fork - LGPL
2474  * <script type="text/javascript">
2475  */
2476
2477 /**
2478  * @class Roo.dd.DD
2479  * A DragDrop implementation where the linked element follows the
2480  * mouse cursor during a drag.
2481  * @extends Roo.dd.DragDrop
2482  * @constructor
2483  * @param {String} id the id of the linked element
2484  * @param {String} sGroup the group of related DragDrop items
2485  * @param {object} config an object containing configurable attributes
2486  *                Valid properties for DD:
2487  *                    scroll
2488  */
2489 Roo.dd.DD = function(id, sGroup, config) {
2490     if (id) {
2491         this.init(id, sGroup, config);
2492     }
2493 };
2494
2495 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2496
2497     /**
2498      * When set to true, the utility automatically tries to scroll the browser
2499      * window wehn a drag and drop element is dragged near the viewport boundary.
2500      * Defaults to true.
2501      * @property scroll
2502      * @type boolean
2503      */
2504     scroll: true,
2505
2506     /**
2507      * Sets the pointer offset to the distance between the linked element's top
2508      * left corner and the location the element was clicked
2509      * @method autoOffset
2510      * @param {int} iPageX the X coordinate of the click
2511      * @param {int} iPageY the Y coordinate of the click
2512      */
2513     autoOffset: function(iPageX, iPageY) {
2514         var x = iPageX - this.startPageX;
2515         var y = iPageY - this.startPageY;
2516         this.setDelta(x, y);
2517     },
2518
2519     /**
2520      * Sets the pointer offset.  You can call this directly to force the
2521      * offset to be in a particular location (e.g., pass in 0,0 to set it
2522      * to the center of the object)
2523      * @method setDelta
2524      * @param {int} iDeltaX the distance from the left
2525      * @param {int} iDeltaY the distance from the top
2526      */
2527     setDelta: function(iDeltaX, iDeltaY) {
2528         this.deltaX = iDeltaX;
2529         this.deltaY = iDeltaY;
2530     },
2531
2532     /**
2533      * Sets the drag element to the location of the mousedown or click event,
2534      * maintaining the cursor location relative to the location on the element
2535      * that was clicked.  Override this if you want to place the element in a
2536      * location other than where the cursor is.
2537      * @method setDragElPos
2538      * @param {int} iPageX the X coordinate of the mousedown or drag event
2539      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2540      */
2541     setDragElPos: function(iPageX, iPageY) {
2542         // the first time we do this, we are going to check to make sure
2543         // the element has css positioning
2544
2545         var el = this.getDragEl();
2546         this.alignElWithMouse(el, iPageX, iPageY);
2547     },
2548
2549     /**
2550      * Sets the element to the location of the mousedown or click event,
2551      * maintaining the cursor location relative to the location on the element
2552      * that was clicked.  Override this if you want to place the element in a
2553      * location other than where the cursor is.
2554      * @method alignElWithMouse
2555      * @param {HTMLElement} el the element to move
2556      * @param {int} iPageX the X coordinate of the mousedown or drag event
2557      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2558      */
2559     alignElWithMouse: function(el, iPageX, iPageY) {
2560         var oCoord = this.getTargetCoord(iPageX, iPageY);
2561         var fly = el.dom ? el : Roo.fly(el);
2562         if (!this.deltaSetXY) {
2563             var aCoord = [oCoord.x, oCoord.y];
2564             fly.setXY(aCoord);
2565             var newLeft = fly.getLeft(true);
2566             var newTop  = fly.getTop(true);
2567             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2568         } else {
2569             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2570         }
2571
2572         this.cachePosition(oCoord.x, oCoord.y);
2573         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2574         return oCoord;
2575     },
2576
2577     /**
2578      * Saves the most recent position so that we can reset the constraints and
2579      * tick marks on-demand.  We need to know this so that we can calculate the
2580      * number of pixels the element is offset from its original position.
2581      * @method cachePosition
2582      * @param iPageX the current x position (optional, this just makes it so we
2583      * don't have to look it up again)
2584      * @param iPageY the current y position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      */
2587     cachePosition: function(iPageX, iPageY) {
2588         if (iPageX) {
2589             this.lastPageX = iPageX;
2590             this.lastPageY = iPageY;
2591         } else {
2592             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2593             this.lastPageX = aCoord[0];
2594             this.lastPageY = aCoord[1];
2595         }
2596     },
2597
2598     /**
2599      * Auto-scroll the window if the dragged object has been moved beyond the
2600      * visible window boundary.
2601      * @method autoScroll
2602      * @param {int} x the drag element's x position
2603      * @param {int} y the drag element's y position
2604      * @param {int} h the height of the drag element
2605      * @param {int} w the width of the drag element
2606      * @private
2607      */
2608     autoScroll: function(x, y, h, w) {
2609
2610         if (this.scroll) {
2611             // The client height
2612             var clientH = Roo.lib.Dom.getViewWidth();
2613
2614             // The client width
2615             var clientW = Roo.lib.Dom.getViewHeight();
2616
2617             // The amt scrolled down
2618             var st = this.DDM.getScrollTop();
2619
2620             // The amt scrolled right
2621             var sl = this.DDM.getScrollLeft();
2622
2623             // Location of the bottom of the element
2624             var bot = h + y;
2625
2626             // Location of the right of the element
2627             var right = w + x;
2628
2629             // The distance from the cursor to the bottom of the visible area,
2630             // adjusted so that we don't scroll if the cursor is beyond the
2631             // element drag constraints
2632             var toBot = (clientH + st - y - this.deltaY);
2633
2634             // The distance from the cursor to the right of the visible area
2635             var toRight = (clientW + sl - x - this.deltaX);
2636
2637
2638             // How close to the edge the cursor must be before we scroll
2639             // var thresh = (document.all) ? 100 : 40;
2640             var thresh = 40;
2641
2642             // How many pixels to scroll per autoscroll op.  This helps to reduce
2643             // clunky scrolling. IE is more sensitive about this ... it needs this
2644             // value to be higher.
2645             var scrAmt = (document.all) ? 80 : 30;
2646
2647             // Scroll down if we are near the bottom of the visible page and the
2648             // obj extends below the crease
2649             if ( bot > clientH && toBot < thresh ) {
2650                 window.scrollTo(sl, st + scrAmt);
2651             }
2652
2653             // Scroll up if the window is scrolled down and the top of the object
2654             // goes above the top border
2655             if ( y < st && st > 0 && y - st < thresh ) {
2656                 window.scrollTo(sl, st - scrAmt);
2657             }
2658
2659             // Scroll right if the obj is beyond the right border and the cursor is
2660             // near the border.
2661             if ( right > clientW && toRight < thresh ) {
2662                 window.scrollTo(sl + scrAmt, st);
2663             }
2664
2665             // Scroll left if the window has been scrolled to the right and the obj
2666             // extends past the left border
2667             if ( x < sl && sl > 0 && x - sl < thresh ) {
2668                 window.scrollTo(sl - scrAmt, st);
2669             }
2670         }
2671     },
2672
2673     /**
2674      * Finds the location the element should be placed if we want to move
2675      * it to where the mouse location less the click offset would place us.
2676      * @method getTargetCoord
2677      * @param {int} iPageX the X coordinate of the click
2678      * @param {int} iPageY the Y coordinate of the click
2679      * @return an object that contains the coordinates (Object.x and Object.y)
2680      * @private
2681      */
2682     getTargetCoord: function(iPageX, iPageY) {
2683
2684
2685         var x = iPageX - this.deltaX;
2686         var y = iPageY - this.deltaY;
2687
2688         if (this.constrainX) {
2689             if (x < this.minX) { x = this.minX; }
2690             if (x > this.maxX) { x = this.maxX; }
2691         }
2692
2693         if (this.constrainY) {
2694             if (y < this.minY) { y = this.minY; }
2695             if (y > this.maxY) { y = this.maxY; }
2696         }
2697
2698         x = this.getTick(x, this.xTicks);
2699         y = this.getTick(y, this.yTicks);
2700
2701
2702         return {x:x, y:y};
2703     },
2704
2705     /*
2706      * Sets up config options specific to this class. Overrides
2707      * Roo.dd.DragDrop, but all versions of this method through the
2708      * inheritance chain are called
2709      */
2710     applyConfig: function() {
2711         Roo.dd.DD.superclass.applyConfig.call(this);
2712         this.scroll = (this.config.scroll !== false);
2713     },
2714
2715     /*
2716      * Event that fires prior to the onMouseDown event.  Overrides
2717      * Roo.dd.DragDrop.
2718      */
2719     b4MouseDown: function(e) {
2720         // this.resetConstraints();
2721         this.autoOffset(e.getPageX(),
2722                             e.getPageY());
2723     },
2724
2725     /*
2726      * Event that fires prior to the onDrag event.  Overrides
2727      * Roo.dd.DragDrop.
2728      */
2729     b4Drag: function(e) {
2730         this.setDragElPos(e.getPageX(),
2731                             e.getPageY());
2732     },
2733
2734     toString: function() {
2735         return ("DD " + this.id);
2736     }
2737
2738     //////////////////////////////////////////////////////////////////////////
2739     // Debugging ygDragDrop events that can be overridden
2740     //////////////////////////////////////////////////////////////////////////
2741     /*
2742     startDrag: function(x, y) {
2743     },
2744
2745     onDrag: function(e) {
2746     },
2747
2748     onDragEnter: function(e, id) {
2749     },
2750
2751     onDragOver: function(e, id) {
2752     },
2753
2754     onDragOut: function(e, id) {
2755     },
2756
2757     onDragDrop: function(e, id) {
2758     },
2759
2760     endDrag: function(e) {
2761     }
2762
2763     */
2764
2765 });/*
2766  * Based on:
2767  * Ext JS Library 1.1.1
2768  * Copyright(c) 2006-2007, Ext JS, LLC.
2769  *
2770  * Originally Released Under LGPL - original licence link has changed is not relivant.
2771  *
2772  * Fork - LGPL
2773  * <script type="text/javascript">
2774  */
2775
2776 /**
2777  * @class Roo.dd.DDProxy
2778  * A DragDrop implementation that inserts an empty, bordered div into
2779  * the document that follows the cursor during drag operations.  At the time of
2780  * the click, the frame div is resized to the dimensions of the linked html
2781  * element, and moved to the exact location of the linked element.
2782  *
2783  * References to the "frame" element refer to the single proxy element that
2784  * was created to be dragged in place of all DDProxy elements on the
2785  * page.
2786  *
2787  * @extends Roo.dd.DD
2788  * @constructor
2789  * @param {String} id the id of the linked html element
2790  * @param {String} sGroup the group of related DragDrop objects
2791  * @param {object} config an object containing configurable attributes
2792  *                Valid properties for DDProxy in addition to those in DragDrop:
2793  *                   resizeFrame, centerFrame, dragElId
2794  */
2795 Roo.dd.DDProxy = function(id, sGroup, config) {
2796     if (id) {
2797         this.init(id, sGroup, config);
2798         this.initFrame();
2799     }
2800 };
2801
2802 /**
2803  * The default drag frame div id
2804  * @property Roo.dd.DDProxy.dragElId
2805  * @type String
2806  * @static
2807  */
2808 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2809
2810 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2811
2812     /**
2813      * By default we resize the drag frame to be the same size as the element
2814      * we want to drag (this is to get the frame effect).  We can turn it off
2815      * if we want a different behavior.
2816      * @property resizeFrame
2817      * @type boolean
2818      */
2819     resizeFrame: true,
2820
2821     /**
2822      * By default the frame is positioned exactly where the drag element is, so
2823      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2824      * you do not have constraints on the obj is to have the drag frame centered
2825      * around the cursor.  Set centerFrame to true for this effect.
2826      * @property centerFrame
2827      * @type boolean
2828      */
2829     centerFrame: false,
2830
2831     /**
2832      * Creates the proxy element if it does not yet exist
2833      * @method createFrame
2834      */
2835     createFrame: function() {
2836         var self = this;
2837         var body = document.body;
2838
2839         if (!body || !body.firstChild) {
2840             setTimeout( function() { self.createFrame(); }, 50 );
2841             return;
2842         }
2843
2844         var div = this.getDragEl();
2845
2846         if (!div) {
2847             div    = document.createElement("div");
2848             div.id = this.dragElId;
2849             var s  = div.style;
2850
2851             s.position   = "absolute";
2852             s.visibility = "hidden";
2853             s.cursor     = "move";
2854             s.border     = "2px solid #aaa";
2855             s.zIndex     = 999;
2856
2857             // appendChild can blow up IE if invoked prior to the window load event
2858             // while rendering a table.  It is possible there are other scenarios
2859             // that would cause this to happen as well.
2860             body.insertBefore(div, body.firstChild);
2861         }
2862     },
2863
2864     /**
2865      * Initialization for the drag frame element.  Must be called in the
2866      * constructor of all subclasses
2867      * @method initFrame
2868      */
2869     initFrame: function() {
2870         this.createFrame();
2871     },
2872
2873     applyConfig: function() {
2874         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2875
2876         this.resizeFrame = (this.config.resizeFrame !== false);
2877         this.centerFrame = (this.config.centerFrame);
2878         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2879     },
2880
2881     /**
2882      * Resizes the drag frame to the dimensions of the clicked object, positions
2883      * it over the object, and finally displays it
2884      * @method showFrame
2885      * @param {int} iPageX X click position
2886      * @param {int} iPageY Y click position
2887      * @private
2888      */
2889     showFrame: function(iPageX, iPageY) {
2890         var el = this.getEl();
2891         var dragEl = this.getDragEl();
2892         var s = dragEl.style;
2893
2894         this._resizeProxy();
2895
2896         if (this.centerFrame) {
2897             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2898                            Math.round(parseInt(s.height, 10)/2) );
2899         }
2900
2901         this.setDragElPos(iPageX, iPageY);
2902
2903         Roo.fly(dragEl).show();
2904     },
2905
2906     /**
2907      * The proxy is automatically resized to the dimensions of the linked
2908      * element when a drag is initiated, unless resizeFrame is set to false
2909      * @method _resizeProxy
2910      * @private
2911      */
2912     _resizeProxy: function() {
2913         if (this.resizeFrame) {
2914             var el = this.getEl();
2915             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2916         }
2917     },
2918
2919     // overrides Roo.dd.DragDrop
2920     b4MouseDown: function(e) {
2921         var x = e.getPageX();
2922         var y = e.getPageY();
2923         this.autoOffset(x, y);
2924         this.setDragElPos(x, y);
2925     },
2926
2927     // overrides Roo.dd.DragDrop
2928     b4StartDrag: function(x, y) {
2929         // show the drag frame
2930         this.showFrame(x, y);
2931     },
2932
2933     // overrides Roo.dd.DragDrop
2934     b4EndDrag: function(e) {
2935         Roo.fly(this.getDragEl()).hide();
2936     },
2937
2938     // overrides Roo.dd.DragDrop
2939     // By default we try to move the element to the last location of the frame.
2940     // This is so that the default behavior mirrors that of Roo.dd.DD.
2941     endDrag: function(e) {
2942
2943         var lel = this.getEl();
2944         var del = this.getDragEl();
2945
2946         // Show the drag frame briefly so we can get its position
2947         del.style.visibility = "";
2948
2949         this.beforeMove();
2950         // Hide the linked element before the move to get around a Safari
2951         // rendering bug.
2952         lel.style.visibility = "hidden";
2953         Roo.dd.DDM.moveToEl(lel, del);
2954         del.style.visibility = "hidden";
2955         lel.style.visibility = "";
2956
2957         this.afterDrag();
2958     },
2959
2960     beforeMove : function(){
2961
2962     },
2963
2964     afterDrag : function(){
2965
2966     },
2967
2968     toString: function() {
2969         return ("DDProxy " + this.id);
2970     }
2971
2972 });
2973 /*
2974  * Based on:
2975  * Ext JS Library 1.1.1
2976  * Copyright(c) 2006-2007, Ext JS, LLC.
2977  *
2978  * Originally Released Under LGPL - original licence link has changed is not relivant.
2979  *
2980  * Fork - LGPL
2981  * <script type="text/javascript">
2982  */
2983
2984  /**
2985  * @class Roo.dd.DDTarget
2986  * A DragDrop implementation that does not move, but can be a drop
2987  * target.  You would get the same result by simply omitting implementation
2988  * for the event callbacks, but this way we reduce the processing cost of the
2989  * event listener and the callbacks.
2990  * @extends Roo.dd.DragDrop
2991  * @constructor
2992  * @param {String} id the id of the element that is a drop target
2993  * @param {String} sGroup the group of related DragDrop objects
2994  * @param {object} config an object containing configurable attributes
2995  *                 Valid properties for DDTarget in addition to those in
2996  *                 DragDrop:
2997  *                    none
2998  */
2999 Roo.dd.DDTarget = function(id, sGroup, config) {
3000     if (id) {
3001         this.initTarget(id, sGroup, config);
3002     }
3003 };
3004
3005 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3006 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3007     toString: function() {
3008         return ("DDTarget " + this.id);
3009     }
3010 });
3011 /*
3012  * Based on:
3013  * Ext JS Library 1.1.1
3014  * Copyright(c) 2006-2007, Ext JS, LLC.
3015  *
3016  * Originally Released Under LGPL - original licence link has changed is not relivant.
3017  *
3018  * Fork - LGPL
3019  * <script type="text/javascript">
3020  */
3021  
3022
3023 /**
3024  * @class Roo.dd.ScrollManager
3025  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3026  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3027  * @singleton
3028  */
3029 Roo.dd.ScrollManager = function(){
3030     var ddm = Roo.dd.DragDropMgr;
3031     var els = {};
3032     var dragEl = null;
3033     var proc = {};
3034     
3035     var onStop = function(e){
3036         dragEl = null;
3037         clearProc();
3038     };
3039     
3040     var triggerRefresh = function(){
3041         if(ddm.dragCurrent){
3042              ddm.refreshCache(ddm.dragCurrent.groups);
3043         }
3044     };
3045     
3046     var doScroll = function(){
3047         if(ddm.dragCurrent){
3048             var dds = Roo.dd.ScrollManager;
3049             if(!dds.animate){
3050                 if(proc.el.scroll(proc.dir, dds.increment)){
3051                     triggerRefresh();
3052                 }
3053             }else{
3054                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3055             }
3056         }
3057     };
3058     
3059     var clearProc = function(){
3060         if(proc.id){
3061             clearInterval(proc.id);
3062         }
3063         proc.id = 0;
3064         proc.el = null;
3065         proc.dir = "";
3066     };
3067     
3068     var startProc = function(el, dir){
3069         clearProc();
3070         proc.el = el;
3071         proc.dir = dir;
3072         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3073     };
3074     
3075     var onFire = function(e, isDrop){
3076         if(isDrop || !ddm.dragCurrent){ return; }
3077         var dds = Roo.dd.ScrollManager;
3078         if(!dragEl || dragEl != ddm.dragCurrent){
3079             dragEl = ddm.dragCurrent;
3080             // refresh regions on drag start
3081             dds.refreshCache();
3082         }
3083         
3084         var xy = Roo.lib.Event.getXY(e);
3085         var pt = new Roo.lib.Point(xy[0], xy[1]);
3086         for(var id in els){
3087             var el = els[id], r = el._region;
3088             if(r && r.contains(pt) && el.isScrollable()){
3089                 if(r.bottom - pt.y <= dds.thresh){
3090                     if(proc.el != el){
3091                         startProc(el, "down");
3092                     }
3093                     return;
3094                 }else if(r.right - pt.x <= dds.thresh){
3095                     if(proc.el != el){
3096                         startProc(el, "left");
3097                     }
3098                     return;
3099                 }else if(pt.y - r.top <= dds.thresh){
3100                     if(proc.el != el){
3101                         startProc(el, "up");
3102                     }
3103                     return;
3104                 }else if(pt.x - r.left <= dds.thresh){
3105                     if(proc.el != el){
3106                         startProc(el, "right");
3107                     }
3108                     return;
3109                 }
3110             }
3111         }
3112         clearProc();
3113     };
3114     
3115     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3116     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3117     
3118     return {
3119         /**
3120          * Registers new overflow element(s) to auto scroll
3121          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3122          */
3123         register : function(el){
3124             if(el instanceof Array){
3125                 for(var i = 0, len = el.length; i < len; i++) {
3126                         this.register(el[i]);
3127                 }
3128             }else{
3129                 el = Roo.get(el);
3130                 els[el.id] = el;
3131             }
3132         },
3133         
3134         /**
3135          * Unregisters overflow element(s) so they are no longer scrolled
3136          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3137          */
3138         unregister : function(el){
3139             if(el instanceof Array){
3140                 for(var i = 0, len = el.length; i < len; i++) {
3141                         this.unregister(el[i]);
3142                 }
3143             }else{
3144                 el = Roo.get(el);
3145                 delete els[el.id];
3146             }
3147         },
3148         
3149         /**
3150          * The number of pixels from the edge of a container the pointer needs to be to 
3151          * trigger scrolling (defaults to 25)
3152          * @type Number
3153          */
3154         thresh : 25,
3155         
3156         /**
3157          * The number of pixels to scroll in each scroll increment (defaults to 50)
3158          * @type Number
3159          */
3160         increment : 100,
3161         
3162         /**
3163          * The frequency of scrolls in milliseconds (defaults to 500)
3164          * @type Number
3165          */
3166         frequency : 500,
3167         
3168         /**
3169          * True to animate the scroll (defaults to true)
3170          * @type Boolean
3171          */
3172         animate: true,
3173         
3174         /**
3175          * The animation duration in seconds - 
3176          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3177          * @type Number
3178          */
3179         animDuration: .4,
3180         
3181         /**
3182          * Manually trigger a cache refresh.
3183          */
3184         refreshCache : function(){
3185             for(var id in els){
3186                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3187                     els[id]._region = els[id].getRegion();
3188                 }
3189             }
3190         }
3191     };
3192 }();/*
3193  * Based on:
3194  * Ext JS Library 1.1.1
3195  * Copyright(c) 2006-2007, Ext JS, LLC.
3196  *
3197  * Originally Released Under LGPL - original licence link has changed is not relivant.
3198  *
3199  * Fork - LGPL
3200  * <script type="text/javascript">
3201  */
3202  
3203
3204 /**
3205  * @class Roo.dd.Registry
3206  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3207  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3208  * @singleton
3209  */
3210 Roo.dd.Registry = function(){
3211     var elements = {}; 
3212     var handles = {}; 
3213     var autoIdSeed = 0;
3214
3215     var getId = function(el, autogen){
3216         if(typeof el == "string"){
3217             return el;
3218         }
3219         var id = el.id;
3220         if(!id && autogen !== false){
3221             id = "roodd-" + (++autoIdSeed);
3222             el.id = id;
3223         }
3224         return id;
3225     };
3226     
3227     return {
3228     /**
3229      * Register a drag drop element
3230      * @param {String|HTMLElement} element The id or DOM node to register
3231      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3232      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3233      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3234      * populated in the data object (if applicable):
3235      * <pre>
3236 Value      Description<br />
3237 ---------  ------------------------------------------<br />
3238 handles    Array of DOM nodes that trigger dragging<br />
3239            for the element being registered<br />
3240 isHandle   True if the element passed in triggers<br />
3241            dragging itself, else false
3242 </pre>
3243      */
3244         register : function(el, data){
3245             data = data || {};
3246             if(typeof el == "string"){
3247                 el = document.getElementById(el);
3248             }
3249             data.ddel = el;
3250             elements[getId(el)] = data;
3251             if(data.isHandle !== false){
3252                 handles[data.ddel.id] = data;
3253             }
3254             if(data.handles){
3255                 var hs = data.handles;
3256                 for(var i = 0, len = hs.length; i < len; i++){
3257                         handles[getId(hs[i])] = data;
3258                 }
3259             }
3260         },
3261
3262     /**
3263      * Unregister a drag drop element
3264      * @param {String|HTMLElement}  element The id or DOM node to unregister
3265      */
3266         unregister : function(el){
3267             var id = getId(el, false);
3268             var data = elements[id];
3269             if(data){
3270                 delete elements[id];
3271                 if(data.handles){
3272                     var hs = data.handles;
3273                     for(var i = 0, len = hs.length; i < len; i++){
3274                         delete handles[getId(hs[i], false)];
3275                     }
3276                 }
3277             }
3278         },
3279
3280     /**
3281      * Returns the handle registered for a DOM Node by id
3282      * @param {String|HTMLElement} id The DOM node or id to look up
3283      * @return {Object} handle The custom handle data
3284      */
3285         getHandle : function(id){
3286             if(typeof id != "string"){ // must be element?
3287                 id = id.id;
3288             }
3289             return handles[id];
3290         },
3291
3292     /**
3293      * Returns the handle that is registered for the DOM node that is the target of the event
3294      * @param {Event} e The event
3295      * @return {Object} handle The custom handle data
3296      */
3297         getHandleFromEvent : function(e){
3298             var t = Roo.lib.Event.getTarget(e);
3299             return t ? handles[t.id] : null;
3300         },
3301
3302     /**
3303      * Returns a custom data object that is registered for a DOM node by id
3304      * @param {String|HTMLElement} id The DOM node or id to look up
3305      * @return {Object} data The custom data
3306      */
3307         getTarget : function(id){
3308             if(typeof id != "string"){ // must be element?
3309                 id = id.id;
3310             }
3311             return elements[id];
3312         },
3313
3314     /**
3315      * Returns a custom data object that is registered for the DOM node that is the target of the event
3316      * @param {Event} e The event
3317      * @return {Object} data The custom data
3318      */
3319         getTargetFromEvent : function(e){
3320             var t = Roo.lib.Event.getTarget(e);
3321             return t ? elements[t.id] || handles[t.id] : null;
3322         }
3323     };
3324 }();/*
3325  * Based on:
3326  * Ext JS Library 1.1.1
3327  * Copyright(c) 2006-2007, Ext JS, LLC.
3328  *
3329  * Originally Released Under LGPL - original licence link has changed is not relivant.
3330  *
3331  * Fork - LGPL
3332  * <script type="text/javascript">
3333  */
3334  
3335
3336 /**
3337  * @class Roo.dd.StatusProxy
3338  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3339  * default drag proxy used by all Roo.dd components.
3340  * @constructor
3341  * @param {Object} config
3342  */
3343 Roo.dd.StatusProxy = function(config){
3344     Roo.apply(this, config);
3345     this.id = this.id || Roo.id();
3346     this.el = new Roo.Layer({
3347         dh: {
3348             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3349                 {tag: "div", cls: "x-dd-drop-icon"},
3350                 {tag: "div", cls: "x-dd-drag-ghost"}
3351             ]
3352         }, 
3353         shadow: !config || config.shadow !== false
3354     });
3355     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3356     this.dropStatus = this.dropNotAllowed;
3357 };
3358
3359 Roo.dd.StatusProxy.prototype = {
3360     /**
3361      * @cfg {String} dropAllowed
3362      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3363      */
3364     dropAllowed : "x-dd-drop-ok",
3365     /**
3366      * @cfg {String} dropNotAllowed
3367      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3368      */
3369     dropNotAllowed : "x-dd-drop-nodrop",
3370
3371     /**
3372      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3373      * over the current target element.
3374      * @param {String} cssClass The css class for the new drop status indicator image
3375      */
3376     setStatus : function(cssClass){
3377         cssClass = cssClass || this.dropNotAllowed;
3378         if(this.dropStatus != cssClass){
3379             this.el.replaceClass(this.dropStatus, cssClass);
3380             this.dropStatus = cssClass;
3381         }
3382     },
3383
3384     /**
3385      * Resets the status indicator to the default dropNotAllowed value
3386      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3387      */
3388     reset : function(clearGhost){
3389         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3390         this.dropStatus = this.dropNotAllowed;
3391         if(clearGhost){
3392             this.ghost.update("");
3393         }
3394     },
3395
3396     /**
3397      * Updates the contents of the ghost element
3398      * @param {String} html The html that will replace the current innerHTML of the ghost element
3399      */
3400     update : function(html){
3401         if(typeof html == "string"){
3402             this.ghost.update(html);
3403         }else{
3404             this.ghost.update("");
3405             html.style.margin = "0";
3406             this.ghost.dom.appendChild(html);
3407         }
3408         // ensure float = none set?? cant remember why though.
3409         var el = this.ghost.dom.firstChild;
3410                 if(el){
3411                         Roo.fly(el).setStyle('float', 'none');
3412                 }
3413     },
3414     
3415     /**
3416      * Returns the underlying proxy {@link Roo.Layer}
3417      * @return {Roo.Layer} el
3418     */
3419     getEl : function(){
3420         return this.el;
3421     },
3422
3423     /**
3424      * Returns the ghost element
3425      * @return {Roo.Element} el
3426      */
3427     getGhost : function(){
3428         return this.ghost;
3429     },
3430
3431     /**
3432      * Hides the proxy
3433      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3434      */
3435     hide : function(clear){
3436         this.el.hide();
3437         if(clear){
3438             this.reset(true);
3439         }
3440     },
3441
3442     /**
3443      * Stops the repair animation if it's currently running
3444      */
3445     stop : function(){
3446         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3447             this.anim.stop();
3448         }
3449     },
3450
3451     /**
3452      * Displays this proxy
3453      */
3454     show : function(){
3455         this.el.show();
3456     },
3457
3458     /**
3459      * Force the Layer to sync its shadow and shim positions to the element
3460      */
3461     sync : function(){
3462         this.el.sync();
3463     },
3464
3465     /**
3466      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3467      * invalid drop operation by the item being dragged.
3468      * @param {Array} xy The XY position of the element ([x, y])
3469      * @param {Function} callback The function to call after the repair is complete
3470      * @param {Object} scope The scope in which to execute the callback
3471      */
3472     repair : function(xy, callback, scope){
3473         this.callback = callback;
3474         this.scope = scope;
3475         if(xy && this.animRepair !== false){
3476             this.el.addClass("x-dd-drag-repair");
3477             this.el.hideUnders(true);
3478             this.anim = this.el.shift({
3479                 duration: this.repairDuration || .5,
3480                 easing: 'easeOut',
3481                 xy: xy,
3482                 stopFx: true,
3483                 callback: this.afterRepair,
3484                 scope: this
3485             });
3486         }else{
3487             this.afterRepair();
3488         }
3489     },
3490
3491     // private
3492     afterRepair : function(){
3493         this.hide(true);
3494         if(typeof this.callback == "function"){
3495             this.callback.call(this.scope || this);
3496         }
3497         this.callback = null;
3498         this.scope = null;
3499     }
3500 };/*
3501  * Based on:
3502  * Ext JS Library 1.1.1
3503  * Copyright(c) 2006-2007, Ext JS, LLC.
3504  *
3505  * Originally Released Under LGPL - original licence link has changed is not relivant.
3506  *
3507  * Fork - LGPL
3508  * <script type="text/javascript">
3509  */
3510
3511 /**
3512  * @class Roo.dd.DragSource
3513  * @extends Roo.dd.DDProxy
3514  * A simple class that provides the basic implementation needed to make any element draggable.
3515  * @constructor
3516  * @param {String/HTMLElement/Element} el The container element
3517  * @param {Object} config
3518  */
3519 Roo.dd.DragSource = function(el, config){
3520     this.el = Roo.get(el);
3521     this.dragData = {};
3522     
3523     Roo.apply(this, config);
3524     
3525     if(!this.proxy){
3526         this.proxy = new Roo.dd.StatusProxy();
3527     }
3528
3529     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3530           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3531     
3532     this.dragging = false;
3533 };
3534
3535 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3536     /**
3537      * @cfg {String} dropAllowed
3538      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3539      */
3540     dropAllowed : "x-dd-drop-ok",
3541     /**
3542      * @cfg {String} dropNotAllowed
3543      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3544      */
3545     dropNotAllowed : "x-dd-drop-nodrop",
3546
3547     /**
3548      * Returns the data object associated with this drag source
3549      * @return {Object} data An object containing arbitrary data
3550      */
3551     getDragData : function(e){
3552         return this.dragData;
3553     },
3554
3555     // private
3556     onDragEnter : function(e, id){
3557         var target = Roo.dd.DragDropMgr.getDDById(id);
3558         this.cachedTarget = target;
3559         if(this.beforeDragEnter(target, e, id) !== false){
3560             if(target.isNotifyTarget){
3561                 var status = target.notifyEnter(this, e, this.dragData);
3562                 this.proxy.setStatus(status);
3563             }else{
3564                 this.proxy.setStatus(this.dropAllowed);
3565             }
3566             
3567             if(this.afterDragEnter){
3568                 /**
3569                  * An empty function by default, but provided so that you can perform a custom action
3570                  * when the dragged item enters the drop target by providing an implementation.
3571                  * @param {Roo.dd.DragDrop} target The drop target
3572                  * @param {Event} e The event object
3573                  * @param {String} id The id of the dragged element
3574                  * @method afterDragEnter
3575                  */
3576                 this.afterDragEnter(target, e, id);
3577             }
3578         }
3579     },
3580
3581     /**
3582      * An empty function by default, but provided so that you can perform a custom action
3583      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3584      * @param {Roo.dd.DragDrop} target The drop target
3585      * @param {Event} e The event object
3586      * @param {String} id The id of the dragged element
3587      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3588      */
3589     beforeDragEnter : function(target, e, id){
3590         return true;
3591     },
3592
3593     // private
3594     alignElWithMouse: function() {
3595         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3596         this.proxy.sync();
3597     },
3598
3599     // private
3600     onDragOver : function(e, id){
3601         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3602         if(this.beforeDragOver(target, e, id) !== false){
3603             if(target.isNotifyTarget){
3604                 var status = target.notifyOver(this, e, this.dragData);
3605                 this.proxy.setStatus(status);
3606             }
3607
3608             if(this.afterDragOver){
3609                 /**
3610                  * An empty function by default, but provided so that you can perform a custom action
3611                  * while the dragged item is over the drop target by providing an implementation.
3612                  * @param {Roo.dd.DragDrop} target The drop target
3613                  * @param {Event} e The event object
3614                  * @param {String} id The id of the dragged element
3615                  * @method afterDragOver
3616                  */
3617                 this.afterDragOver(target, e, id);
3618             }
3619         }
3620     },
3621
3622     /**
3623      * An empty function by default, but provided so that you can perform a custom action
3624      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3625      * @param {Roo.dd.DragDrop} target The drop target
3626      * @param {Event} e The event object
3627      * @param {String} id The id of the dragged element
3628      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3629      */
3630     beforeDragOver : function(target, e, id){
3631         return true;
3632     },
3633
3634     // private
3635     onDragOut : function(e, id){
3636         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3637         if(this.beforeDragOut(target, e, id) !== false){
3638             if(target.isNotifyTarget){
3639                 target.notifyOut(this, e, this.dragData);
3640             }
3641             this.proxy.reset();
3642             if(this.afterDragOut){
3643                 /**
3644                  * An empty function by default, but provided so that you can perform a custom action
3645                  * after the dragged item is dragged out of the target without dropping.
3646                  * @param {Roo.dd.DragDrop} target The drop target
3647                  * @param {Event} e The event object
3648                  * @param {String} id The id of the dragged element
3649                  * @method afterDragOut
3650                  */
3651                 this.afterDragOut(target, e, id);
3652             }
3653         }
3654         this.cachedTarget = null;
3655     },
3656
3657     /**
3658      * An empty function by default, but provided so that you can perform a custom action before the dragged
3659      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3660      * @param {Roo.dd.DragDrop} target The drop target
3661      * @param {Event} e The event object
3662      * @param {String} id The id of the dragged element
3663      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3664      */
3665     beforeDragOut : function(target, e, id){
3666         return true;
3667     },
3668     
3669     // private
3670     onDragDrop : function(e, id){
3671         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3672         if(this.beforeDragDrop(target, e, id) !== false){
3673             if(target.isNotifyTarget){
3674                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3675                     this.onValidDrop(target, e, id);
3676                 }else{
3677                     this.onInvalidDrop(target, e, id);
3678                 }
3679             }else{
3680                 this.onValidDrop(target, e, id);
3681             }
3682             
3683             if(this.afterDragDrop){
3684                 /**
3685                  * An empty function by default, but provided so that you can perform a custom action
3686                  * after a valid drag drop has occurred by providing an implementation.
3687                  * @param {Roo.dd.DragDrop} target The drop target
3688                  * @param {Event} e The event object
3689                  * @param {String} id The id of the dropped element
3690                  * @method afterDragDrop
3691                  */
3692                 this.afterDragDrop(target, e, id);
3693             }
3694         }
3695         delete this.cachedTarget;
3696     },
3697
3698     /**
3699      * An empty function by default, but provided so that you can perform a custom action before the dragged
3700      * item is dropped onto the target and optionally cancel the onDragDrop.
3701      * @param {Roo.dd.DragDrop} target The drop target
3702      * @param {Event} e The event object
3703      * @param {String} id The id of the dragged element
3704      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3705      */
3706     beforeDragDrop : function(target, e, id){
3707         return true;
3708     },
3709
3710     // private
3711     onValidDrop : function(target, e, id){
3712         this.hideProxy();
3713         if(this.afterValidDrop){
3714             /**
3715              * An empty function by default, but provided so that you can perform a custom action
3716              * after a valid drop has occurred by providing an implementation.
3717              * @param {Object} target The target DD 
3718              * @param {Event} e The event object
3719              * @param {String} id The id of the dropped element
3720              * @method afterInvalidDrop
3721              */
3722             this.afterValidDrop(target, e, id);
3723         }
3724     },
3725
3726     // private
3727     getRepairXY : function(e, data){
3728         return this.el.getXY();  
3729     },
3730
3731     // private
3732     onInvalidDrop : function(target, e, id){
3733         this.beforeInvalidDrop(target, e, id);
3734         if(this.cachedTarget){
3735             if(this.cachedTarget.isNotifyTarget){
3736                 this.cachedTarget.notifyOut(this, e, this.dragData);
3737             }
3738             this.cacheTarget = null;
3739         }
3740         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3741
3742         if(this.afterInvalidDrop){
3743             /**
3744              * An empty function by default, but provided so that you can perform a custom action
3745              * after an invalid drop has occurred by providing an implementation.
3746              * @param {Event} e The event object
3747              * @param {String} id The id of the dropped element
3748              * @method afterInvalidDrop
3749              */
3750             this.afterInvalidDrop(e, id);
3751         }
3752     },
3753
3754     // private
3755     afterRepair : function(){
3756         if(Roo.enableFx){
3757             this.el.highlight(this.hlColor || "c3daf9");
3758         }
3759         this.dragging = false;
3760     },
3761
3762     /**
3763      * An empty function by default, but provided so that you can perform a custom action after an invalid
3764      * drop has occurred.
3765      * @param {Roo.dd.DragDrop} target The drop target
3766      * @param {Event} e The event object
3767      * @param {String} id The id of the dragged element
3768      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3769      */
3770     beforeInvalidDrop : function(target, e, id){
3771         return true;
3772     },
3773
3774     // private
3775     handleMouseDown : function(e){
3776         if(this.dragging) {
3777             return;
3778         }
3779         var data = this.getDragData(e);
3780         if(data && this.onBeforeDrag(data, e) !== false){
3781             this.dragData = data;
3782             this.proxy.stop();
3783             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3784         } 
3785     },
3786
3787     /**
3788      * An empty function by default, but provided so that you can perform a custom action before the initial
3789      * drag event begins and optionally cancel it.
3790      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3791      * @param {Event} e The event object
3792      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3793      */
3794     onBeforeDrag : function(data, e){
3795         return true;
3796     },
3797
3798     /**
3799      * An empty function by default, but provided so that you can perform a custom action once the initial
3800      * drag event has begun.  The drag cannot be canceled from this function.
3801      * @param {Number} x The x position of the click on the dragged object
3802      * @param {Number} y The y position of the click on the dragged object
3803      */
3804     onStartDrag : Roo.emptyFn,
3805
3806     // private - YUI override
3807     startDrag : function(x, y){
3808         this.proxy.reset();
3809         this.dragging = true;
3810         this.proxy.update("");
3811         this.onInitDrag(x, y);
3812         this.proxy.show();
3813     },
3814
3815     // private
3816     onInitDrag : function(x, y){
3817         var clone = this.el.dom.cloneNode(true);
3818         clone.id = Roo.id(); // prevent duplicate ids
3819         this.proxy.update(clone);
3820         this.onStartDrag(x, y);
3821         return true;
3822     },
3823
3824     /**
3825      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3826      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3827      */
3828     getProxy : function(){
3829         return this.proxy;  
3830     },
3831
3832     /**
3833      * Hides the drag source's {@link Roo.dd.StatusProxy}
3834      */
3835     hideProxy : function(){
3836         this.proxy.hide();  
3837         this.proxy.reset(true);
3838         this.dragging = false;
3839     },
3840
3841     // private
3842     triggerCacheRefresh : function(){
3843         Roo.dd.DDM.refreshCache(this.groups);
3844     },
3845
3846     // private - override to prevent hiding
3847     b4EndDrag: function(e) {
3848     },
3849
3850     // private - override to prevent moving
3851     endDrag : function(e){
3852         this.onEndDrag(this.dragData, e);
3853     },
3854
3855     // private
3856     onEndDrag : function(data, e){
3857     },
3858     
3859     // private - pin to cursor
3860     autoOffset : function(x, y) {
3861         this.setDelta(-12, -20);
3862     }    
3863 });/*
3864  * Based on:
3865  * Ext JS Library 1.1.1
3866  * Copyright(c) 2006-2007, Ext JS, LLC.
3867  *
3868  * Originally Released Under LGPL - original licence link has changed is not relivant.
3869  *
3870  * Fork - LGPL
3871  * <script type="text/javascript">
3872  */
3873
3874
3875 /**
3876  * @class Roo.dd.DropTarget
3877  * @extends Roo.dd.DDTarget
3878  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3879  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3880  * @constructor
3881  * @param {String/HTMLElement/Element} el The container element
3882  * @param {Object} config
3883  */
3884 Roo.dd.DropTarget = function(el, config){
3885     this.el = Roo.get(el);
3886     
3887     Roo.apply(this, config);
3888     
3889     if(this.containerScroll){
3890         Roo.dd.ScrollManager.register(this.el);
3891     }
3892     
3893     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
3894           {isTarget: true});
3895
3896 };
3897
3898 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3899     /**
3900      * @cfg {String} overClass
3901      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3902      */
3903     /**
3904      * @cfg {String} dropAllowed
3905      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3906      */
3907     dropAllowed : "x-dd-drop-ok",
3908     /**
3909      * @cfg {String} dropNotAllowed
3910      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3911      */
3912     dropNotAllowed : "x-dd-drop-nodrop",
3913
3914     // private
3915     isTarget : true,
3916
3917     // private
3918     isNotifyTarget : true,
3919
3920     /**
3921      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3922      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3923      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3924      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3925      * @param {Event} e The event
3926      * @param {Object} data An object containing arbitrary data supplied by the drag source
3927      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3928      * underlying {@link Roo.dd.StatusProxy} can be updated
3929      */
3930     notifyEnter : function(dd, e, data){
3931         if(this.overClass){
3932             this.el.addClass(this.overClass);
3933         }
3934         return this.dropAllowed;
3935     },
3936
3937     /**
3938      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3939      * This method will be called on every mouse movement while the drag source is over the drop target.
3940      * This default implementation simply returns the dropAllowed config value.
3941      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3942      * @param {Event} e The event
3943      * @param {Object} data An object containing arbitrary data supplied by the drag source
3944      * @return {String} status The CSS class that communicates the drop status back to the source so that the
3945      * underlying {@link Roo.dd.StatusProxy} can be updated
3946      */
3947     notifyOver : function(dd, e, data){
3948         return this.dropAllowed;
3949     },
3950
3951     /**
3952      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3953      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3954      * overClass (if any) from the drop element.
3955      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3956      * @param {Event} e The event
3957      * @param {Object} data An object containing arbitrary data supplied by the drag source
3958      */
3959     notifyOut : function(dd, e, data){
3960         if(this.overClass){
3961             this.el.removeClass(this.overClass);
3962         }
3963     },
3964
3965     /**
3966      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3967      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3968      * implementation that does something to process the drop event and returns true so that the drag source's
3969      * repair action does not run.
3970      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3971      * @param {Event} e The event
3972      * @param {Object} data An object containing arbitrary data supplied by the drag source
3973      * @return {Boolean} True if the drop was valid, else false
3974      */
3975     notifyDrop : function(dd, e, data){
3976         return false;
3977     }
3978 });/*
3979  * Based on:
3980  * Ext JS Library 1.1.1
3981  * Copyright(c) 2006-2007, Ext JS, LLC.
3982  *
3983  * Originally Released Under LGPL - original licence link has changed is not relivant.
3984  *
3985  * Fork - LGPL
3986  * <script type="text/javascript">
3987  */
3988
3989
3990 /**
3991  * @class Roo.dd.DragZone
3992  * @extends Roo.dd.DragSource
3993  * This class provides a container DD instance that proxies for multiple child node sources.<br />
3994  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
3995  * @constructor
3996  * @param {String/HTMLElement/Element} el The container element
3997  * @param {Object} config
3998  */
3999 Roo.dd.DragZone = function(el, config){
4000     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4001     if(this.containerScroll){
4002         Roo.dd.ScrollManager.register(this.el);
4003     }
4004 };
4005
4006 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4007     /**
4008      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4009      * for auto scrolling during drag operations.
4010      */
4011     /**
4012      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4013      * method after a failed drop (defaults to "c3daf9" - light blue)
4014      */
4015
4016     /**
4017      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4018      * for a valid target to drag based on the mouse down. Override this method
4019      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4020      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4021      * @param {EventObject} e The mouse down event
4022      * @return {Object} The dragData
4023      */
4024     getDragData : function(e){
4025         return Roo.dd.Registry.getHandleFromEvent(e);
4026     },
4027     
4028     /**
4029      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4030      * this.dragData.ddel
4031      * @param {Number} x The x position of the click on the dragged object
4032      * @param {Number} y The y position of the click on the dragged object
4033      * @return {Boolean} true to continue the drag, false to cancel
4034      */
4035     onInitDrag : function(x, y){
4036         this.proxy.update(this.dragData.ddel.cloneNode(true));
4037         this.onStartDrag(x, y);
4038         return true;
4039     },
4040     
4041     /**
4042      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4043      */
4044     afterRepair : function(){
4045         if(Roo.enableFx){
4046             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4047         }
4048         this.dragging = false;
4049     },
4050
4051     /**
4052      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4053      * the XY of this.dragData.ddel
4054      * @param {EventObject} e The mouse up event
4055      * @return {Array} The xy location (e.g. [100, 200])
4056      */
4057     getRepairXY : function(e){
4058         return Roo.Element.fly(this.dragData.ddel).getXY();  
4059     }
4060 });/*
4061  * Based on:
4062  * Ext JS Library 1.1.1
4063  * Copyright(c) 2006-2007, Ext JS, LLC.
4064  *
4065  * Originally Released Under LGPL - original licence link has changed is not relivant.
4066  *
4067  * Fork - LGPL
4068  * <script type="text/javascript">
4069  */
4070 /**
4071  * @class Roo.dd.DropZone
4072  * @extends Roo.dd.DropTarget
4073  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4074  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4075  * @constructor
4076  * @param {String/HTMLElement/Element} el The container element
4077  * @param {Object} config
4078  */
4079 Roo.dd.DropZone = function(el, config){
4080     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4081 };
4082
4083 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4084     /**
4085      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4086      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4087      * provide your own custom lookup.
4088      * @param {Event} e The event
4089      * @return {Object} data The custom data
4090      */
4091     getTargetFromEvent : function(e){
4092         return Roo.dd.Registry.getTargetFromEvent(e);
4093     },
4094
4095     /**
4096      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4097      * that it has registered.  This method has no default implementation and should be overridden to provide
4098      * node-specific processing if necessary.
4099      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4100      * {@link #getTargetFromEvent} for this node)
4101      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4102      * @param {Event} e The event
4103      * @param {Object} data An object containing arbitrary data supplied by the drag source
4104      */
4105     onNodeEnter : function(n, dd, e, data){
4106         
4107     },
4108
4109     /**
4110      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4111      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4112      * overridden to provide the proper feedback.
4113      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4114      * {@link #getTargetFromEvent} for this node)
4115      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4116      * @param {Event} e The event
4117      * @param {Object} data An object containing arbitrary data supplied by the drag source
4118      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4119      * underlying {@link Roo.dd.StatusProxy} can be updated
4120      */
4121     onNodeOver : function(n, dd, e, data){
4122         return this.dropAllowed;
4123     },
4124
4125     /**
4126      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4127      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4128      * node-specific processing if necessary.
4129      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4130      * {@link #getTargetFromEvent} for this node)
4131      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4132      * @param {Event} e The event
4133      * @param {Object} data An object containing arbitrary data supplied by the drag source
4134      */
4135     onNodeOut : function(n, dd, e, data){
4136         
4137     },
4138
4139     /**
4140      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4141      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4142      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4143      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4144      * {@link #getTargetFromEvent} for this node)
4145      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4146      * @param {Event} e The event
4147      * @param {Object} data An object containing arbitrary data supplied by the drag source
4148      * @return {Boolean} True if the drop was valid, else false
4149      */
4150     onNodeDrop : function(n, dd, e, data){
4151         return false;
4152     },
4153
4154     /**
4155      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4156      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4157      * it should be overridden to provide the proper feedback if necessary.
4158      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4159      * @param {Event} e The event
4160      * @param {Object} data An object containing arbitrary data supplied by the drag source
4161      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4162      * underlying {@link Roo.dd.StatusProxy} can be updated
4163      */
4164     onContainerOver : function(dd, e, data){
4165         return this.dropNotAllowed;
4166     },
4167
4168     /**
4169      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4170      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4171      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4172      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4173      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4174      * @param {Event} e The event
4175      * @param {Object} data An object containing arbitrary data supplied by the drag source
4176      * @return {Boolean} True if the drop was valid, else false
4177      */
4178     onContainerDrop : function(dd, e, data){
4179         return false;
4180     },
4181
4182     /**
4183      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4184      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4185      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4186      * you should override this method and provide a custom implementation.
4187      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4188      * @param {Event} e The event
4189      * @param {Object} data An object containing arbitrary data supplied by the drag source
4190      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4191      * underlying {@link Roo.dd.StatusProxy} can be updated
4192      */
4193     notifyEnter : function(dd, e, data){
4194         return this.dropNotAllowed;
4195     },
4196
4197     /**
4198      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4199      * This method will be called on every mouse movement while the drag source is over the drop zone.
4200      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4201      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4202      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4203      * registered node, it will call {@link #onContainerOver}.
4204      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4205      * @param {Event} e The event
4206      * @param {Object} data An object containing arbitrary data supplied by the drag source
4207      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4208      * underlying {@link Roo.dd.StatusProxy} can be updated
4209      */
4210     notifyOver : function(dd, e, data){
4211         var n = this.getTargetFromEvent(e);
4212         if(!n){ // not over valid drop target
4213             if(this.lastOverNode){
4214                 this.onNodeOut(this.lastOverNode, dd, e, data);
4215                 this.lastOverNode = null;
4216             }
4217             return this.onContainerOver(dd, e, data);
4218         }
4219         if(this.lastOverNode != n){
4220             if(this.lastOverNode){
4221                 this.onNodeOut(this.lastOverNode, dd, e, data);
4222             }
4223             this.onNodeEnter(n, dd, e, data);
4224             this.lastOverNode = n;
4225         }
4226         return this.onNodeOver(n, dd, e, data);
4227     },
4228
4229     /**
4230      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4231      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4232      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4233      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4234      * @param {Event} e The event
4235      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4236      */
4237     notifyOut : function(dd, e, data){
4238         if(this.lastOverNode){
4239             this.onNodeOut(this.lastOverNode, dd, e, data);
4240             this.lastOverNode = null;
4241         }
4242     },
4243
4244     /**
4245      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4246      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4247      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4248      * otherwise it will call {@link #onContainerDrop}.
4249      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4250      * @param {Event} e The event
4251      * @param {Object} data An object containing arbitrary data supplied by the drag source
4252      * @return {Boolean} True if the drop was valid, else false
4253      */
4254     notifyDrop : function(dd, e, data){
4255         if(this.lastOverNode){
4256             this.onNodeOut(this.lastOverNode, dd, e, data);
4257             this.lastOverNode = null;
4258         }
4259         var n = this.getTargetFromEvent(e);
4260         return n ?
4261             this.onNodeDrop(n, dd, e, data) :
4262             this.onContainerDrop(dd, e, data);
4263     },
4264
4265     // private
4266     triggerCacheRefresh : function(){
4267         Roo.dd.DDM.refreshCache(this.groups);
4268     }  
4269 });/*
4270  * Based on:
4271  * Ext JS Library 1.1.1
4272  * Copyright(c) 2006-2007, Ext JS, LLC.
4273  *
4274  * Originally Released Under LGPL - original licence link has changed is not relivant.
4275  *
4276  * Fork - LGPL
4277  * <script type="text/javascript">
4278  */
4279
4280
4281 /**
4282  * @class Roo.data.SortTypes
4283  * @singleton
4284  * Defines the default sorting (casting?) comparison functions used when sorting data.
4285  */
4286 Roo.data.SortTypes = {
4287     /**
4288      * Default sort that does nothing
4289      * @param {Mixed} s The value being converted
4290      * @return {Mixed} The comparison value
4291      */
4292     none : function(s){
4293         return s;
4294     },
4295     
4296     /**
4297      * The regular expression used to strip tags
4298      * @type {RegExp}
4299      * @property
4300      */
4301     stripTagsRE : /<\/?[^>]+>/gi,
4302     
4303     /**
4304      * Strips all HTML tags to sort on text only
4305      * @param {Mixed} s The value being converted
4306      * @return {String} The comparison value
4307      */
4308     asText : function(s){
4309         return String(s).replace(this.stripTagsRE, "");
4310     },
4311     
4312     /**
4313      * Strips all HTML tags to sort on text only - Case insensitive
4314      * @param {Mixed} s The value being converted
4315      * @return {String} The comparison value
4316      */
4317     asUCText : function(s){
4318         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4319     },
4320     
4321     /**
4322      * Case insensitive string
4323      * @param {Mixed} s The value being converted
4324      * @return {String} The comparison value
4325      */
4326     asUCString : function(s) {
4327         return String(s).toUpperCase();
4328     },
4329     
4330     /**
4331      * Date sorting
4332      * @param {Mixed} s The value being converted
4333      * @return {Number} The comparison value
4334      */
4335     asDate : function(s) {
4336         if(!s){
4337             return 0;
4338         }
4339         if(s instanceof Date){
4340             return s.getTime();
4341         }
4342         return Date.parse(String(s));
4343     },
4344     
4345     /**
4346      * Float sorting
4347      * @param {Mixed} s The value being converted
4348      * @return {Float} The comparison value
4349      */
4350     asFloat : function(s) {
4351         var val = parseFloat(String(s).replace(/,/g, ""));
4352         if(isNaN(val)) val = 0;
4353         return val;
4354     },
4355     
4356     /**
4357      * Integer sorting
4358      * @param {Mixed} s The value being converted
4359      * @return {Number} The comparison value
4360      */
4361     asInt : function(s) {
4362         var val = parseInt(String(s).replace(/,/g, ""));
4363         if(isNaN(val)) val = 0;
4364         return val;
4365     }
4366 };/*
4367  * Based on:
4368  * Ext JS Library 1.1.1
4369  * Copyright(c) 2006-2007, Ext JS, LLC.
4370  *
4371  * Originally Released Under LGPL - original licence link has changed is not relivant.
4372  *
4373  * Fork - LGPL
4374  * <script type="text/javascript">
4375  */
4376
4377 /**
4378 * @class Roo.data.Record
4379  * Instances of this class encapsulate both record <em>definition</em> information, and record
4380  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4381  * to access Records cached in an {@link Roo.data.Store} object.<br>
4382  * <p>
4383  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4384  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4385  * objects.<br>
4386  * <p>
4387  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4388  * @constructor
4389  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4390  * {@link #create}. The parameters are the same.
4391  * @param {Array} data An associative Array of data values keyed by the field name.
4392  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4393  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4394  * not specified an integer id is generated.
4395  */
4396 Roo.data.Record = function(data, id){
4397     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4398     this.data = data;
4399 };
4400
4401 /**
4402  * Generate a constructor for a specific record layout.
4403  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4404  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4405  * Each field definition object may contain the following properties: <ul>
4406  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4407  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4408  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4409  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4410  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4411  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4412  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4413  * this may be omitted.</p></li>
4414  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4415  * <ul><li>auto (Default, implies no conversion)</li>
4416  * <li>string</li>
4417  * <li>int</li>
4418  * <li>float</li>
4419  * <li>boolean</li>
4420  * <li>date</li></ul></p></li>
4421  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4422  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4423  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4424  * by the Reader into an object that will be stored in the Record. It is passed the
4425  * following parameters:<ul>
4426  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4427  * </ul></p></li>
4428  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4429  * </ul>
4430  * <br>usage:<br><pre><code>
4431 var TopicRecord = Roo.data.Record.create(
4432     {name: 'title', mapping: 'topic_title'},
4433     {name: 'author', mapping: 'username'},
4434     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4435     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4436     {name: 'lastPoster', mapping: 'user2'},
4437     {name: 'excerpt', mapping: 'post_text'}
4438 );
4439
4440 var myNewRecord = new TopicRecord({
4441     title: 'Do my job please',
4442     author: 'noobie',
4443     totalPosts: 1,
4444     lastPost: new Date(),
4445     lastPoster: 'Animal',
4446     excerpt: 'No way dude!'
4447 });
4448 myStore.add(myNewRecord);
4449 </code></pre>
4450  * @method create
4451  * @static
4452  */
4453 Roo.data.Record.create = function(o){
4454     var f = function(){
4455         f.superclass.constructor.apply(this, arguments);
4456     };
4457     Roo.extend(f, Roo.data.Record);
4458     var p = f.prototype;
4459     p.fields = new Roo.util.MixedCollection(false, function(field){
4460         return field.name;
4461     });
4462     for(var i = 0, len = o.length; i < len; i++){
4463         p.fields.add(new Roo.data.Field(o[i]));
4464     }
4465     f.getField = function(name){
4466         return p.fields.get(name);  
4467     };
4468     return f;
4469 };
4470
4471 Roo.data.Record.AUTO_ID = 1000;
4472 Roo.data.Record.EDIT = 'edit';
4473 Roo.data.Record.REJECT = 'reject';
4474 Roo.data.Record.COMMIT = 'commit';
4475
4476 Roo.data.Record.prototype = {
4477     /**
4478      * Readonly flag - true if this record has been modified.
4479      * @type Boolean
4480      */
4481     dirty : false,
4482     editing : false,
4483     error: null,
4484     modified: null,
4485
4486     // private
4487     join : function(store){
4488         this.store = store;
4489     },
4490
4491     /**
4492      * Set the named field to the specified value.
4493      * @param {String} name The name of the field to set.
4494      * @param {Object} value The value to set the field to.
4495      */
4496     set : function(name, value){
4497         if(this.data[name] == value){
4498             return;
4499         }
4500         this.dirty = true;
4501         if(!this.modified){
4502             this.modified = {};
4503         }
4504         if(typeof this.modified[name] == 'undefined'){
4505             this.modified[name] = this.data[name];
4506         }
4507         this.data[name] = value;
4508         if(!this.editing){
4509             this.store.afterEdit(this);
4510         }       
4511     },
4512
4513     /**
4514      * Get the value of the named field.
4515      * @param {String} name The name of the field to get the value of.
4516      * @return {Object} The value of the field.
4517      */
4518     get : function(name){
4519         return this.data[name]; 
4520     },
4521
4522     // private
4523     beginEdit : function(){
4524         this.editing = true;
4525         this.modified = {}; 
4526     },
4527
4528     // private
4529     cancelEdit : function(){
4530         this.editing = false;
4531         delete this.modified;
4532     },
4533
4534     // private
4535     endEdit : function(){
4536         this.editing = false;
4537         if(this.dirty && this.store){
4538             this.store.afterEdit(this);
4539         }
4540     },
4541
4542     /**
4543      * Usually called by the {@link Roo.data.Store} which owns the Record.
4544      * Rejects all changes made to the Record since either creation, or the last commit operation.
4545      * Modified fields are reverted to their original values.
4546      * <p>
4547      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4548      * of reject operations.
4549      */
4550     reject : function(){
4551         var m = this.modified;
4552         for(var n in m){
4553             if(typeof m[n] != "function"){
4554                 this.data[n] = m[n];
4555             }
4556         }
4557         this.dirty = false;
4558         delete this.modified;
4559         this.editing = false;
4560         if(this.store){
4561             this.store.afterReject(this);
4562         }
4563     },
4564
4565     /**
4566      * Usually called by the {@link Roo.data.Store} which owns the Record.
4567      * Commits all changes made to the Record since either creation, or the last commit operation.
4568      * <p>
4569      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4570      * of commit operations.
4571      */
4572     commit : function(){
4573         this.dirty = false;
4574         delete this.modified;
4575         this.editing = false;
4576         if(this.store){
4577             this.store.afterCommit(this);
4578         }
4579     },
4580
4581     // private
4582     hasError : function(){
4583         return this.error != null;
4584     },
4585
4586     // private
4587     clearError : function(){
4588         this.error = null;
4589     },
4590
4591     /**
4592      * Creates a copy of this record.
4593      * @param {String} id (optional) A new record id if you don't want to use this record's id
4594      * @return {Record}
4595      */
4596     copy : function(newId) {
4597         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4598     }
4599 };/*
4600  * Based on:
4601  * Ext JS Library 1.1.1
4602  * Copyright(c) 2006-2007, Ext JS, LLC.
4603  *
4604  * Originally Released Under LGPL - original licence link has changed is not relivant.
4605  *
4606  * Fork - LGPL
4607  * <script type="text/javascript">
4608  */
4609
4610
4611
4612 /**
4613  * @class Roo.data.Store
4614  * @extends Roo.util.Observable
4615  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4616  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4617  * <p>
4618  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4619  * has no knowledge of the format of the data returned by the Proxy.<br>
4620  * <p>
4621  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4622  * instances from the data object. These records are cached and made available through accessor functions.
4623  * @constructor
4624  * Creates a new Store.
4625  * @param {Object} config A config object containing the objects needed for the Store to access data,
4626  * and read the data into Records.
4627  */
4628 Roo.data.Store = function(config){
4629     this.data = new Roo.util.MixedCollection(false);
4630     this.data.getKey = function(o){
4631         return o.id;
4632     };
4633     this.baseParams = {};
4634     // private
4635     this.paramNames = {
4636         "start" : "start",
4637         "limit" : "limit",
4638         "sort" : "sort",
4639         "dir" : "dir"
4640     };
4641
4642     if(config && config.data){
4643         this.inlineData = config.data;
4644         delete config.data;
4645     }
4646
4647     Roo.apply(this, config);
4648     
4649     if(this.reader){ // reader passed
4650         this.reader = Roo.factory(this.reader, Roo.data);
4651         this.reader.xmodule = this.xmodule || false;
4652         if(!this.recordType){
4653             this.recordType = this.reader.recordType;
4654         }
4655         if(this.reader.onMetaChange){
4656             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4657         }
4658     }
4659
4660     if(this.recordType){
4661         this.fields = this.recordType.prototype.fields;
4662     }
4663     this.modified = [];
4664
4665     this.addEvents({
4666         /**
4667          * @event datachanged
4668          * Fires when the data cache has changed, and a widget which is using this Store
4669          * as a Record cache should refresh its view.
4670          * @param {Store} this
4671          */
4672         datachanged : true,
4673         /**
4674          * @event metachange
4675          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4676          * @param {Store} this
4677          * @param {Object} meta The JSON metadata
4678          */
4679         metachange : true,
4680         /**
4681          * @event add
4682          * Fires when Records have been added to the Store
4683          * @param {Store} this
4684          * @param {Roo.data.Record[]} records The array of Records added
4685          * @param {Number} index The index at which the record(s) were added
4686          */
4687         add : true,
4688         /**
4689          * @event remove
4690          * Fires when a Record has been removed from the Store
4691          * @param {Store} this
4692          * @param {Roo.data.Record} record The Record that was removed
4693          * @param {Number} index The index at which the record was removed
4694          */
4695         remove : true,
4696         /**
4697          * @event update
4698          * Fires when a Record has been updated
4699          * @param {Store} this
4700          * @param {Roo.data.Record} record The Record that was updated
4701          * @param {String} operation The update operation being performed.  Value may be one of:
4702          * <pre><code>
4703  Roo.data.Record.EDIT
4704  Roo.data.Record.REJECT
4705  Roo.data.Record.COMMIT
4706          * </code></pre>
4707          */
4708         update : true,
4709         /**
4710          * @event clear
4711          * Fires when the data cache has been cleared.
4712          * @param {Store} this
4713          */
4714         clear : true,
4715         /**
4716          * @event beforeload
4717          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4718          * the load action will be canceled.
4719          * @param {Store} this
4720          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4721          */
4722         beforeload : true,
4723         /**
4724          * @event load
4725          * Fires after a new set of Records has been loaded.
4726          * @param {Store} this
4727          * @param {Roo.data.Record[]} records The Records that were loaded
4728          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4729          */
4730         load : true,
4731         /**
4732          * @event loadexception
4733          * Fires if an exception occurs in the Proxy during loading.
4734          * Called with the signature of the Proxy's "loadexception" event.
4735          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4736          * 
4737          * @param {Proxy} 
4738          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4739          * @param {Object} load options 
4740          * @param {Object} jsonData from your request (normally this contains the Exception)
4741          */
4742         loadexception : true
4743     });
4744     
4745     if(this.proxy){
4746         this.proxy = Roo.factory(this.proxy, Roo.data);
4747         this.proxy.xmodule = this.xmodule || false;
4748         this.relayEvents(this.proxy,  ["loadexception"]);
4749     }
4750     this.sortToggle = {};
4751
4752     Roo.data.Store.superclass.constructor.call(this);
4753
4754     if(this.inlineData){
4755         this.loadData(this.inlineData);
4756         delete this.inlineData;
4757     }
4758 };
4759 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4760      /**
4761     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4762     * without a remote query - used by combo/forms at present.
4763     */
4764     
4765     /**
4766     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4767     */
4768     /**
4769     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4770     */
4771     /**
4772     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4773     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4774     */
4775     /**
4776     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4777     * on any HTTP request
4778     */
4779     /**
4780     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4781     */
4782     /**
4783     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4784     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4785     */
4786     remoteSort : false,
4787
4788     /**
4789     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4790      * loaded or when a record is removed. (defaults to false).
4791     */
4792     pruneModifiedRecords : false,
4793
4794     // private
4795     lastOptions : null,
4796
4797     /**
4798      * Add Records to the Store and fires the add event.
4799      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4800      */
4801     add : function(records){
4802         records = [].concat(records);
4803         for(var i = 0, len = records.length; i < len; i++){
4804             records[i].join(this);
4805         }
4806         var index = this.data.length;
4807         this.data.addAll(records);
4808         this.fireEvent("add", this, records, index);
4809     },
4810
4811     /**
4812      * Remove a Record from the Store and fires the remove event.
4813      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4814      */
4815     remove : function(record){
4816         var index = this.data.indexOf(record);
4817         this.data.removeAt(index);
4818         if(this.pruneModifiedRecords){
4819             this.modified.remove(record);
4820         }
4821         this.fireEvent("remove", this, record, index);
4822     },
4823
4824     /**
4825      * Remove all Records from the Store and fires the clear event.
4826      */
4827     removeAll : function(){
4828         this.data.clear();
4829         if(this.pruneModifiedRecords){
4830             this.modified = [];
4831         }
4832         this.fireEvent("clear", this);
4833     },
4834
4835     /**
4836      * Inserts Records to the Store at the given index and fires the add event.
4837      * @param {Number} index The start index at which to insert the passed Records.
4838      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4839      */
4840     insert : function(index, records){
4841         records = [].concat(records);
4842         for(var i = 0, len = records.length; i < len; i++){
4843             this.data.insert(index, records[i]);
4844             records[i].join(this);
4845         }
4846         this.fireEvent("add", this, records, index);
4847     },
4848
4849     /**
4850      * Get the index within the cache of the passed Record.
4851      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4852      * @return {Number} The index of the passed Record. Returns -1 if not found.
4853      */
4854     indexOf : function(record){
4855         return this.data.indexOf(record);
4856     },
4857
4858     /**
4859      * Get the index within the cache of the Record with the passed id.
4860      * @param {String} id The id of the Record to find.
4861      * @return {Number} The index of the Record. Returns -1 if not found.
4862      */
4863     indexOfId : function(id){
4864         return this.data.indexOfKey(id);
4865     },
4866
4867     /**
4868      * Get the Record with the specified id.
4869      * @param {String} id The id of the Record to find.
4870      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4871      */
4872     getById : function(id){
4873         return this.data.key(id);
4874     },
4875
4876     /**
4877      * Get the Record at the specified index.
4878      * @param {Number} index The index of the Record to find.
4879      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4880      */
4881     getAt : function(index){
4882         return this.data.itemAt(index);
4883     },
4884
4885     /**
4886      * Returns a range of Records between specified indices.
4887      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4888      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4889      * @return {Roo.data.Record[]} An array of Records
4890      */
4891     getRange : function(start, end){
4892         return this.data.getRange(start, end);
4893     },
4894
4895     // private
4896     storeOptions : function(o){
4897         o = Roo.apply({}, o);
4898         delete o.callback;
4899         delete o.scope;
4900         this.lastOptions = o;
4901     },
4902
4903     /**
4904      * Loads the Record cache from the configured Proxy using the configured Reader.
4905      * <p>
4906      * If using remote paging, then the first load call must specify the <em>start</em>
4907      * and <em>limit</em> properties in the options.params property to establish the initial
4908      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4909      * <p>
4910      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4911      * and this call will return before the new data has been loaded. Perform any post-processing
4912      * in a callback function, or in a "load" event handler.</strong>
4913      * <p>
4914      * @param {Object} options An object containing properties which control loading options:<ul>
4915      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4916      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4917      * passed the following arguments:<ul>
4918      * <li>r : Roo.data.Record[]</li>
4919      * <li>options: Options object from the load call</li>
4920      * <li>success: Boolean success indicator</li></ul></li>
4921      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4922      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4923      * </ul>
4924      */
4925     load : function(options){
4926         options = options || {};
4927         if(this.fireEvent("beforeload", this, options) !== false){
4928             this.storeOptions(options);
4929             var p = Roo.apply(options.params || {}, this.baseParams);
4930             // if meta was not loaded from remote source.. try requesting it.
4931             if (!this.reader.metaFromRemote) {
4932                 p._requestMeta = 1;
4933             }
4934             if(this.sortInfo && this.remoteSort){
4935                 var pn = this.paramNames;
4936                 p[pn["sort"]] = this.sortInfo.field;
4937                 p[pn["dir"]] = this.sortInfo.direction;
4938             }
4939             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4940         }
4941     },
4942
4943     /**
4944      * Reloads the Record cache from the configured Proxy using the configured Reader and
4945      * the options from the last load operation performed.
4946      * @param {Object} options (optional) An object containing properties which may override the options
4947      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
4948      * the most recently used options are reused).
4949      */
4950     reload : function(options){
4951         this.load(Roo.applyIf(options||{}, this.lastOptions));
4952     },
4953
4954     // private
4955     // Called as a callback by the Reader during a load operation.
4956     loadRecords : function(o, options, success){
4957         if(!o || success === false){
4958             if(success !== false){
4959                 this.fireEvent("load", this, [], options);
4960             }
4961             if(options.callback){
4962                 options.callback.call(options.scope || this, [], options, false);
4963             }
4964             return;
4965         }
4966         // if data returned failure - throw an exception.
4967         if (o.success === false) {
4968             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
4969             return;
4970         }
4971         var r = o.records, t = o.totalRecords || r.length;
4972         if(!options || options.add !== true){
4973             if(this.pruneModifiedRecords){
4974                 this.modified = [];
4975             }
4976             for(var i = 0, len = r.length; i < len; i++){
4977                 r[i].join(this);
4978             }
4979             if(this.snapshot){
4980                 this.data = this.snapshot;
4981                 delete this.snapshot;
4982             }
4983             this.data.clear();
4984             this.data.addAll(r);
4985             this.totalLength = t;
4986             this.applySort();
4987             this.fireEvent("datachanged", this);
4988         }else{
4989             this.totalLength = Math.max(t, this.data.length+r.length);
4990             this.add(r);
4991         }
4992         this.fireEvent("load", this, r, options);
4993         if(options.callback){
4994             options.callback.call(options.scope || this, r, options, true);
4995         }
4996     },
4997
4998     /**
4999      * Loads data from a passed data block. A Reader which understands the format of the data
5000      * must have been configured in the constructor.
5001      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5002      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5003      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5004      */
5005     loadData : function(o, append){
5006         var r = this.reader.readRecords(o);
5007         this.loadRecords(r, {add: append}, true);
5008     },
5009
5010     /**
5011      * Gets the number of cached records.
5012      * <p>
5013      * <em>If using paging, this may not be the total size of the dataset. If the data object
5014      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5015      * the data set size</em>
5016      */
5017     getCount : function(){
5018         return this.data.length || 0;
5019     },
5020
5021     /**
5022      * Gets the total number of records in the dataset as returned by the server.
5023      * <p>
5024      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5025      * the dataset size</em>
5026      */
5027     getTotalCount : function(){
5028         return this.totalLength || 0;
5029     },
5030
5031     /**
5032      * Returns the sort state of the Store as an object with two properties:
5033      * <pre><code>
5034  field {String} The name of the field by which the Records are sorted
5035  direction {String} The sort order, "ASC" or "DESC"
5036      * </code></pre>
5037      */
5038     getSortState : function(){
5039         return this.sortInfo;
5040     },
5041
5042     // private
5043     applySort : function(){
5044         if(this.sortInfo && !this.remoteSort){
5045             var s = this.sortInfo, f = s.field;
5046             var st = this.fields.get(f).sortType;
5047             var fn = function(r1, r2){
5048                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5049                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5050             };
5051             this.data.sort(s.direction, fn);
5052             if(this.snapshot && this.snapshot != this.data){
5053                 this.snapshot.sort(s.direction, fn);
5054             }
5055         }
5056     },
5057
5058     /**
5059      * Sets the default sort column and order to be used by the next load operation.
5060      * @param {String} fieldName The name of the field to sort by.
5061      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5062      */
5063     setDefaultSort : function(field, dir){
5064         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5065     },
5066
5067     /**
5068      * Sort the Records.
5069      * If remote sorting is used, the sort is performed on the server, and the cache is
5070      * reloaded. If local sorting is used, the cache is sorted internally.
5071      * @param {String} fieldName The name of the field to sort by.
5072      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5073      */
5074     sort : function(fieldName, dir){
5075         var f = this.fields.get(fieldName);
5076         if(!dir){
5077             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5078                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5079             }else{
5080                 dir = f.sortDir;
5081             }
5082         }
5083         this.sortToggle[f.name] = dir;
5084         this.sortInfo = {field: f.name, direction: dir};
5085         if(!this.remoteSort){
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.load(this.lastOptions);
5090         }
5091     },
5092
5093     /**
5094      * Calls the specified function for each of the Records in the cache.
5095      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5096      * Returning <em>false</em> aborts and exits the iteration.
5097      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5098      */
5099     each : function(fn, scope){
5100         this.data.each(fn, scope);
5101     },
5102
5103     /**
5104      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5105      * (e.g., during paging).
5106      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5107      */
5108     getModifiedRecords : function(){
5109         return this.modified;
5110     },
5111
5112     // private
5113     createFilterFn : function(property, value, anyMatch){
5114         if(!value.exec){ // not a regex
5115             value = String(value);
5116             if(value.length == 0){
5117                 return false;
5118             }
5119             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5120         }
5121         return function(r){
5122             return value.test(r.data[property]);
5123         };
5124     },
5125
5126     /**
5127      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5128      * @param {String} property A field on your records
5129      * @param {Number} start The record index to start at (defaults to 0)
5130      * @param {Number} end The last record index to include (defaults to length - 1)
5131      * @return {Number} The sum
5132      */
5133     sum : function(property, start, end){
5134         var rs = this.data.items, v = 0;
5135         start = start || 0;
5136         end = (end || end === 0) ? end : rs.length-1;
5137
5138         for(var i = start; i <= end; i++){
5139             v += (rs[i].data[property] || 0);
5140         }
5141         return v;
5142     },
5143
5144     /**
5145      * Filter the records by a specified property.
5146      * @param {String} field A field on your records
5147      * @param {String/RegExp} value Either a string that the field
5148      * should start with or a RegExp to test against the field
5149      * @param {Boolean} anyMatch True to match any part not just the beginning
5150      */
5151     filter : function(property, value, anyMatch){
5152         var fn = this.createFilterFn(property, value, anyMatch);
5153         return fn ? this.filterBy(fn) : this.clearFilter();
5154     },
5155
5156     /**
5157      * Filter by a function. The specified function will be called with each
5158      * record in this data source. If the function returns true the record is included,
5159      * otherwise it is filtered.
5160      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5161      * @param {Object} scope (optional) The scope of the function (defaults to this)
5162      */
5163     filterBy : function(fn, scope){
5164         this.snapshot = this.snapshot || this.data;
5165         this.data = this.queryBy(fn, scope||this);
5166         this.fireEvent("datachanged", this);
5167     },
5168
5169     /**
5170      * Query the records by a specified property.
5171      * @param {String} field A field on your records
5172      * @param {String/RegExp} value Either a string that the field
5173      * should start with or a RegExp to test against the field
5174      * @param {Boolean} anyMatch True to match any part not just the beginning
5175      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5176      */
5177     query : function(property, value, anyMatch){
5178         var fn = this.createFilterFn(property, value, anyMatch);
5179         return fn ? this.queryBy(fn) : this.data.clone();
5180     },
5181
5182     /**
5183      * Query by a function. The specified function will be called with each
5184      * record in this data source. If the function returns true the record is included
5185      * in the results.
5186      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5187      * @param {Object} scope (optional) The scope of the function (defaults to this)
5188       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5189      **/
5190     queryBy : function(fn, scope){
5191         var data = this.snapshot || this.data;
5192         return data.filterBy(fn, scope||this);
5193     },
5194
5195     /**
5196      * Collects unique values for a particular dataIndex from this store.
5197      * @param {String} dataIndex The property to collect
5198      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5199      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5200      * @return {Array} An array of the unique values
5201      **/
5202     collect : function(dataIndex, allowNull, bypassFilter){
5203         var d = (bypassFilter === true && this.snapshot) ?
5204                 this.snapshot.items : this.data.items;
5205         var v, sv, r = [], l = {};
5206         for(var i = 0, len = d.length; i < len; i++){
5207             v = d[i].data[dataIndex];
5208             sv = String(v);
5209             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5210                 l[sv] = true;
5211                 r[r.length] = v;
5212             }
5213         }
5214         return r;
5215     },
5216
5217     /**
5218      * Revert to a view of the Record cache with no filtering applied.
5219      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5220      */
5221     clearFilter : function(suppressEvent){
5222         if(this.snapshot && this.snapshot != this.data){
5223             this.data = this.snapshot;
5224             delete this.snapshot;
5225             if(suppressEvent !== true){
5226                 this.fireEvent("datachanged", this);
5227             }
5228         }
5229     },
5230
5231     // private
5232     afterEdit : function(record){
5233         if(this.modified.indexOf(record) == -1){
5234             this.modified.push(record);
5235         }
5236         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5237     },
5238
5239     // private
5240     afterReject : function(record){
5241         this.modified.remove(record);
5242         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5243     },
5244
5245     // private
5246     afterCommit : function(record){
5247         this.modified.remove(record);
5248         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5249     },
5250
5251     /**
5252      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5253      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5254      */
5255     commitChanges : function(){
5256         var m = this.modified.slice(0);
5257         this.modified = [];
5258         for(var i = 0, len = m.length; i < len; i++){
5259             m[i].commit();
5260         }
5261     },
5262
5263     /**
5264      * Cancel outstanding changes on all changed records.
5265      */
5266     rejectChanges : function(){
5267         var m = this.modified.slice(0);
5268         this.modified = [];
5269         for(var i = 0, len = m.length; i < len; i++){
5270             m[i].reject();
5271         }
5272     },
5273
5274     onMetaChange : function(meta, rtype, o){
5275         this.recordType = rtype;
5276         this.fields = rtype.prototype.fields;
5277         delete this.snapshot;
5278         this.sortInfo = meta.sortInfo || this.sortInfo;
5279         this.modified = [];
5280         this.fireEvent('metachange', this, this.reader.meta);
5281     }
5282 });/*
5283  * Based on:
5284  * Ext JS Library 1.1.1
5285  * Copyright(c) 2006-2007, Ext JS, LLC.
5286  *
5287  * Originally Released Under LGPL - original licence link has changed is not relivant.
5288  *
5289  * Fork - LGPL
5290  * <script type="text/javascript">
5291  */
5292
5293 /**
5294  * @class Roo.data.SimpleStore
5295  * @extends Roo.data.Store
5296  * Small helper class to make creating Stores from Array data easier.
5297  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5298  * @cfg {Array} fields An array of field definition objects, or field name strings.
5299  * @cfg {Array} data The multi-dimensional array of data
5300  * @constructor
5301  * @param {Object} config
5302  */
5303 Roo.data.SimpleStore = function(config){
5304     Roo.data.SimpleStore.superclass.constructor.call(this, {
5305         isLocal : true,
5306         reader: new Roo.data.ArrayReader({
5307                 id: config.id
5308             },
5309             Roo.data.Record.create(config.fields)
5310         ),
5311         proxy : new Roo.data.MemoryProxy(config.data)
5312     });
5313     this.load();
5314 };
5315 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5316  * Based on:
5317  * Ext JS Library 1.1.1
5318  * Copyright(c) 2006-2007, Ext JS, LLC.
5319  *
5320  * Originally Released Under LGPL - original licence link has changed is not relivant.
5321  *
5322  * Fork - LGPL
5323  * <script type="text/javascript">
5324  */
5325
5326 /**
5327 /**
5328  * @extends Roo.data.Store
5329  * @class Roo.data.JsonStore
5330  * Small helper class to make creating Stores for JSON data easier. <br/>
5331 <pre><code>
5332 var store = new Roo.data.JsonStore({
5333     url: 'get-images.php',
5334     root: 'images',
5335     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5336 });
5337 </code></pre>
5338  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5339  * JsonReader and HttpProxy (unless inline data is provided).</b>
5340  * @cfg {Array} fields An array of field definition objects, or field name strings.
5341  * @constructor
5342  * @param {Object} config
5343  */
5344 Roo.data.JsonStore = function(c){
5345     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5346         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5347         reader: new Roo.data.JsonReader(c, c.fields)
5348     }));
5349 };
5350 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5351  * Based on:
5352  * Ext JS Library 1.1.1
5353  * Copyright(c) 2006-2007, Ext JS, LLC.
5354  *
5355  * Originally Released Under LGPL - original licence link has changed is not relivant.
5356  *
5357  * Fork - LGPL
5358  * <script type="text/javascript">
5359  */
5360
5361  
5362 Roo.data.Field = function(config){
5363     if(typeof config == "string"){
5364         config = {name: config};
5365     }
5366     Roo.apply(this, config);
5367     
5368     if(!this.type){
5369         this.type = "auto";
5370     }
5371     
5372     var st = Roo.data.SortTypes;
5373     // named sortTypes are supported, here we look them up
5374     if(typeof this.sortType == "string"){
5375         this.sortType = st[this.sortType];
5376     }
5377     
5378     // set default sortType for strings and dates
5379     if(!this.sortType){
5380         switch(this.type){
5381             case "string":
5382                 this.sortType = st.asUCString;
5383                 break;
5384             case "date":
5385                 this.sortType = st.asDate;
5386                 break;
5387             default:
5388                 this.sortType = st.none;
5389         }
5390     }
5391
5392     // define once
5393     var stripRe = /[\$,%]/g;
5394
5395     // prebuilt conversion function for this field, instead of
5396     // switching every time we're reading a value
5397     if(!this.convert){
5398         var cv, dateFormat = this.dateFormat;
5399         switch(this.type){
5400             case "":
5401             case "auto":
5402             case undefined:
5403                 cv = function(v){ return v; };
5404                 break;
5405             case "string":
5406                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5407                 break;
5408             case "int":
5409                 cv = function(v){
5410                     return v !== undefined && v !== null && v !== '' ?
5411                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5412                     };
5413                 break;
5414             case "float":
5415                 cv = function(v){
5416                     return v !== undefined && v !== null && v !== '' ?
5417                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5418                     };
5419                 break;
5420             case "bool":
5421             case "boolean":
5422                 cv = function(v){ return v === true || v === "true" || v == 1; };
5423                 break;
5424             case "date":
5425                 cv = function(v){
5426                     if(!v){
5427                         return '';
5428                     }
5429                     if(v instanceof Date){
5430                         return v;
5431                     }
5432                     if(dateFormat){
5433                         if(dateFormat == "timestamp"){
5434                             return new Date(v*1000);
5435                         }
5436                         return Date.parseDate(v, dateFormat);
5437                     }
5438                     var parsed = Date.parse(v);
5439                     return parsed ? new Date(parsed) : null;
5440                 };
5441              break;
5442             
5443         }
5444         this.convert = cv;
5445     }
5446 };
5447
5448 Roo.data.Field.prototype = {
5449     dateFormat: null,
5450     defaultValue: "",
5451     mapping: null,
5452     sortType : null,
5453     sortDir : "ASC"
5454 };/*
5455  * Based on:
5456  * Ext JS Library 1.1.1
5457  * Copyright(c) 2006-2007, Ext JS, LLC.
5458  *
5459  * Originally Released Under LGPL - original licence link has changed is not relivant.
5460  *
5461  * Fork - LGPL
5462  * <script type="text/javascript">
5463  */
5464  
5465 // Base class for reading structured data from a data source.  This class is intended to be
5466 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5467
5468 /**
5469  * @class Roo.data.DataReader
5470  * Base class for reading structured data from a data source.  This class is intended to be
5471  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5472  */
5473
5474 Roo.data.DataReader = function(meta, recordType){
5475     
5476     this.meta = meta;
5477     
5478     this.recordType = recordType instanceof Array ? 
5479         Roo.data.Record.create(recordType) : recordType;
5480 };
5481
5482 Roo.data.DataReader.prototype = {
5483      /**
5484      * Create an empty record
5485      * @param {Object} data (optional) - overlay some values
5486      * @return {Roo.data.Record} record created.
5487      */
5488     newRow :  function(d) {
5489         var da =  {};
5490         this.recordType.prototype.fields.each(function(c) {
5491             switch( c.type) {
5492                 case 'int' : da[c.name] = 0; break;
5493                 case 'date' : da[c.name] = new Date(); break;
5494                 case 'float' : da[c.name] = 0.0; break;
5495                 case 'boolean' : da[c.name] = false; break;
5496                 default : da[c.name] = ""; break;
5497             }
5498             
5499         });
5500         return new this.recordType(Roo.apply(da, d));
5501     }
5502     
5503 };/*
5504  * Based on:
5505  * Ext JS Library 1.1.1
5506  * Copyright(c) 2006-2007, Ext JS, LLC.
5507  *
5508  * Originally Released Under LGPL - original licence link has changed is not relivant.
5509  *
5510  * Fork - LGPL
5511  * <script type="text/javascript">
5512  */
5513
5514 /**
5515  * @class Roo.data.DataProxy
5516  * @extends Roo.data.Observable
5517  * This class is an abstract base class for implementations which provide retrieval of
5518  * unformatted data objects.<br>
5519  * <p>
5520  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5521  * (of the appropriate type which knows how to parse the data object) to provide a block of
5522  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5523  * <p>
5524  * Custom implementations must implement the load method as described in
5525  * {@link Roo.data.HttpProxy#load}.
5526  */
5527 Roo.data.DataProxy = function(){
5528     this.addEvents({
5529         /**
5530          * @event beforeload
5531          * Fires before a network request is made to retrieve a data object.
5532          * @param {Object} This DataProxy object.
5533          * @param {Object} params The params parameter to the load function.
5534          */
5535         beforeload : true,
5536         /**
5537          * @event load
5538          * Fires before the load method's callback is called.
5539          * @param {Object} This DataProxy object.
5540          * @param {Object} o The data object.
5541          * @param {Object} arg The callback argument object passed to the load function.
5542          */
5543         load : true,
5544         /**
5545          * @event loadexception
5546          * Fires if an Exception occurs during data retrieval.
5547          * @param {Object} This DataProxy object.
5548          * @param {Object} o The data object.
5549          * @param {Object} arg The callback argument object passed to the load function.
5550          * @param {Object} e The Exception.
5551          */
5552         loadexception : true
5553     });
5554     Roo.data.DataProxy.superclass.constructor.call(this);
5555 };
5556
5557 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5558
5559     /**
5560      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5561      */
5562 /*
5563  * Based on:
5564  * Ext JS Library 1.1.1
5565  * Copyright(c) 2006-2007, Ext JS, LLC.
5566  *
5567  * Originally Released Under LGPL - original licence link has changed is not relivant.
5568  *
5569  * Fork - LGPL
5570  * <script type="text/javascript">
5571  */
5572 /**
5573  * @class Roo.data.MemoryProxy
5574  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5575  * to the Reader when its load method is called.
5576  * @constructor
5577  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5578  */
5579 Roo.data.MemoryProxy = function(data){
5580     if (data.data) {
5581         data = data.data;
5582     }
5583     Roo.data.MemoryProxy.superclass.constructor.call(this);
5584     this.data = data;
5585 };
5586
5587 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5588     /**
5589      * Load data from the requested source (in this case an in-memory
5590      * data object passed to the constructor), read the data object into
5591      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5592      * process that block using the passed callback.
5593      * @param {Object} params This parameter is not used by the MemoryProxy class.
5594      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5595      * object into a block of Roo.data.Records.
5596      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5597      * The function must be passed <ul>
5598      * <li>The Record block object</li>
5599      * <li>The "arg" argument from the load function</li>
5600      * <li>A boolean success indicator</li>
5601      * </ul>
5602      * @param {Object} scope The scope in which to call the callback
5603      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5604      */
5605     load : function(params, reader, callback, scope, arg){
5606         params = params || {};
5607         var result;
5608         try {
5609             result = reader.readRecords(this.data);
5610         }catch(e){
5611             this.fireEvent("loadexception", this, arg, null, e);
5612             callback.call(scope, null, arg, false);
5613             return;
5614         }
5615         callback.call(scope, result, arg, true);
5616     },
5617     
5618     // private
5619     update : function(params, records){
5620         
5621     }
5622 });/*
5623  * Based on:
5624  * Ext JS Library 1.1.1
5625  * Copyright(c) 2006-2007, Ext JS, LLC.
5626  *
5627  * Originally Released Under LGPL - original licence link has changed is not relivant.
5628  *
5629  * Fork - LGPL
5630  * <script type="text/javascript">
5631  */
5632 /**
5633  * @class Roo.data.HttpProxy
5634  * @extends Roo.data.DataProxy
5635  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5636  * configured to reference a certain URL.<br><br>
5637  * <p>
5638  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5639  * from which the running page was served.<br><br>
5640  * <p>
5641  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5642  * <p>
5643  * Be aware that to enable the browser to parse an XML document, the server must set
5644  * the Content-Type header in the HTTP response to "text/xml".
5645  * @constructor
5646  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5647  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5648  * will be used to make the request.
5649  */
5650 Roo.data.HttpProxy = function(conn){
5651     Roo.data.HttpProxy.superclass.constructor.call(this);
5652     // is conn a conn config or a real conn?
5653     this.conn = conn;
5654     this.useAjax = !conn || !conn.events;
5655   
5656 };
5657
5658 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5659     // thse are take from connection...
5660     
5661     /**
5662      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5663      */
5664     /**
5665      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5666      * extra parameters to each request made by this object. (defaults to undefined)
5667      */
5668     /**
5669      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5670      *  to each request made by this object. (defaults to undefined)
5671      */
5672     /**
5673      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5674      */
5675     /**
5676      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5677      */
5678      /**
5679      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5680      * @type Boolean
5681      */
5682   
5683
5684     /**
5685      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5686      * @type Boolean
5687      */
5688     /**
5689      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5690      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5691      * a finer-grained basis than the DataProxy events.
5692      */
5693     getConnection : function(){
5694         return this.useAjax ? Roo.Ajax : this.conn;
5695     },
5696
5697     /**
5698      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5699      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5700      * process that block using the passed callback.
5701      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5702      * for the request to the remote server.
5703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5704      * object into a block of Roo.data.Records.
5705      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5706      * The function must be passed <ul>
5707      * <li>The Record block object</li>
5708      * <li>The "arg" argument from the load function</li>
5709      * <li>A boolean success indicator</li>
5710      * </ul>
5711      * @param {Object} scope The scope in which to call the callback
5712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713      */
5714     load : function(params, reader, callback, scope, arg){
5715         if(this.fireEvent("beforeload", this, params) !== false){
5716             var  o = {
5717                 params : params || {},
5718                 request: {
5719                     callback : callback,
5720                     scope : scope,
5721                     arg : arg
5722                 },
5723                 reader: reader,
5724                 callback : this.loadResponse,
5725                 scope: this
5726             };
5727             if(this.useAjax){
5728                 Roo.applyIf(o, this.conn);
5729                 if(this.activeRequest){
5730                     Roo.Ajax.abort(this.activeRequest);
5731                 }
5732                 this.activeRequest = Roo.Ajax.request(o);
5733             }else{
5734                 this.conn.request(o);
5735             }
5736         }else{
5737             callback.call(scope||this, null, arg, false);
5738         }
5739     },
5740
5741     // private
5742     loadResponse : function(o, success, response){
5743         delete this.activeRequest;
5744         if(!success){
5745             this.fireEvent("loadexception", this, o, response);
5746             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5747             return;
5748         }
5749         var result;
5750         try {
5751             result = o.reader.read(response);
5752         }catch(e){
5753             this.fireEvent("loadexception", this, o, response, e);
5754             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5755             return;
5756         }
5757         
5758         this.fireEvent("load", this, o, o.request.arg);
5759         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5760     },
5761
5762     // private
5763     update : function(dataSet){
5764
5765     },
5766
5767     // private
5768     updateResponse : function(dataSet){
5769
5770     }
5771 });/*
5772  * Based on:
5773  * Ext JS Library 1.1.1
5774  * Copyright(c) 2006-2007, Ext JS, LLC.
5775  *
5776  * Originally Released Under LGPL - original licence link has changed is not relivant.
5777  *
5778  * Fork - LGPL
5779  * <script type="text/javascript">
5780  */
5781
5782 /**
5783  * @class Roo.data.ScriptTagProxy
5784  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5785  * other than the originating domain of the running page.<br><br>
5786  * <p>
5787  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5788  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5789  * <p>
5790  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5791  * source code that is used as the source inside a &lt;script> tag.<br><br>
5792  * <p>
5793  * In order for the browser to process the returned data, the server must wrap the data object
5794  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5795  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5796  * depending on whether the callback name was passed:
5797  * <p>
5798  * <pre><code>
5799 boolean scriptTag = false;
5800 String cb = request.getParameter("callback");
5801 if (cb != null) {
5802     scriptTag = true;
5803     response.setContentType("text/javascript");
5804 } else {
5805     response.setContentType("application/x-json");
5806 }
5807 Writer out = response.getWriter();
5808 if (scriptTag) {
5809     out.write(cb + "(");
5810 }
5811 out.print(dataBlock.toJsonString());
5812 if (scriptTag) {
5813     out.write(");");
5814 }
5815 </pre></code>
5816  *
5817  * @constructor
5818  * @param {Object} config A configuration object.
5819  */
5820 Roo.data.ScriptTagProxy = function(config){
5821     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5822     Roo.apply(this, config);
5823     this.head = document.getElementsByTagName("head")[0];
5824 };
5825
5826 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5827
5828 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5829     /**
5830      * @cfg {String} url The URL from which to request the data object.
5831      */
5832     /**
5833      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5834      */
5835     timeout : 30000,
5836     /**
5837      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5838      * the server the name of the callback function set up by the load call to process the returned data object.
5839      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5840      * javascript output which calls this named function passing the data object as its only parameter.
5841      */
5842     callbackParam : "callback",
5843     /**
5844      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5845      * name to the request.
5846      */
5847     nocache : true,
5848
5849     /**
5850      * Load data from the configured URL, read the data object into
5851      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5852      * process that block using the passed callback.
5853      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5854      * for the request to the remote server.
5855      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5856      * object into a block of Roo.data.Records.
5857      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5858      * The function must be passed <ul>
5859      * <li>The Record block object</li>
5860      * <li>The "arg" argument from the load function</li>
5861      * <li>A boolean success indicator</li>
5862      * </ul>
5863      * @param {Object} scope The scope in which to call the callback
5864      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5865      */
5866     load : function(params, reader, callback, scope, arg){
5867         if(this.fireEvent("beforeload", this, params) !== false){
5868
5869             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5870
5871             var url = this.url;
5872             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5873             if(this.nocache){
5874                 url += "&_dc=" + (new Date().getTime());
5875             }
5876             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5877             var trans = {
5878                 id : transId,
5879                 cb : "stcCallback"+transId,
5880                 scriptId : "stcScript"+transId,
5881                 params : params,
5882                 arg : arg,
5883                 url : url,
5884                 callback : callback,
5885                 scope : scope,
5886                 reader : reader
5887             };
5888             var conn = this;
5889
5890             window[trans.cb] = function(o){
5891                 conn.handleResponse(o, trans);
5892             };
5893
5894             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5895
5896             if(this.autoAbort !== false){
5897                 this.abort();
5898             }
5899
5900             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5901
5902             var script = document.createElement("script");
5903             script.setAttribute("src", url);
5904             script.setAttribute("type", "text/javascript");
5905             script.setAttribute("id", trans.scriptId);
5906             this.head.appendChild(script);
5907
5908             this.trans = trans;
5909         }else{
5910             callback.call(scope||this, null, arg, false);
5911         }
5912     },
5913
5914     // private
5915     isLoading : function(){
5916         return this.trans ? true : false;
5917     },
5918
5919     /**
5920      * Abort the current server request.
5921      */
5922     abort : function(){
5923         if(this.isLoading()){
5924             this.destroyTrans(this.trans);
5925         }
5926     },
5927
5928     // private
5929     destroyTrans : function(trans, isLoaded){
5930         this.head.removeChild(document.getElementById(trans.scriptId));
5931         clearTimeout(trans.timeoutId);
5932         if(isLoaded){
5933             window[trans.cb] = undefined;
5934             try{
5935                 delete window[trans.cb];
5936             }catch(e){}
5937         }else{
5938             // if hasn't been loaded, wait for load to remove it to prevent script error
5939             window[trans.cb] = function(){
5940                 window[trans.cb] = undefined;
5941                 try{
5942                     delete window[trans.cb];
5943                 }catch(e){}
5944             };
5945         }
5946     },
5947
5948     // private
5949     handleResponse : function(o, trans){
5950         this.trans = false;
5951         this.destroyTrans(trans, true);
5952         var result;
5953         try {
5954             result = trans.reader.readRecords(o);
5955         }catch(e){
5956             this.fireEvent("loadexception", this, o, trans.arg, e);
5957             trans.callback.call(trans.scope||window, null, trans.arg, false);
5958             return;
5959         }
5960         this.fireEvent("load", this, o, trans.arg);
5961         trans.callback.call(trans.scope||window, result, trans.arg, true);
5962     },
5963
5964     // private
5965     handleFailure : function(trans){
5966         this.trans = false;
5967         this.destroyTrans(trans, false);
5968         this.fireEvent("loadexception", this, null, trans.arg);
5969         trans.callback.call(trans.scope||window, null, trans.arg, false);
5970     }
5971 });/*
5972  * Based on:
5973  * Ext JS Library 1.1.1
5974  * Copyright(c) 2006-2007, Ext JS, LLC.
5975  *
5976  * Originally Released Under LGPL - original licence link has changed is not relivant.
5977  *
5978  * Fork - LGPL
5979  * <script type="text/javascript">
5980  */
5981
5982 /**
5983  * @class Roo.data.JsonReader
5984  * @extends Roo.data.DataReader
5985  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
5986  * based on mappings in a provided Roo.data.Record constructor.
5987  * 
5988  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
5989  * in the reply previously. 
5990  * 
5991  * <p>
5992  * Example code:
5993  * <pre><code>
5994 var RecordDef = Roo.data.Record.create([
5995     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
5996     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
5997 ]);
5998 var myReader = new Roo.data.JsonReader({
5999     totalProperty: "results",    // The property which contains the total dataset size (optional)
6000     root: "rows",                // The property which contains an Array of row objects
6001     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6002 }, RecordDef);
6003 </code></pre>
6004  * <p>
6005  * This would consume a JSON file like this:
6006  * <pre><code>
6007 { 'results': 2, 'rows': [
6008     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6009     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6010 }
6011 </code></pre>
6012  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6013  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6014  * paged from the remote server.
6015  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6016  * @cfg {String} root name of the property which contains the Array of row objects.
6017  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6018  * @constructor
6019  * Create a new JsonReader
6020  * @param {Object} meta Metadata configuration options
6021  * @param {Object} recordType Either an Array of field definition objects,
6022  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6023  */
6024 Roo.data.JsonReader = function(meta, recordType){
6025     
6026     meta = meta || {};
6027     // set some defaults:
6028     Roo.applyIf(meta, {
6029         totalProperty: 'total',
6030         successProperty : 'success',
6031         root : 'data',
6032         id : 'id'
6033     });
6034     
6035     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6036 };
6037 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6038     
6039     /**
6040      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6041      * Used by Store query builder to append _requestMeta to params.
6042      * 
6043      */
6044     metaFromRemote : false,
6045     /**
6046      * This method is only used by a DataProxy which has retrieved data from a remote server.
6047      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6048      * @return {Object} data A data block which is used by an Roo.data.Store object as
6049      * a cache of Roo.data.Records.
6050      */
6051     read : function(response){
6052         var json = response.responseText;
6053        
6054         var o = /* eval:var:o */ eval("("+json+")");
6055         if(!o) {
6056             throw {message: "JsonReader.read: Json object not found"};
6057         }
6058         
6059         if(o.metaData){
6060             
6061             delete this.ef;
6062             this.metaFromRemote = true;
6063             this.meta = o.metaData;
6064             this.recordType = Roo.data.Record.create(o.metaData.fields);
6065             this.onMetaChange(this.meta, this.recordType, o);
6066         }
6067         return this.readRecords(o);
6068     },
6069
6070     // private function a store will implement
6071     onMetaChange : function(meta, recordType, o){
6072
6073     },
6074
6075     /**
6076          * @ignore
6077          */
6078     simpleAccess: function(obj, subsc) {
6079         return obj[subsc];
6080     },
6081
6082         /**
6083          * @ignore
6084          */
6085     getJsonAccessor: function(){
6086         var re = /[\[\.]/;
6087         return function(expr) {
6088             try {
6089                 return(re.test(expr))
6090                     ? new Function("obj", "return obj." + expr)
6091                     : function(obj){
6092                         return obj[expr];
6093                     };
6094             } catch(e){}
6095             return Roo.emptyFn;
6096         };
6097     }(),
6098
6099     /**
6100      * Create a data block containing Roo.data.Records from an XML document.
6101      * @param {Object} o An object which contains an Array of row objects in the property specified
6102      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6103      * which contains the total size of the dataset.
6104      * @return {Object} data A data block which is used by an Roo.data.Store object as
6105      * a cache of Roo.data.Records.
6106      */
6107     readRecords : function(o){
6108         /**
6109          * After any data loads, the raw JSON data is available for further custom processing.
6110          * @type Object
6111          */
6112         this.jsonData = o;
6113         var s = this.meta, Record = this.recordType,
6114             f = Record.prototype.fields, fi = f.items, fl = f.length;
6115
6116 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6117         if (!this.ef) {
6118             if(s.totalProperty) {
6119                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6120                 }
6121                 if(s.successProperty) {
6122                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6123                 }
6124                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6125                 if (s.id) {
6126                         var g = this.getJsonAccessor(s.id);
6127                         this.getId = function(rec) {
6128                                 var r = g(rec);
6129                                 return (r === undefined || r === "") ? null : r;
6130                         };
6131                 } else {
6132                         this.getId = function(){return null;};
6133                 }
6134             this.ef = [];
6135             for(var jj = 0; jj < fl; jj++){
6136                 f = fi[jj];
6137                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6138                 this.ef[jj] = this.getJsonAccessor(map);
6139             }
6140         }
6141
6142         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6143         if(s.totalProperty){
6144             var vt = parseInt(this.getTotal(o), 10);
6145             if(!isNaN(vt)){
6146                 totalRecords = vt;
6147             }
6148         }
6149         if(s.successProperty){
6150             var vs = this.getSuccess(o);
6151             if(vs === false || vs === 'false'){
6152                 success = false;
6153             }
6154         }
6155         var records = [];
6156             for(var i = 0; i < c; i++){
6157                     var n = root[i];
6158                 var values = {};
6159                 var id = this.getId(n);
6160                 for(var j = 0; j < fl; j++){
6161                     f = fi[j];
6162                 var v = this.ef[j](n);
6163                 if (!f.convert) {
6164                     Roo.log('missing convert for ' + f.name);
6165                     Roo.log(f);
6166                     continue;
6167                 }
6168                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6169                 }
6170                 var record = new Record(values, id);
6171                 record.json = n;
6172                 records[i] = record;
6173             }
6174             return {
6175                 success : success,
6176                 records : records,
6177                 totalRecords : totalRecords
6178             };
6179     }
6180 });/*
6181  * Based on:
6182  * Ext JS Library 1.1.1
6183  * Copyright(c) 2006-2007, Ext JS, LLC.
6184  *
6185  * Originally Released Under LGPL - original licence link has changed is not relivant.
6186  *
6187  * Fork - LGPL
6188  * <script type="text/javascript">
6189  */
6190
6191 /**
6192  * @class Roo.data.XmlReader
6193  * @extends Roo.data.DataReader
6194  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6195  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6196  * <p>
6197  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6198  * header in the HTTP response must be set to "text/xml".</em>
6199  * <p>
6200  * Example code:
6201  * <pre><code>
6202 var RecordDef = Roo.data.Record.create([
6203    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6204    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6205 ]);
6206 var myReader = new Roo.data.XmlReader({
6207    totalRecords: "results", // The element which contains the total dataset size (optional)
6208    record: "row",           // The repeated element which contains row information
6209    id: "id"                 // The element within the row that provides an ID for the record (optional)
6210 }, RecordDef);
6211 </code></pre>
6212  * <p>
6213  * This would consume an XML file like this:
6214  * <pre><code>
6215 &lt;?xml?>
6216 &lt;dataset>
6217  &lt;results>2&lt;/results>
6218  &lt;row>
6219    &lt;id>1&lt;/id>
6220    &lt;name>Bill&lt;/name>
6221    &lt;occupation>Gardener&lt;/occupation>
6222  &lt;/row>
6223  &lt;row>
6224    &lt;id>2&lt;/id>
6225    &lt;name>Ben&lt;/name>
6226    &lt;occupation>Horticulturalist&lt;/occupation>
6227  &lt;/row>
6228 &lt;/dataset>
6229 </code></pre>
6230  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6231  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6232  * paged from the remote server.
6233  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6234  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6235  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6236  * a record identifier value.
6237  * @constructor
6238  * Create a new XmlReader
6239  * @param {Object} meta Metadata configuration options
6240  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6241  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6242  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6243  */
6244 Roo.data.XmlReader = function(meta, recordType){
6245     meta = meta || {};
6246     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6247 };
6248 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6249     /**
6250      * This method is only used by a DataProxy which has retrieved data from a remote server.
6251          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6252          * to contain a method called 'responseXML' that returns an XML document object.
6253      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6254      * a cache of Roo.data.Records.
6255      */
6256     read : function(response){
6257         var doc = response.responseXML;
6258         if(!doc) {
6259             throw {message: "XmlReader.read: XML Document not available"};
6260         }
6261         return this.readRecords(doc);
6262     },
6263
6264     /**
6265      * Create a data block containing Roo.data.Records from an XML document.
6266          * @param {Object} doc A parsed XML document.
6267      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6268      * a cache of Roo.data.Records.
6269      */
6270     readRecords : function(doc){
6271         /**
6272          * After any data loads/reads, the raw XML Document is available for further custom processing.
6273          * @type XMLDocument
6274          */
6275         this.xmlData = doc;
6276         var root = doc.documentElement || doc;
6277         var q = Roo.DomQuery;
6278         var recordType = this.recordType, fields = recordType.prototype.fields;
6279         var sid = this.meta.id;
6280         var totalRecords = 0, success = true;
6281         if(this.meta.totalRecords){
6282             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6283         }
6284         
6285         if(this.meta.success){
6286             var sv = q.selectValue(this.meta.success, root, true);
6287             success = sv !== false && sv !== 'false';
6288         }
6289         var records = [];
6290         var ns = q.select(this.meta.record, root);
6291         for(var i = 0, len = ns.length; i < len; i++) {
6292                 var n = ns[i];
6293                 var values = {};
6294                 var id = sid ? q.selectValue(sid, n) : undefined;
6295                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6296                     var f = fields.items[j];
6297                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6298                     v = f.convert(v);
6299                     values[f.name] = v;
6300                 }
6301                 var record = new recordType(values, id);
6302                 record.node = n;
6303                 records[records.length] = record;
6304             }
6305
6306             return {
6307                 success : success,
6308                 records : records,
6309                 totalRecords : totalRecords || records.length
6310             };
6311     }
6312 });/*
6313  * Based on:
6314  * Ext JS Library 1.1.1
6315  * Copyright(c) 2006-2007, Ext JS, LLC.
6316  *
6317  * Originally Released Under LGPL - original licence link has changed is not relivant.
6318  *
6319  * Fork - LGPL
6320  * <script type="text/javascript">
6321  */
6322
6323 /**
6324  * @class Roo.data.ArrayReader
6325  * @extends Roo.data.DataReader
6326  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6327  * Each element of that Array represents a row of data fields. The
6328  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6329  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6330  * <p>
6331  * Example code:.
6332  * <pre><code>
6333 var RecordDef = Roo.data.Record.create([
6334     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6335     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6336 ]);
6337 var myReader = new Roo.data.ArrayReader({
6338     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6339 }, RecordDef);
6340 </code></pre>
6341  * <p>
6342  * This would consume an Array like this:
6343  * <pre><code>
6344 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6345   </code></pre>
6346  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6347  * @constructor
6348  * Create a new JsonReader
6349  * @param {Object} meta Metadata configuration options.
6350  * @param {Object} recordType Either an Array of field definition objects
6351  * as specified to {@link Roo.data.Record#create},
6352  * or an {@link Roo.data.Record} object
6353  * created using {@link Roo.data.Record#create}.
6354  */
6355 Roo.data.ArrayReader = function(meta, recordType){
6356     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6357 };
6358
6359 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6360     /**
6361      * Create a data block containing Roo.data.Records from an XML document.
6362      * @param {Object} o An Array of row objects which represents the dataset.
6363      * @return {Object} data A data block which is used by an Roo.data.Store object as
6364      * a cache of Roo.data.Records.
6365      */
6366     readRecords : function(o){
6367         var sid = this.meta ? this.meta.id : null;
6368         var recordType = this.recordType, fields = recordType.prototype.fields;
6369         var records = [];
6370         var root = o;
6371             for(var i = 0; i < root.length; i++){
6372                     var n = root[i];
6373                 var values = {};
6374                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6375                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6376                 var f = fields.items[j];
6377                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6378                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6379                 v = f.convert(v);
6380                 values[f.name] = v;
6381             }
6382                 var record = new recordType(values, id);
6383                 record.json = n;
6384                 records[records.length] = record;
6385             }
6386             return {
6387                 records : records,
6388                 totalRecords : records.length
6389             };
6390     }
6391 });/*
6392  * Based on:
6393  * Ext JS Library 1.1.1
6394  * Copyright(c) 2006-2007, Ext JS, LLC.
6395  *
6396  * Originally Released Under LGPL - original licence link has changed is not relivant.
6397  *
6398  * Fork - LGPL
6399  * <script type="text/javascript">
6400  */
6401
6402
6403 /**
6404  * @class Roo.data.Tree
6405  * @extends Roo.util.Observable
6406  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6407  * in the tree have most standard DOM functionality.
6408  * @constructor
6409  * @param {Node} root (optional) The root node
6410  */
6411 Roo.data.Tree = function(root){
6412    this.nodeHash = {};
6413    /**
6414     * The root node for this tree
6415     * @type Node
6416     */
6417    this.root = null;
6418    if(root){
6419        this.setRootNode(root);
6420    }
6421    this.addEvents({
6422        /**
6423         * @event append
6424         * Fires when a new child node is appended to a node in this tree.
6425         * @param {Tree} tree The owner tree
6426         * @param {Node} parent The parent node
6427         * @param {Node} node The newly appended node
6428         * @param {Number} index The index of the newly appended node
6429         */
6430        "append" : true,
6431        /**
6432         * @event remove
6433         * Fires when a child node is removed from a node in this tree.
6434         * @param {Tree} tree The owner tree
6435         * @param {Node} parent The parent node
6436         * @param {Node} node The child node removed
6437         */
6438        "remove" : true,
6439        /**
6440         * @event move
6441         * Fires when a node is moved to a new location in the tree
6442         * @param {Tree} tree The owner tree
6443         * @param {Node} node The node moved
6444         * @param {Node} oldParent The old parent of this node
6445         * @param {Node} newParent The new parent of this node
6446         * @param {Number} index The index it was moved to
6447         */
6448        "move" : true,
6449        /**
6450         * @event insert
6451         * Fires when a new child node is inserted in a node in this tree.
6452         * @param {Tree} tree The owner tree
6453         * @param {Node} parent The parent node
6454         * @param {Node} node The child node inserted
6455         * @param {Node} refNode The child node the node was inserted before
6456         */
6457        "insert" : true,
6458        /**
6459         * @event beforeappend
6460         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6461         * @param {Tree} tree The owner tree
6462         * @param {Node} parent The parent node
6463         * @param {Node} node The child node to be appended
6464         */
6465        "beforeappend" : true,
6466        /**
6467         * @event beforeremove
6468         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6469         * @param {Tree} tree The owner tree
6470         * @param {Node} parent The parent node
6471         * @param {Node} node The child node to be removed
6472         */
6473        "beforeremove" : true,
6474        /**
6475         * @event beforemove
6476         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6477         * @param {Tree} tree The owner tree
6478         * @param {Node} node The node being moved
6479         * @param {Node} oldParent The parent of the node
6480         * @param {Node} newParent The new parent the node is moving to
6481         * @param {Number} index The index it is being moved to
6482         */
6483        "beforemove" : true,
6484        /**
6485         * @event beforeinsert
6486         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6487         * @param {Tree} tree The owner tree
6488         * @param {Node} parent The parent node
6489         * @param {Node} node The child node to be inserted
6490         * @param {Node} refNode The child node the node is being inserted before
6491         */
6492        "beforeinsert" : true
6493    });
6494
6495     Roo.data.Tree.superclass.constructor.call(this);
6496 };
6497
6498 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6499     pathSeparator: "/",
6500
6501     proxyNodeEvent : function(){
6502         return this.fireEvent.apply(this, arguments);
6503     },
6504
6505     /**
6506      * Returns the root node for this tree.
6507      * @return {Node}
6508      */
6509     getRootNode : function(){
6510         return this.root;
6511     },
6512
6513     /**
6514      * Sets the root node for this tree.
6515      * @param {Node} node
6516      * @return {Node}
6517      */
6518     setRootNode : function(node){
6519         this.root = node;
6520         node.ownerTree = this;
6521         node.isRoot = true;
6522         this.registerNode(node);
6523         return node;
6524     },
6525
6526     /**
6527      * Gets a node in this tree by its id.
6528      * @param {String} id
6529      * @return {Node}
6530      */
6531     getNodeById : function(id){
6532         return this.nodeHash[id];
6533     },
6534
6535     registerNode : function(node){
6536         this.nodeHash[node.id] = node;
6537     },
6538
6539     unregisterNode : function(node){
6540         delete this.nodeHash[node.id];
6541     },
6542
6543     toString : function(){
6544         return "[Tree"+(this.id?" "+this.id:"")+"]";
6545     }
6546 });
6547
6548 /**
6549  * @class Roo.data.Node
6550  * @extends Roo.util.Observable
6551  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6552  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6553  * @constructor
6554  * @param {Object} attributes The attributes/config for the node
6555  */
6556 Roo.data.Node = function(attributes){
6557     /**
6558      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6559      * @type {Object}
6560      */
6561     this.attributes = attributes || {};
6562     this.leaf = this.attributes.leaf;
6563     /**
6564      * The node id. @type String
6565      */
6566     this.id = this.attributes.id;
6567     if(!this.id){
6568         this.id = Roo.id(null, "ynode-");
6569         this.attributes.id = this.id;
6570     }
6571     /**
6572      * All child nodes of this node. @type Array
6573      */
6574     this.childNodes = [];
6575     if(!this.childNodes.indexOf){ // indexOf is a must
6576         this.childNodes.indexOf = function(o){
6577             for(var i = 0, len = this.length; i < len; i++){
6578                 if(this[i] == o) {
6579                     return i;
6580                 }
6581             }
6582             return -1;
6583         };
6584     }
6585     /**
6586      * The parent node for this node. @type Node
6587      */
6588     this.parentNode = null;
6589     /**
6590      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6591      */
6592     this.firstChild = null;
6593     /**
6594      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6595      */
6596     this.lastChild = null;
6597     /**
6598      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6599      */
6600     this.previousSibling = null;
6601     /**
6602      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6603      */
6604     this.nextSibling = null;
6605
6606     this.addEvents({
6607        /**
6608         * @event append
6609         * Fires when a new child node is appended
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} this This node
6612         * @param {Node} node The newly appended node
6613         * @param {Number} index The index of the newly appended node
6614         */
6615        "append" : true,
6616        /**
6617         * @event remove
6618         * Fires when a child node is removed
6619         * @param {Tree} tree The owner tree
6620         * @param {Node} this This node
6621         * @param {Node} node The removed node
6622         */
6623        "remove" : true,
6624        /**
6625         * @event move
6626         * Fires when this node is moved to a new location in the tree
6627         * @param {Tree} tree The owner tree
6628         * @param {Node} this This node
6629         * @param {Node} oldParent The old parent of this node
6630         * @param {Node} newParent The new parent of this node
6631         * @param {Number} index The index it was moved to
6632         */
6633        "move" : true,
6634        /**
6635         * @event insert
6636         * Fires when a new child node is inserted.
6637         * @param {Tree} tree The owner tree
6638         * @param {Node} this This node
6639         * @param {Node} node The child node inserted
6640         * @param {Node} refNode The child node the node was inserted before
6641         */
6642        "insert" : true,
6643        /**
6644         * @event beforeappend
6645         * Fires before a new child is appended, return false to cancel the append.
6646         * @param {Tree} tree The owner tree
6647         * @param {Node} this This node
6648         * @param {Node} node The child node to be appended
6649         */
6650        "beforeappend" : true,
6651        /**
6652         * @event beforeremove
6653         * Fires before a child is removed, return false to cancel the remove.
6654         * @param {Tree} tree The owner tree
6655         * @param {Node} this This node
6656         * @param {Node} node The child node to be removed
6657         */
6658        "beforeremove" : true,
6659        /**
6660         * @event beforemove
6661         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6662         * @param {Tree} tree The owner tree
6663         * @param {Node} this This node
6664         * @param {Node} oldParent The parent of this node
6665         * @param {Node} newParent The new parent this node is moving to
6666         * @param {Number} index The index it is being moved to
6667         */
6668        "beforemove" : true,
6669        /**
6670         * @event beforeinsert
6671         * Fires before a new child is inserted, return false to cancel the insert.
6672         * @param {Tree} tree The owner tree
6673         * @param {Node} this This node
6674         * @param {Node} node The child node to be inserted
6675         * @param {Node} refNode The child node the node is being inserted before
6676         */
6677        "beforeinsert" : true
6678    });
6679     this.listeners = this.attributes.listeners;
6680     Roo.data.Node.superclass.constructor.call(this);
6681 };
6682
6683 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6684     fireEvent : function(evtName){
6685         // first do standard event for this node
6686         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6687             return false;
6688         }
6689         // then bubble it up to the tree if the event wasn't cancelled
6690         var ot = this.getOwnerTree();
6691         if(ot){
6692             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6693                 return false;
6694             }
6695         }
6696         return true;
6697     },
6698
6699     /**
6700      * Returns true if this node is a leaf
6701      * @return {Boolean}
6702      */
6703     isLeaf : function(){
6704         return this.leaf === true;
6705     },
6706
6707     // private
6708     setFirstChild : function(node){
6709         this.firstChild = node;
6710     },
6711
6712     //private
6713     setLastChild : function(node){
6714         this.lastChild = node;
6715     },
6716
6717
6718     /**
6719      * Returns true if this node is the last child of its parent
6720      * @return {Boolean}
6721      */
6722     isLast : function(){
6723        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6724     },
6725
6726     /**
6727      * Returns true if this node is the first child of its parent
6728      * @return {Boolean}
6729      */
6730     isFirst : function(){
6731        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6732     },
6733
6734     hasChildNodes : function(){
6735         return !this.isLeaf() && this.childNodes.length > 0;
6736     },
6737
6738     /**
6739      * Insert node(s) as the last child node of this node.
6740      * @param {Node/Array} node The node or Array of nodes to append
6741      * @return {Node} The appended node if single append, or null if an array was passed
6742      */
6743     appendChild : function(node){
6744         var multi = false;
6745         if(node instanceof Array){
6746             multi = node;
6747         }else if(arguments.length > 1){
6748             multi = arguments;
6749         }
6750         // if passed an array or multiple args do them one by one
6751         if(multi){
6752             for(var i = 0, len = multi.length; i < len; i++) {
6753                 this.appendChild(multi[i]);
6754             }
6755         }else{
6756             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6757                 return false;
6758             }
6759             var index = this.childNodes.length;
6760             var oldParent = node.parentNode;
6761             // it's a move, make sure we move it cleanly
6762             if(oldParent){
6763                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6764                     return false;
6765                 }
6766                 oldParent.removeChild(node);
6767             }
6768             index = this.childNodes.length;
6769             if(index == 0){
6770                 this.setFirstChild(node);
6771             }
6772             this.childNodes.push(node);
6773             node.parentNode = this;
6774             var ps = this.childNodes[index-1];
6775             if(ps){
6776                 node.previousSibling = ps;
6777                 ps.nextSibling = node;
6778             }else{
6779                 node.previousSibling = null;
6780             }
6781             node.nextSibling = null;
6782             this.setLastChild(node);
6783             node.setOwnerTree(this.getOwnerTree());
6784             this.fireEvent("append", this.ownerTree, this, node, index);
6785             if(oldParent){
6786                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6787             }
6788             return node;
6789         }
6790     },
6791
6792     /**
6793      * Removes a child node from this node.
6794      * @param {Node} node The node to remove
6795      * @return {Node} The removed node
6796      */
6797     removeChild : function(node){
6798         var index = this.childNodes.indexOf(node);
6799         if(index == -1){
6800             return false;
6801         }
6802         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6803             return false;
6804         }
6805
6806         // remove it from childNodes collection
6807         this.childNodes.splice(index, 1);
6808
6809         // update siblings
6810         if(node.previousSibling){
6811             node.previousSibling.nextSibling = node.nextSibling;
6812         }
6813         if(node.nextSibling){
6814             node.nextSibling.previousSibling = node.previousSibling;
6815         }
6816
6817         // update child refs
6818         if(this.firstChild == node){
6819             this.setFirstChild(node.nextSibling);
6820         }
6821         if(this.lastChild == node){
6822             this.setLastChild(node.previousSibling);
6823         }
6824
6825         node.setOwnerTree(null);
6826         // clear any references from the node
6827         node.parentNode = null;
6828         node.previousSibling = null;
6829         node.nextSibling = null;
6830         this.fireEvent("remove", this.ownerTree, this, node);
6831         return node;
6832     },
6833
6834     /**
6835      * Inserts the first node before the second node in this nodes childNodes collection.
6836      * @param {Node} node The node to insert
6837      * @param {Node} refNode The node to insert before (if null the node is appended)
6838      * @return {Node} The inserted node
6839      */
6840     insertBefore : function(node, refNode){
6841         if(!refNode){ // like standard Dom, refNode can be null for append
6842             return this.appendChild(node);
6843         }
6844         // nothing to do
6845         if(node == refNode){
6846             return false;
6847         }
6848
6849         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6850             return false;
6851         }
6852         var index = this.childNodes.indexOf(refNode);
6853         var oldParent = node.parentNode;
6854         var refIndex = index;
6855
6856         // when moving internally, indexes will change after remove
6857         if(oldParent == this && this.childNodes.indexOf(node) < index){
6858             refIndex--;
6859         }
6860
6861         // it's a move, make sure we move it cleanly
6862         if(oldParent){
6863             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6864                 return false;
6865             }
6866             oldParent.removeChild(node);
6867         }
6868         if(refIndex == 0){
6869             this.setFirstChild(node);
6870         }
6871         this.childNodes.splice(refIndex, 0, node);
6872         node.parentNode = this;
6873         var ps = this.childNodes[refIndex-1];
6874         if(ps){
6875             node.previousSibling = ps;
6876             ps.nextSibling = node;
6877         }else{
6878             node.previousSibling = null;
6879         }
6880         node.nextSibling = refNode;
6881         refNode.previousSibling = node;
6882         node.setOwnerTree(this.getOwnerTree());
6883         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6884         if(oldParent){
6885             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6886         }
6887         return node;
6888     },
6889
6890     /**
6891      * Returns the child node at the specified index.
6892      * @param {Number} index
6893      * @return {Node}
6894      */
6895     item : function(index){
6896         return this.childNodes[index];
6897     },
6898
6899     /**
6900      * Replaces one child node in this node with another.
6901      * @param {Node} newChild The replacement node
6902      * @param {Node} oldChild The node to replace
6903      * @return {Node} The replaced node
6904      */
6905     replaceChild : function(newChild, oldChild){
6906         this.insertBefore(newChild, oldChild);
6907         this.removeChild(oldChild);
6908         return oldChild;
6909     },
6910
6911     /**
6912      * Returns the index of a child node
6913      * @param {Node} node
6914      * @return {Number} The index of the node or -1 if it was not found
6915      */
6916     indexOf : function(child){
6917         return this.childNodes.indexOf(child);
6918     },
6919
6920     /**
6921      * Returns the tree this node is in.
6922      * @return {Tree}
6923      */
6924     getOwnerTree : function(){
6925         // if it doesn't have one, look for one
6926         if(!this.ownerTree){
6927             var p = this;
6928             while(p){
6929                 if(p.ownerTree){
6930                     this.ownerTree = p.ownerTree;
6931                     break;
6932                 }
6933                 p = p.parentNode;
6934             }
6935         }
6936         return this.ownerTree;
6937     },
6938
6939     /**
6940      * Returns depth of this node (the root node has a depth of 0)
6941      * @return {Number}
6942      */
6943     getDepth : function(){
6944         var depth = 0;
6945         var p = this;
6946         while(p.parentNode){
6947             ++depth;
6948             p = p.parentNode;
6949         }
6950         return depth;
6951     },
6952
6953     // private
6954     setOwnerTree : function(tree){
6955         // if it's move, we need to update everyone
6956         if(tree != this.ownerTree){
6957             if(this.ownerTree){
6958                 this.ownerTree.unregisterNode(this);
6959             }
6960             this.ownerTree = tree;
6961             var cs = this.childNodes;
6962             for(var i = 0, len = cs.length; i < len; i++) {
6963                 cs[i].setOwnerTree(tree);
6964             }
6965             if(tree){
6966                 tree.registerNode(this);
6967             }
6968         }
6969     },
6970
6971     /**
6972      * Returns the path for this node. The path can be used to expand or select this node programmatically.
6973      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
6974      * @return {String} The path
6975      */
6976     getPath : function(attr){
6977         attr = attr || "id";
6978         var p = this.parentNode;
6979         var b = [this.attributes[attr]];
6980         while(p){
6981             b.unshift(p.attributes[attr]);
6982             p = p.parentNode;
6983         }
6984         var sep = this.getOwnerTree().pathSeparator;
6985         return sep + b.join(sep);
6986     },
6987
6988     /**
6989      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
6990      * function call will be the scope provided or the current node. The arguments to the function
6991      * will be the args provided or the current node. If the function returns false at any point,
6992      * the bubble is stopped.
6993      * @param {Function} fn The function to call
6994      * @param {Object} scope (optional) The scope of the function (defaults to current node)
6995      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
6996      */
6997     bubble : function(fn, scope, args){
6998         var p = this;
6999         while(p){
7000             if(fn.call(scope || p, args || p) === false){
7001                 break;
7002             }
7003             p = p.parentNode;
7004         }
7005     },
7006
7007     /**
7008      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7009      * function call will be the scope provided or the current node. The arguments to the function
7010      * will be the args provided or the current node. If the function returns false at any point,
7011      * the cascade is stopped on that branch.
7012      * @param {Function} fn The function to call
7013      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7014      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7015      */
7016     cascade : function(fn, scope, args){
7017         if(fn.call(scope || this, args || this) !== false){
7018             var cs = this.childNodes;
7019             for(var i = 0, len = cs.length; i < len; i++) {
7020                 cs[i].cascade(fn, scope, args);
7021             }
7022         }
7023     },
7024
7025     /**
7026      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7027      * function call will be the scope provided or the current node. The arguments to the function
7028      * will be the args provided or the current node. If the function returns false at any point,
7029      * the iteration stops.
7030      * @param {Function} fn The function to call
7031      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7032      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7033      */
7034     eachChild : function(fn, scope, args){
7035         var cs = this.childNodes;
7036         for(var i = 0, len = cs.length; i < len; i++) {
7037                 if(fn.call(scope || this, args || cs[i]) === false){
7038                     break;
7039                 }
7040         }
7041     },
7042
7043     /**
7044      * Finds the first child that has the attribute with the specified value.
7045      * @param {String} attribute The attribute name
7046      * @param {Mixed} value The value to search for
7047      * @return {Node} The found child or null if none was found
7048      */
7049     findChild : function(attribute, value){
7050         var cs = this.childNodes;
7051         for(var i = 0, len = cs.length; i < len; i++) {
7052                 if(cs[i].attributes[attribute] == value){
7053                     return cs[i];
7054                 }
7055         }
7056         return null;
7057     },
7058
7059     /**
7060      * Finds the first child by a custom function. The child matches if the function passed
7061      * returns true.
7062      * @param {Function} fn
7063      * @param {Object} scope (optional)
7064      * @return {Node} The found child or null if none was found
7065      */
7066     findChildBy : function(fn, scope){
7067         var cs = this.childNodes;
7068         for(var i = 0, len = cs.length; i < len; i++) {
7069                 if(fn.call(scope||cs[i], cs[i]) === true){
7070                     return cs[i];
7071                 }
7072         }
7073         return null;
7074     },
7075
7076     /**
7077      * Sorts this nodes children using the supplied sort function
7078      * @param {Function} fn
7079      * @param {Object} scope (optional)
7080      */
7081     sort : function(fn, scope){
7082         var cs = this.childNodes;
7083         var len = cs.length;
7084         if(len > 0){
7085             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7086             cs.sort(sortFn);
7087             for(var i = 0; i < len; i++){
7088                 var n = cs[i];
7089                 n.previousSibling = cs[i-1];
7090                 n.nextSibling = cs[i+1];
7091                 if(i == 0){
7092                     this.setFirstChild(n);
7093                 }
7094                 if(i == len-1){
7095                     this.setLastChild(n);
7096                 }
7097             }
7098         }
7099     },
7100
7101     /**
7102      * Returns true if this node is an ancestor (at any point) of the passed node.
7103      * @param {Node} node
7104      * @return {Boolean}
7105      */
7106     contains : function(node){
7107         return node.isAncestor(this);
7108     },
7109
7110     /**
7111      * Returns true if the passed node is an ancestor (at any point) of this node.
7112      * @param {Node} node
7113      * @return {Boolean}
7114      */
7115     isAncestor : function(node){
7116         var p = this.parentNode;
7117         while(p){
7118             if(p == node){
7119                 return true;
7120             }
7121             p = p.parentNode;
7122         }
7123         return false;
7124     },
7125
7126     toString : function(){
7127         return "[Node"+(this.id?" "+this.id:"")+"]";
7128     }
7129 });/*
7130  * Based on:
7131  * Ext JS Library 1.1.1
7132  * Copyright(c) 2006-2007, Ext JS, LLC.
7133  *
7134  * Originally Released Under LGPL - original licence link has changed is not relivant.
7135  *
7136  * Fork - LGPL
7137  * <script type="text/javascript">
7138  */
7139  
7140
7141 /**
7142  * @class Roo.ComponentMgr
7143  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7144  * @singleton
7145  */
7146 Roo.ComponentMgr = function(){
7147     var all = new Roo.util.MixedCollection();
7148
7149     return {
7150         /**
7151          * Registers a component.
7152          * @param {Roo.Component} c The component
7153          */
7154         register : function(c){
7155             all.add(c);
7156         },
7157
7158         /**
7159          * Unregisters a component.
7160          * @param {Roo.Component} c The component
7161          */
7162         unregister : function(c){
7163             all.remove(c);
7164         },
7165
7166         /**
7167          * Returns a component by id
7168          * @param {String} id The component id
7169          */
7170         get : function(id){
7171             return all.get(id);
7172         },
7173
7174         /**
7175          * Registers a function that will be called when a specified component is added to ComponentMgr
7176          * @param {String} id The component id
7177          * @param {Funtction} fn The callback function
7178          * @param {Object} scope The scope of the callback
7179          */
7180         onAvailable : function(id, fn, scope){
7181             all.on("add", function(index, o){
7182                 if(o.id == id){
7183                     fn.call(scope || o, o);
7184                     all.un("add", fn, scope);
7185                 }
7186             });
7187         }
7188     };
7189 }();/*
7190  * Based on:
7191  * Ext JS Library 1.1.1
7192  * Copyright(c) 2006-2007, Ext JS, LLC.
7193  *
7194  * Originally Released Under LGPL - original licence link has changed is not relivant.
7195  *
7196  * Fork - LGPL
7197  * <script type="text/javascript">
7198  */
7199  
7200 /**
7201  * @class Roo.Component
7202  * @extends Roo.util.Observable
7203  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7204  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7205  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7206  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7207  * All visual components (widgets) that require rendering into a layout should subclass Component.
7208  * @constructor
7209  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7210  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7211  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7212  */
7213 Roo.Component = function(config){
7214     config = config || {};
7215     if(config.tagName || config.dom || typeof config == "string"){ // element object
7216         config = {el: config, id: config.id || config};
7217     }
7218     this.initialConfig = config;
7219
7220     Roo.apply(this, config);
7221     this.addEvents({
7222         /**
7223          * @event disable
7224          * Fires after the component is disabled.
7225              * @param {Roo.Component} this
7226              */
7227         disable : true,
7228         /**
7229          * @event enable
7230          * Fires after the component is enabled.
7231              * @param {Roo.Component} this
7232              */
7233         enable : true,
7234         /**
7235          * @event beforeshow
7236          * Fires before the component is shown.  Return false to stop the show.
7237              * @param {Roo.Component} this
7238              */
7239         beforeshow : true,
7240         /**
7241          * @event show
7242          * Fires after the component is shown.
7243              * @param {Roo.Component} this
7244              */
7245         show : true,
7246         /**
7247          * @event beforehide
7248          * Fires before the component is hidden. Return false to stop the hide.
7249              * @param {Roo.Component} this
7250              */
7251         beforehide : true,
7252         /**
7253          * @event hide
7254          * Fires after the component is hidden.
7255              * @param {Roo.Component} this
7256              */
7257         hide : true,
7258         /**
7259          * @event beforerender
7260          * Fires before the component is rendered. Return false to stop the render.
7261              * @param {Roo.Component} this
7262              */
7263         beforerender : true,
7264         /**
7265          * @event render
7266          * Fires after the component is rendered.
7267              * @param {Roo.Component} this
7268              */
7269         render : true,
7270         /**
7271          * @event beforedestroy
7272          * Fires before the component is destroyed. Return false to stop the destroy.
7273              * @param {Roo.Component} this
7274              */
7275         beforedestroy : true,
7276         /**
7277          * @event destroy
7278          * Fires after the component is destroyed.
7279              * @param {Roo.Component} this
7280              */
7281         destroy : true
7282     });
7283     if(!this.id){
7284         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7285     }
7286     Roo.ComponentMgr.register(this);
7287     Roo.Component.superclass.constructor.call(this);
7288     this.initComponent();
7289     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7290         this.render(this.renderTo);
7291         delete this.renderTo;
7292     }
7293 };
7294
7295 // private
7296 Roo.Component.AUTO_ID = 1000;
7297
7298 Roo.extend(Roo.Component, Roo.util.Observable, {
7299     /**
7300      * @property {Boolean} hidden
7301      * true if this component is hidden. Read-only.
7302      */
7303     hidden : false,
7304     /**
7305      * true if this component is disabled. Read-only.
7306      */
7307     disabled : false,
7308     /**
7309      * true if this component has been rendered. Read-only.
7310      */
7311     rendered : false,
7312     
7313     /** @cfg {String} disableClass
7314      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7315      */
7316     disabledClass : "x-item-disabled",
7317         /** @cfg {Boolean} allowDomMove
7318          * Whether the component can move the Dom node when rendering (defaults to true).
7319          */
7320     allowDomMove : true,
7321     /** @cfg {String} hideMode
7322      * How this component should hidden. Supported values are
7323      * "visibility" (css visibility), "offsets" (negative offset position) and
7324      * "display" (css display) - defaults to "display".
7325      */
7326     hideMode: 'display',
7327
7328     // private
7329     ctype : "Roo.Component",
7330
7331     /** @cfg {String} actionMode 
7332      * which property holds the element that used for  hide() / show() / disable() / enable()
7333      * default is 'el' 
7334      */
7335     actionMode : "el",
7336
7337     // private
7338     getActionEl : function(){
7339         return this[this.actionMode];
7340     },
7341
7342     initComponent : Roo.emptyFn,
7343     /**
7344      * If this is a lazy rendering component, render it to its container element.
7345      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7346      */
7347     render : function(container, position){
7348         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7349             if(!container && this.el){
7350                 this.el = Roo.get(this.el);
7351                 container = this.el.dom.parentNode;
7352                 this.allowDomMove = false;
7353             }
7354             this.container = Roo.get(container);
7355             this.rendered = true;
7356             if(position !== undefined){
7357                 if(typeof position == 'number'){
7358                     position = this.container.dom.childNodes[position];
7359                 }else{
7360                     position = Roo.getDom(position);
7361                 }
7362             }
7363             this.onRender(this.container, position || null);
7364             if(this.cls){
7365                 this.el.addClass(this.cls);
7366                 delete this.cls;
7367             }
7368             if(this.style){
7369                 this.el.applyStyles(this.style);
7370                 delete this.style;
7371             }
7372             this.fireEvent("render", this);
7373             this.afterRender(this.container);
7374             if(this.hidden){
7375                 this.hide();
7376             }
7377             if(this.disabled){
7378                 this.disable();
7379             }
7380         }
7381         return this;
7382     },
7383
7384     // private
7385     // default function is not really useful
7386     onRender : function(ct, position){
7387         if(this.el){
7388             this.el = Roo.get(this.el);
7389             if(this.allowDomMove !== false){
7390                 ct.dom.insertBefore(this.el.dom, position);
7391             }
7392         }
7393     },
7394
7395     // private
7396     getAutoCreate : function(){
7397         var cfg = typeof this.autoCreate == "object" ?
7398                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7399         if(this.id && !cfg.id){
7400             cfg.id = this.id;
7401         }
7402         return cfg;
7403     },
7404
7405     // private
7406     afterRender : Roo.emptyFn,
7407
7408     /**
7409      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7410      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7411      */
7412     destroy : function(){
7413         if(this.fireEvent("beforedestroy", this) !== false){
7414             this.purgeListeners();
7415             this.beforeDestroy();
7416             if(this.rendered){
7417                 this.el.removeAllListeners();
7418                 this.el.remove();
7419                 if(this.actionMode == "container"){
7420                     this.container.remove();
7421                 }
7422             }
7423             this.onDestroy();
7424             Roo.ComponentMgr.unregister(this);
7425             this.fireEvent("destroy", this);
7426         }
7427     },
7428
7429         // private
7430     beforeDestroy : function(){
7431
7432     },
7433
7434         // private
7435         onDestroy : function(){
7436
7437     },
7438
7439     /**
7440      * Returns the underlying {@link Roo.Element}.
7441      * @return {Roo.Element} The element
7442      */
7443     getEl : function(){
7444         return this.el;
7445     },
7446
7447     /**
7448      * Returns the id of this component.
7449      * @return {String}
7450      */
7451     getId : function(){
7452         return this.id;
7453     },
7454
7455     /**
7456      * Try to focus this component.
7457      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7458      * @return {Roo.Component} this
7459      */
7460     focus : function(selectText){
7461         if(this.rendered){
7462             this.el.focus();
7463             if(selectText === true){
7464                 this.el.dom.select();
7465             }
7466         }
7467         return this;
7468     },
7469
7470     // private
7471     blur : function(){
7472         if(this.rendered){
7473             this.el.blur();
7474         }
7475         return this;
7476     },
7477
7478     /**
7479      * Disable this component.
7480      * @return {Roo.Component} this
7481      */
7482     disable : function(){
7483         if(this.rendered){
7484             this.onDisable();
7485         }
7486         this.disabled = true;
7487         this.fireEvent("disable", this);
7488         return this;
7489     },
7490
7491         // private
7492     onDisable : function(){
7493         this.getActionEl().addClass(this.disabledClass);
7494         this.el.dom.disabled = true;
7495     },
7496
7497     /**
7498      * Enable this component.
7499      * @return {Roo.Component} this
7500      */
7501     enable : function(){
7502         if(this.rendered){
7503             this.onEnable();
7504         }
7505         this.disabled = false;
7506         this.fireEvent("enable", this);
7507         return this;
7508     },
7509
7510         // private
7511     onEnable : function(){
7512         this.getActionEl().removeClass(this.disabledClass);
7513         this.el.dom.disabled = false;
7514     },
7515
7516     /**
7517      * Convenience function for setting disabled/enabled by boolean.
7518      * @param {Boolean} disabled
7519      */
7520     setDisabled : function(disabled){
7521         this[disabled ? "disable" : "enable"]();
7522     },
7523
7524     /**
7525      * Show this component.
7526      * @return {Roo.Component} this
7527      */
7528     show: function(){
7529         if(this.fireEvent("beforeshow", this) !== false){
7530             this.hidden = false;
7531             if(this.rendered){
7532                 this.onShow();
7533             }
7534             this.fireEvent("show", this);
7535         }
7536         return this;
7537     },
7538
7539     // private
7540     onShow : function(){
7541         var ae = this.getActionEl();
7542         if(this.hideMode == 'visibility'){
7543             ae.dom.style.visibility = "visible";
7544         }else if(this.hideMode == 'offsets'){
7545             ae.removeClass('x-hidden');
7546         }else{
7547             ae.dom.style.display = "";
7548         }
7549     },
7550
7551     /**
7552      * Hide this component.
7553      * @return {Roo.Component} this
7554      */
7555     hide: function(){
7556         if(this.fireEvent("beforehide", this) !== false){
7557             this.hidden = true;
7558             if(this.rendered){
7559                 this.onHide();
7560             }
7561             this.fireEvent("hide", this);
7562         }
7563         return this;
7564     },
7565
7566     // private
7567     onHide : function(){
7568         var ae = this.getActionEl();
7569         if(this.hideMode == 'visibility'){
7570             ae.dom.style.visibility = "hidden";
7571         }else if(this.hideMode == 'offsets'){
7572             ae.addClass('x-hidden');
7573         }else{
7574             ae.dom.style.display = "none";
7575         }
7576     },
7577
7578     /**
7579      * Convenience function to hide or show this component by boolean.
7580      * @param {Boolean} visible True to show, false to hide
7581      * @return {Roo.Component} this
7582      */
7583     setVisible: function(visible){
7584         if(visible) {
7585             this.show();
7586         }else{
7587             this.hide();
7588         }
7589         return this;
7590     },
7591
7592     /**
7593      * Returns true if this component is visible.
7594      */
7595     isVisible : function(){
7596         return this.getActionEl().isVisible();
7597     },
7598
7599     cloneConfig : function(overrides){
7600         overrides = overrides || {};
7601         var id = overrides.id || Roo.id();
7602         var cfg = Roo.applyIf(overrides, this.initialConfig);
7603         cfg.id = id; // prevent dup id
7604         return new this.constructor(cfg);
7605     }
7606 });/*
7607  * Based on:
7608  * Ext JS Library 1.1.1
7609  * Copyright(c) 2006-2007, Ext JS, LLC.
7610  *
7611  * Originally Released Under LGPL - original licence link has changed is not relivant.
7612  *
7613  * Fork - LGPL
7614  * <script type="text/javascript">
7615  */
7616  (function(){ 
7617 /**
7618  * @class Roo.Layer
7619  * @extends Roo.Element
7620  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7621  * automatic maintaining of shadow/shim positions.
7622  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7623  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7624  * you can pass a string with a CSS class name. False turns off the shadow.
7625  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7626  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7627  * @cfg {String} cls CSS class to add to the element
7628  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7629  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7630  * @constructor
7631  * @param {Object} config An object with config options.
7632  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7633  */
7634
7635 Roo.Layer = function(config, existingEl){
7636     config = config || {};
7637     var dh = Roo.DomHelper;
7638     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7639     if(existingEl){
7640         this.dom = Roo.getDom(existingEl);
7641     }
7642     if(!this.dom){
7643         var o = config.dh || {tag: "div", cls: "x-layer"};
7644         this.dom = dh.append(pel, o);
7645     }
7646     if(config.cls){
7647         this.addClass(config.cls);
7648     }
7649     this.constrain = config.constrain !== false;
7650     this.visibilityMode = Roo.Element.VISIBILITY;
7651     if(config.id){
7652         this.id = this.dom.id = config.id;
7653     }else{
7654         this.id = Roo.id(this.dom);
7655     }
7656     this.zindex = config.zindex || this.getZIndex();
7657     this.position("absolute", this.zindex);
7658     if(config.shadow){
7659         this.shadowOffset = config.shadowOffset || 4;
7660         this.shadow = new Roo.Shadow({
7661             offset : this.shadowOffset,
7662             mode : config.shadow
7663         });
7664     }else{
7665         this.shadowOffset = 0;
7666     }
7667     this.useShim = config.shim !== false && Roo.useShims;
7668     this.useDisplay = config.useDisplay;
7669     this.hide();
7670 };
7671
7672 var supr = Roo.Element.prototype;
7673
7674 // shims are shared among layer to keep from having 100 iframes
7675 var shims = [];
7676
7677 Roo.extend(Roo.Layer, Roo.Element, {
7678
7679     getZIndex : function(){
7680         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7681     },
7682
7683     getShim : function(){
7684         if(!this.useShim){
7685             return null;
7686         }
7687         if(this.shim){
7688             return this.shim;
7689         }
7690         var shim = shims.shift();
7691         if(!shim){
7692             shim = this.createShim();
7693             shim.enableDisplayMode('block');
7694             shim.dom.style.display = 'none';
7695             shim.dom.style.visibility = 'visible';
7696         }
7697         var pn = this.dom.parentNode;
7698         if(shim.dom.parentNode != pn){
7699             pn.insertBefore(shim.dom, this.dom);
7700         }
7701         shim.setStyle('z-index', this.getZIndex()-2);
7702         this.shim = shim;
7703         return shim;
7704     },
7705
7706     hideShim : function(){
7707         if(this.shim){
7708             this.shim.setDisplayed(false);
7709             shims.push(this.shim);
7710             delete this.shim;
7711         }
7712     },
7713
7714     disableShadow : function(){
7715         if(this.shadow){
7716             this.shadowDisabled = true;
7717             this.shadow.hide();
7718             this.lastShadowOffset = this.shadowOffset;
7719             this.shadowOffset = 0;
7720         }
7721     },
7722
7723     enableShadow : function(show){
7724         if(this.shadow){
7725             this.shadowDisabled = false;
7726             this.shadowOffset = this.lastShadowOffset;
7727             delete this.lastShadowOffset;
7728             if(show){
7729                 this.sync(true);
7730             }
7731         }
7732     },
7733
7734     // private
7735     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7736     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7737     sync : function(doShow){
7738         var sw = this.shadow;
7739         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7740             var sh = this.getShim();
7741
7742             var w = this.getWidth(),
7743                 h = this.getHeight();
7744
7745             var l = this.getLeft(true),
7746                 t = this.getTop(true);
7747
7748             if(sw && !this.shadowDisabled){
7749                 if(doShow && !sw.isVisible()){
7750                     sw.show(this);
7751                 }else{
7752                     sw.realign(l, t, w, h);
7753                 }
7754                 if(sh){
7755                     if(doShow){
7756                        sh.show();
7757                     }
7758                     // fit the shim behind the shadow, so it is shimmed too
7759                     var a = sw.adjusts, s = sh.dom.style;
7760                     s.left = (Math.min(l, l+a.l))+"px";
7761                     s.top = (Math.min(t, t+a.t))+"px";
7762                     s.width = (w+a.w)+"px";
7763                     s.height = (h+a.h)+"px";
7764                 }
7765             }else if(sh){
7766                 if(doShow){
7767                    sh.show();
7768                 }
7769                 sh.setSize(w, h);
7770                 sh.setLeftTop(l, t);
7771             }
7772             
7773         }
7774     },
7775
7776     // private
7777     destroy : function(){
7778         this.hideShim();
7779         if(this.shadow){
7780             this.shadow.hide();
7781         }
7782         this.removeAllListeners();
7783         var pn = this.dom.parentNode;
7784         if(pn){
7785             pn.removeChild(this.dom);
7786         }
7787         Roo.Element.uncache(this.id);
7788     },
7789
7790     remove : function(){
7791         this.destroy();
7792     },
7793
7794     // private
7795     beginUpdate : function(){
7796         this.updating = true;
7797     },
7798
7799     // private
7800     endUpdate : function(){
7801         this.updating = false;
7802         this.sync(true);
7803     },
7804
7805     // private
7806     hideUnders : function(negOffset){
7807         if(this.shadow){
7808             this.shadow.hide();
7809         }
7810         this.hideShim();
7811     },
7812
7813     // private
7814     constrainXY : function(){
7815         if(this.constrain){
7816             var vw = Roo.lib.Dom.getViewWidth(),
7817                 vh = Roo.lib.Dom.getViewHeight();
7818             var s = Roo.get(document).getScroll();
7819
7820             var xy = this.getXY();
7821             var x = xy[0], y = xy[1];   
7822             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7823             // only move it if it needs it
7824             var moved = false;
7825             // first validate right/bottom
7826             if((x + w) > vw+s.left){
7827                 x = vw - w - this.shadowOffset;
7828                 moved = true;
7829             }
7830             if((y + h) > vh+s.top){
7831                 y = vh - h - this.shadowOffset;
7832                 moved = true;
7833             }
7834             // then make sure top/left isn't negative
7835             if(x < s.left){
7836                 x = s.left;
7837                 moved = true;
7838             }
7839             if(y < s.top){
7840                 y = s.top;
7841                 moved = true;
7842             }
7843             if(moved){
7844                 if(this.avoidY){
7845                     var ay = this.avoidY;
7846                     if(y <= ay && (y+h) >= ay){
7847                         y = ay-h-5;   
7848                     }
7849                 }
7850                 xy = [x, y];
7851                 this.storeXY(xy);
7852                 supr.setXY.call(this, xy);
7853                 this.sync();
7854             }
7855         }
7856     },
7857
7858     isVisible : function(){
7859         return this.visible;    
7860     },
7861
7862     // private
7863     showAction : function(){
7864         this.visible = true; // track visibility to prevent getStyle calls
7865         if(this.useDisplay === true){
7866             this.setDisplayed("");
7867         }else if(this.lastXY){
7868             supr.setXY.call(this, this.lastXY);
7869         }else if(this.lastLT){
7870             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7871         }
7872     },
7873
7874     // private
7875     hideAction : function(){
7876         this.visible = false;
7877         if(this.useDisplay === true){
7878             this.setDisplayed(false);
7879         }else{
7880             this.setLeftTop(-10000,-10000);
7881         }
7882     },
7883
7884     // overridden Element method
7885     setVisible : function(v, a, d, c, e){
7886         if(v){
7887             this.showAction();
7888         }
7889         if(a && v){
7890             var cb = function(){
7891                 this.sync(true);
7892                 if(c){
7893                     c();
7894                 }
7895             }.createDelegate(this);
7896             supr.setVisible.call(this, true, true, d, cb, e);
7897         }else{
7898             if(!v){
7899                 this.hideUnders(true);
7900             }
7901             var cb = c;
7902             if(a){
7903                 cb = function(){
7904                     this.hideAction();
7905                     if(c){
7906                         c();
7907                     }
7908                 }.createDelegate(this);
7909             }
7910             supr.setVisible.call(this, v, a, d, cb, e);
7911             if(v){
7912                 this.sync(true);
7913             }else if(!a){
7914                 this.hideAction();
7915             }
7916         }
7917     },
7918
7919     storeXY : function(xy){
7920         delete this.lastLT;
7921         this.lastXY = xy;
7922     },
7923
7924     storeLeftTop : function(left, top){
7925         delete this.lastXY;
7926         this.lastLT = [left, top];
7927     },
7928
7929     // private
7930     beforeFx : function(){
7931         this.beforeAction();
7932         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7933     },
7934
7935     // private
7936     afterFx : function(){
7937         Roo.Layer.superclass.afterFx.apply(this, arguments);
7938         this.sync(this.isVisible());
7939     },
7940
7941     // private
7942     beforeAction : function(){
7943         if(!this.updating && this.shadow){
7944             this.shadow.hide();
7945         }
7946     },
7947
7948     // overridden Element method
7949     setLeft : function(left){
7950         this.storeLeftTop(left, this.getTop(true));
7951         supr.setLeft.apply(this, arguments);
7952         this.sync();
7953     },
7954
7955     setTop : function(top){
7956         this.storeLeftTop(this.getLeft(true), top);
7957         supr.setTop.apply(this, arguments);
7958         this.sync();
7959     },
7960
7961     setLeftTop : function(left, top){
7962         this.storeLeftTop(left, top);
7963         supr.setLeftTop.apply(this, arguments);
7964         this.sync();
7965     },
7966
7967     setXY : function(xy, a, d, c, e){
7968         this.fixDisplay();
7969         this.beforeAction();
7970         this.storeXY(xy);
7971         var cb = this.createCB(c);
7972         supr.setXY.call(this, xy, a, d, cb, e);
7973         if(!a){
7974             cb();
7975         }
7976     },
7977
7978     // private
7979     createCB : function(c){
7980         var el = this;
7981         return function(){
7982             el.constrainXY();
7983             el.sync(true);
7984             if(c){
7985                 c();
7986             }
7987         };
7988     },
7989
7990     // overridden Element method
7991     setX : function(x, a, d, c, e){
7992         this.setXY([x, this.getY()], a, d, c, e);
7993     },
7994
7995     // overridden Element method
7996     setY : function(y, a, d, c, e){
7997         this.setXY([this.getX(), y], a, d, c, e);
7998     },
7999
8000     // overridden Element method
8001     setSize : function(w, h, a, d, c, e){
8002         this.beforeAction();
8003         var cb = this.createCB(c);
8004         supr.setSize.call(this, w, h, a, d, cb, e);
8005         if(!a){
8006             cb();
8007         }
8008     },
8009
8010     // overridden Element method
8011     setWidth : function(w, a, d, c, e){
8012         this.beforeAction();
8013         var cb = this.createCB(c);
8014         supr.setWidth.call(this, w, a, d, cb, e);
8015         if(!a){
8016             cb();
8017         }
8018     },
8019
8020     // overridden Element method
8021     setHeight : function(h, a, d, c, e){
8022         this.beforeAction();
8023         var cb = this.createCB(c);
8024         supr.setHeight.call(this, h, a, d, cb, e);
8025         if(!a){
8026             cb();
8027         }
8028     },
8029
8030     // overridden Element method
8031     setBounds : function(x, y, w, h, a, d, c, e){
8032         this.beforeAction();
8033         var cb = this.createCB(c);
8034         if(!a){
8035             this.storeXY([x, y]);
8036             supr.setXY.call(this, [x, y]);
8037             supr.setSize.call(this, w, h, a, d, cb, e);
8038             cb();
8039         }else{
8040             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8041         }
8042         return this;
8043     },
8044     
8045     /**
8046      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8047      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8048      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8049      * @param {Number} zindex The new z-index to set
8050      * @return {this} The Layer
8051      */
8052     setZIndex : function(zindex){
8053         this.zindex = zindex;
8054         this.setStyle("z-index", zindex + 2);
8055         if(this.shadow){
8056             this.shadow.setZIndex(zindex + 1);
8057         }
8058         if(this.shim){
8059             this.shim.setStyle("z-index", zindex);
8060         }
8061     }
8062 });
8063 })();/*
8064  * Based on:
8065  * Ext JS Library 1.1.1
8066  * Copyright(c) 2006-2007, Ext JS, LLC.
8067  *
8068  * Originally Released Under LGPL - original licence link has changed is not relivant.
8069  *
8070  * Fork - LGPL
8071  * <script type="text/javascript">
8072  */
8073
8074
8075 /**
8076  * @class Roo.Shadow
8077  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8078  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8079  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8080  * @constructor
8081  * Create a new Shadow
8082  * @param {Object} config The config object
8083  */
8084 Roo.Shadow = function(config){
8085     Roo.apply(this, config);
8086     if(typeof this.mode != "string"){
8087         this.mode = this.defaultMode;
8088     }
8089     var o = this.offset, a = {h: 0};
8090     var rad = Math.floor(this.offset/2);
8091     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8092         case "drop":
8093             a.w = 0;
8094             a.l = a.t = o;
8095             a.t -= 1;
8096             if(Roo.isIE){
8097                 a.l -= this.offset + rad;
8098                 a.t -= this.offset + rad;
8099                 a.w -= rad;
8100                 a.h -= rad;
8101                 a.t += 1;
8102             }
8103         break;
8104         case "sides":
8105             a.w = (o*2);
8106             a.l = -o;
8107             a.t = o-1;
8108             if(Roo.isIE){
8109                 a.l -= (this.offset - rad);
8110                 a.t -= this.offset + rad;
8111                 a.l += 1;
8112                 a.w -= (this.offset - rad)*2;
8113                 a.w -= rad + 1;
8114                 a.h -= 1;
8115             }
8116         break;
8117         case "frame":
8118             a.w = a.h = (o*2);
8119             a.l = a.t = -o;
8120             a.t += 1;
8121             a.h -= 2;
8122             if(Roo.isIE){
8123                 a.l -= (this.offset - rad);
8124                 a.t -= (this.offset - rad);
8125                 a.l += 1;
8126                 a.w -= (this.offset + rad + 1);
8127                 a.h -= (this.offset + rad);
8128                 a.h += 1;
8129             }
8130         break;
8131     };
8132
8133     this.adjusts = a;
8134 };
8135
8136 Roo.Shadow.prototype = {
8137     /**
8138      * @cfg {String} mode
8139      * The shadow display mode.  Supports the following options:<br />
8140      * sides: Shadow displays on both sides and bottom only<br />
8141      * frame: Shadow displays equally on all four sides<br />
8142      * drop: Traditional bottom-right drop shadow (default)
8143      */
8144     /**
8145      * @cfg {String} offset
8146      * The number of pixels to offset the shadow from the element (defaults to 4)
8147      */
8148     offset: 4,
8149
8150     // private
8151     defaultMode: "drop",
8152
8153     /**
8154      * Displays the shadow under the target element
8155      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8156      */
8157     show : function(target){
8158         target = Roo.get(target);
8159         if(!this.el){
8160             this.el = Roo.Shadow.Pool.pull();
8161             if(this.el.dom.nextSibling != target.dom){
8162                 this.el.insertBefore(target);
8163             }
8164         }
8165         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8166         if(Roo.isIE){
8167             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8168         }
8169         this.realign(
8170             target.getLeft(true),
8171             target.getTop(true),
8172             target.getWidth(),
8173             target.getHeight()
8174         );
8175         this.el.dom.style.display = "block";
8176     },
8177
8178     /**
8179      * Returns true if the shadow is visible, else false
8180      */
8181     isVisible : function(){
8182         return this.el ? true : false;  
8183     },
8184
8185     /**
8186      * Direct alignment when values are already available. Show must be called at least once before
8187      * calling this method to ensure it is initialized.
8188      * @param {Number} left The target element left position
8189      * @param {Number} top The target element top position
8190      * @param {Number} width The target element width
8191      * @param {Number} height The target element height
8192      */
8193     realign : function(l, t, w, h){
8194         if(!this.el){
8195             return;
8196         }
8197         var a = this.adjusts, d = this.el.dom, s = d.style;
8198         var iea = 0;
8199         s.left = (l+a.l)+"px";
8200         s.top = (t+a.t)+"px";
8201         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8202  
8203         if(s.width != sws || s.height != shs){
8204             s.width = sws;
8205             s.height = shs;
8206             if(!Roo.isIE){
8207                 var cn = d.childNodes;
8208                 var sww = Math.max(0, (sw-12))+"px";
8209                 cn[0].childNodes[1].style.width = sww;
8210                 cn[1].childNodes[1].style.width = sww;
8211                 cn[2].childNodes[1].style.width = sww;
8212                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8213             }
8214         }
8215     },
8216
8217     /**
8218      * Hides this shadow
8219      */
8220     hide : function(){
8221         if(this.el){
8222             this.el.dom.style.display = "none";
8223             Roo.Shadow.Pool.push(this.el);
8224             delete this.el;
8225         }
8226     },
8227
8228     /**
8229      * Adjust the z-index of this shadow
8230      * @param {Number} zindex The new z-index
8231      */
8232     setZIndex : function(z){
8233         this.zIndex = z;
8234         if(this.el){
8235             this.el.setStyle("z-index", z);
8236         }
8237     }
8238 };
8239
8240 // Private utility class that manages the internal Shadow cache
8241 Roo.Shadow.Pool = function(){
8242     var p = [];
8243     var markup = Roo.isIE ?
8244                  '<div class="x-ie-shadow"></div>' :
8245                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8246     return {
8247         pull : function(){
8248             var sh = p.shift();
8249             if(!sh){
8250                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8251                 sh.autoBoxAdjust = false;
8252             }
8253             return sh;
8254         },
8255
8256         push : function(sh){
8257             p.push(sh);
8258         }
8259     };
8260 }();/*
8261  * Based on:
8262  * Ext JS Library 1.1.1
8263  * Copyright(c) 2006-2007, Ext JS, LLC.
8264  *
8265  * Originally Released Under LGPL - original licence link has changed is not relivant.
8266  *
8267  * Fork - LGPL
8268  * <script type="text/javascript">
8269  */
8270
8271 /**
8272  * @class Roo.BoxComponent
8273  * @extends Roo.Component
8274  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8275  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8276  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8277  * layout containers.
8278  * @constructor
8279  * @param {Roo.Element/String/Object} config The configuration options.
8280  */
8281 Roo.BoxComponent = function(config){
8282     Roo.Component.call(this, config);
8283     this.addEvents({
8284         /**
8285          * @event resize
8286          * Fires after the component is resized.
8287              * @param {Roo.Component} this
8288              * @param {Number} adjWidth The box-adjusted width that was set
8289              * @param {Number} adjHeight The box-adjusted height that was set
8290              * @param {Number} rawWidth The width that was originally specified
8291              * @param {Number} rawHeight The height that was originally specified
8292              */
8293         resize : true,
8294         /**
8295          * @event move
8296          * Fires after the component is moved.
8297              * @param {Roo.Component} this
8298              * @param {Number} x The new x position
8299              * @param {Number} y The new y position
8300              */
8301         move : true
8302     });
8303 };
8304
8305 Roo.extend(Roo.BoxComponent, Roo.Component, {
8306     // private, set in afterRender to signify that the component has been rendered
8307     boxReady : false,
8308     // private, used to defer height settings to subclasses
8309     deferHeight: false,
8310     /** @cfg {Number} width
8311      * width (optional) size of component
8312      */
8313      /** @cfg {Number} height
8314      * height (optional) size of component
8315      */
8316      
8317     /**
8318      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8319      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8320      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8321      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8322      * @return {Roo.BoxComponent} this
8323      */
8324     setSize : function(w, h){
8325         // support for standard size objects
8326         if(typeof w == 'object'){
8327             h = w.height;
8328             w = w.width;
8329         }
8330         // not rendered
8331         if(!this.boxReady){
8332             this.width = w;
8333             this.height = h;
8334             return this;
8335         }
8336
8337         // prevent recalcs when not needed
8338         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8339             return this;
8340         }
8341         this.lastSize = {width: w, height: h};
8342
8343         var adj = this.adjustSize(w, h);
8344         var aw = adj.width, ah = adj.height;
8345         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8346             var rz = this.getResizeEl();
8347             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8348                 rz.setSize(aw, ah);
8349             }else if(!this.deferHeight && ah !== undefined){
8350                 rz.setHeight(ah);
8351             }else if(aw !== undefined){
8352                 rz.setWidth(aw);
8353             }
8354             this.onResize(aw, ah, w, h);
8355             this.fireEvent('resize', this, aw, ah, w, h);
8356         }
8357         return this;
8358     },
8359
8360     /**
8361      * Gets the current size of the component's underlying element.
8362      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8363      */
8364     getSize : function(){
8365         return this.el.getSize();
8366     },
8367
8368     /**
8369      * Gets the current XY position of the component's underlying element.
8370      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8371      * @return {Array} The XY position of the element (e.g., [100, 200])
8372      */
8373     getPosition : function(local){
8374         if(local === true){
8375             return [this.el.getLeft(true), this.el.getTop(true)];
8376         }
8377         return this.xy || this.el.getXY();
8378     },
8379
8380     /**
8381      * Gets the current box measurements of the component's underlying element.
8382      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8383      * @returns {Object} box An object in the format {x, y, width, height}
8384      */
8385     getBox : function(local){
8386         var s = this.el.getSize();
8387         if(local){
8388             s.x = this.el.getLeft(true);
8389             s.y = this.el.getTop(true);
8390         }else{
8391             var xy = this.xy || this.el.getXY();
8392             s.x = xy[0];
8393             s.y = xy[1];
8394         }
8395         return s;
8396     },
8397
8398     /**
8399      * Sets the current box measurements of the component's underlying element.
8400      * @param {Object} box An object in the format {x, y, width, height}
8401      * @returns {Roo.BoxComponent} this
8402      */
8403     updateBox : function(box){
8404         this.setSize(box.width, box.height);
8405         this.setPagePosition(box.x, box.y);
8406         return this;
8407     },
8408
8409     // protected
8410     getResizeEl : function(){
8411         return this.resizeEl || this.el;
8412     },
8413
8414     // protected
8415     getPositionEl : function(){
8416         return this.positionEl || this.el;
8417     },
8418
8419     /**
8420      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8421      * This method fires the move event.
8422      * @param {Number} left The new left
8423      * @param {Number} top The new top
8424      * @returns {Roo.BoxComponent} this
8425      */
8426     setPosition : function(x, y){
8427         this.x = x;
8428         this.y = y;
8429         if(!this.boxReady){
8430             return this;
8431         }
8432         var adj = this.adjustPosition(x, y);
8433         var ax = adj.x, ay = adj.y;
8434
8435         var el = this.getPositionEl();
8436         if(ax !== undefined || ay !== undefined){
8437             if(ax !== undefined && ay !== undefined){
8438                 el.setLeftTop(ax, ay);
8439             }else if(ax !== undefined){
8440                 el.setLeft(ax);
8441             }else if(ay !== undefined){
8442                 el.setTop(ay);
8443             }
8444             this.onPosition(ax, ay);
8445             this.fireEvent('move', this, ax, ay);
8446         }
8447         return this;
8448     },
8449
8450     /**
8451      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8452      * This method fires the move event.
8453      * @param {Number} x The new x position
8454      * @param {Number} y The new y position
8455      * @returns {Roo.BoxComponent} this
8456      */
8457     setPagePosition : function(x, y){
8458         this.pageX = x;
8459         this.pageY = y;
8460         if(!this.boxReady){
8461             return;
8462         }
8463         if(x === undefined || y === undefined){ // cannot translate undefined points
8464             return;
8465         }
8466         var p = this.el.translatePoints(x, y);
8467         this.setPosition(p.left, p.top);
8468         return this;
8469     },
8470
8471     // private
8472     onRender : function(ct, position){
8473         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8474         if(this.resizeEl){
8475             this.resizeEl = Roo.get(this.resizeEl);
8476         }
8477         if(this.positionEl){
8478             this.positionEl = Roo.get(this.positionEl);
8479         }
8480     },
8481
8482     // private
8483     afterRender : function(){
8484         Roo.BoxComponent.superclass.afterRender.call(this);
8485         this.boxReady = true;
8486         this.setSize(this.width, this.height);
8487         if(this.x || this.y){
8488             this.setPosition(this.x, this.y);
8489         }
8490         if(this.pageX || this.pageY){
8491             this.setPagePosition(this.pageX, this.pageY);
8492         }
8493     },
8494
8495     /**
8496      * Force the component's size to recalculate based on the underlying element's current height and width.
8497      * @returns {Roo.BoxComponent} this
8498      */
8499     syncSize : function(){
8500         delete this.lastSize;
8501         this.setSize(this.el.getWidth(), this.el.getHeight());
8502         return this;
8503     },
8504
8505     /**
8506      * Called after the component is resized, this method is empty by default but can be implemented by any
8507      * subclass that needs to perform custom logic after a resize occurs.
8508      * @param {Number} adjWidth The box-adjusted width that was set
8509      * @param {Number} adjHeight The box-adjusted height that was set
8510      * @param {Number} rawWidth The width that was originally specified
8511      * @param {Number} rawHeight The height that was originally specified
8512      */
8513     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8514
8515     },
8516
8517     /**
8518      * Called after the component is moved, this method is empty by default but can be implemented by any
8519      * subclass that needs to perform custom logic after a move occurs.
8520      * @param {Number} x The new x position
8521      * @param {Number} y The new y position
8522      */
8523     onPosition : function(x, y){
8524
8525     },
8526
8527     // private
8528     adjustSize : function(w, h){
8529         if(this.autoWidth){
8530             w = 'auto';
8531         }
8532         if(this.autoHeight){
8533             h = 'auto';
8534         }
8535         return {width : w, height: h};
8536     },
8537
8538     // private
8539     adjustPosition : function(x, y){
8540         return {x : x, y: y};
8541     }
8542 });/*
8543  * Based on:
8544  * Ext JS Library 1.1.1
8545  * Copyright(c) 2006-2007, Ext JS, LLC.
8546  *
8547  * Originally Released Under LGPL - original licence link has changed is not relivant.
8548  *
8549  * Fork - LGPL
8550  * <script type="text/javascript">
8551  */
8552
8553
8554 /**
8555  * @class Roo.SplitBar
8556  * @extends Roo.util.Observable
8557  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8558  * <br><br>
8559  * Usage:
8560  * <pre><code>
8561 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8562                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8563 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8564 split.minSize = 100;
8565 split.maxSize = 600;
8566 split.animate = true;
8567 split.on('moved', splitterMoved);
8568 </code></pre>
8569  * @constructor
8570  * Create a new SplitBar
8571  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8572  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8573  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8574  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8575                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8576                         position of the SplitBar).
8577  */
8578 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8579     
8580     /** @private */
8581     this.el = Roo.get(dragElement, true);
8582     this.el.dom.unselectable = "on";
8583     /** @private */
8584     this.resizingEl = Roo.get(resizingElement, true);
8585
8586     /**
8587      * @private
8588      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8589      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8590      * @type Number
8591      */
8592     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8593     
8594     /**
8595      * The minimum size of the resizing element. (Defaults to 0)
8596      * @type Number
8597      */
8598     this.minSize = 0;
8599     
8600     /**
8601      * The maximum size of the resizing element. (Defaults to 2000)
8602      * @type Number
8603      */
8604     this.maxSize = 2000;
8605     
8606     /**
8607      * Whether to animate the transition to the new size
8608      * @type Boolean
8609      */
8610     this.animate = false;
8611     
8612     /**
8613      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8614      * @type Boolean
8615      */
8616     this.useShim = false;
8617     
8618     /** @private */
8619     this.shim = null;
8620     
8621     if(!existingProxy){
8622         /** @private */
8623         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8624     }else{
8625         this.proxy = Roo.get(existingProxy).dom;
8626     }
8627     /** @private */
8628     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8629     
8630     /** @private */
8631     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8632     
8633     /** @private */
8634     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8635     
8636     /** @private */
8637     this.dragSpecs = {};
8638     
8639     /**
8640      * @private The adapter to use to positon and resize elements
8641      */
8642     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8643     this.adapter.init(this);
8644     
8645     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8646         /** @private */
8647         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8648         this.el.addClass("x-splitbar-h");
8649     }else{
8650         /** @private */
8651         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8652         this.el.addClass("x-splitbar-v");
8653     }
8654     
8655     this.addEvents({
8656         /**
8657          * @event resize
8658          * Fires when the splitter is moved (alias for {@link #event-moved})
8659          * @param {Roo.SplitBar} this
8660          * @param {Number} newSize the new width or height
8661          */
8662         "resize" : true,
8663         /**
8664          * @event moved
8665          * Fires when the splitter is moved
8666          * @param {Roo.SplitBar} this
8667          * @param {Number} newSize the new width or height
8668          */
8669         "moved" : true,
8670         /**
8671          * @event beforeresize
8672          * Fires before the splitter is dragged
8673          * @param {Roo.SplitBar} this
8674          */
8675         "beforeresize" : true,
8676
8677         "beforeapply" : true
8678     });
8679
8680     Roo.util.Observable.call(this);
8681 };
8682
8683 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8684     onStartProxyDrag : function(x, y){
8685         this.fireEvent("beforeresize", this);
8686         if(!this.overlay){
8687             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8688             o.unselectable();
8689             o.enableDisplayMode("block");
8690             // all splitbars share the same overlay
8691             Roo.SplitBar.prototype.overlay = o;
8692         }
8693         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8694         this.overlay.show();
8695         Roo.get(this.proxy).setDisplayed("block");
8696         var size = this.adapter.getElementSize(this);
8697         this.activeMinSize = this.getMinimumSize();;
8698         this.activeMaxSize = this.getMaximumSize();;
8699         var c1 = size - this.activeMinSize;
8700         var c2 = Math.max(this.activeMaxSize - size, 0);
8701         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8702             this.dd.resetConstraints();
8703             this.dd.setXConstraint(
8704                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8705                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8706             );
8707             this.dd.setYConstraint(0, 0);
8708         }else{
8709             this.dd.resetConstraints();
8710             this.dd.setXConstraint(0, 0);
8711             this.dd.setYConstraint(
8712                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8713                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8714             );
8715          }
8716         this.dragSpecs.startSize = size;
8717         this.dragSpecs.startPoint = [x, y];
8718         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8719     },
8720     
8721     /** 
8722      * @private Called after the drag operation by the DDProxy
8723      */
8724     onEndProxyDrag : function(e){
8725         Roo.get(this.proxy).setDisplayed(false);
8726         var endPoint = Roo.lib.Event.getXY(e);
8727         if(this.overlay){
8728             this.overlay.hide();
8729         }
8730         var newSize;
8731         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8732             newSize = this.dragSpecs.startSize + 
8733                 (this.placement == Roo.SplitBar.LEFT ?
8734                     endPoint[0] - this.dragSpecs.startPoint[0] :
8735                     this.dragSpecs.startPoint[0] - endPoint[0]
8736                 );
8737         }else{
8738             newSize = this.dragSpecs.startSize + 
8739                 (this.placement == Roo.SplitBar.TOP ?
8740                     endPoint[1] - this.dragSpecs.startPoint[1] :
8741                     this.dragSpecs.startPoint[1] - endPoint[1]
8742                 );
8743         }
8744         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8745         if(newSize != this.dragSpecs.startSize){
8746             if(this.fireEvent('beforeapply', this, newSize) !== false){
8747                 this.adapter.setElementSize(this, newSize);
8748                 this.fireEvent("moved", this, newSize);
8749                 this.fireEvent("resize", this, newSize);
8750             }
8751         }
8752     },
8753     
8754     /**
8755      * Get the adapter this SplitBar uses
8756      * @return The adapter object
8757      */
8758     getAdapter : function(){
8759         return this.adapter;
8760     },
8761     
8762     /**
8763      * Set the adapter this SplitBar uses
8764      * @param {Object} adapter A SplitBar adapter object
8765      */
8766     setAdapter : function(adapter){
8767         this.adapter = adapter;
8768         this.adapter.init(this);
8769     },
8770     
8771     /**
8772      * Gets the minimum size for the resizing element
8773      * @return {Number} The minimum size
8774      */
8775     getMinimumSize : function(){
8776         return this.minSize;
8777     },
8778     
8779     /**
8780      * Sets the minimum size for the resizing element
8781      * @param {Number} minSize The minimum size
8782      */
8783     setMinimumSize : function(minSize){
8784         this.minSize = minSize;
8785     },
8786     
8787     /**
8788      * Gets the maximum size for the resizing element
8789      * @return {Number} The maximum size
8790      */
8791     getMaximumSize : function(){
8792         return this.maxSize;
8793     },
8794     
8795     /**
8796      * Sets the maximum size for the resizing element
8797      * @param {Number} maxSize The maximum size
8798      */
8799     setMaximumSize : function(maxSize){
8800         this.maxSize = maxSize;
8801     },
8802     
8803     /**
8804      * Sets the initialize size for the resizing element
8805      * @param {Number} size The initial size
8806      */
8807     setCurrentSize : function(size){
8808         var oldAnimate = this.animate;
8809         this.animate = false;
8810         this.adapter.setElementSize(this, size);
8811         this.animate = oldAnimate;
8812     },
8813     
8814     /**
8815      * Destroy this splitbar. 
8816      * @param {Boolean} removeEl True to remove the element
8817      */
8818     destroy : function(removeEl){
8819         if(this.shim){
8820             this.shim.remove();
8821         }
8822         this.dd.unreg();
8823         this.proxy.parentNode.removeChild(this.proxy);
8824         if(removeEl){
8825             this.el.remove();
8826         }
8827     }
8828 });
8829
8830 /**
8831  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8832  */
8833 Roo.SplitBar.createProxy = function(dir){
8834     var proxy = new Roo.Element(document.createElement("div"));
8835     proxy.unselectable();
8836     var cls = 'x-splitbar-proxy';
8837     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8838     document.body.appendChild(proxy.dom);
8839     return proxy.dom;
8840 };
8841
8842 /** 
8843  * @class Roo.SplitBar.BasicLayoutAdapter
8844  * Default Adapter. It assumes the splitter and resizing element are not positioned
8845  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8846  */
8847 Roo.SplitBar.BasicLayoutAdapter = function(){
8848 };
8849
8850 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8851     // do nothing for now
8852     init : function(s){
8853     
8854     },
8855     /**
8856      * Called before drag operations to get the current size of the resizing element. 
8857      * @param {Roo.SplitBar} s The SplitBar using this adapter
8858      */
8859      getElementSize : function(s){
8860         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8861             return s.resizingEl.getWidth();
8862         }else{
8863             return s.resizingEl.getHeight();
8864         }
8865     },
8866     
8867     /**
8868      * Called after drag operations to set the size of the resizing element.
8869      * @param {Roo.SplitBar} s The SplitBar using this adapter
8870      * @param {Number} newSize The new size to set
8871      * @param {Function} onComplete A function to be invoked when resizing is complete
8872      */
8873     setElementSize : function(s, newSize, onComplete){
8874         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8875             if(!s.animate){
8876                 s.resizingEl.setWidth(newSize);
8877                 if(onComplete){
8878                     onComplete(s, newSize);
8879                 }
8880             }else{
8881                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8882             }
8883         }else{
8884             
8885             if(!s.animate){
8886                 s.resizingEl.setHeight(newSize);
8887                 if(onComplete){
8888                     onComplete(s, newSize);
8889                 }
8890             }else{
8891                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8892             }
8893         }
8894     }
8895 };
8896
8897 /** 
8898  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8899  * @extends Roo.SplitBar.BasicLayoutAdapter
8900  * Adapter that  moves the splitter element to align with the resized sizing element. 
8901  * Used with an absolute positioned SplitBar.
8902  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8903  * document.body, make sure you assign an id to the body element.
8904  */
8905 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8906     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8907     this.container = Roo.get(container);
8908 };
8909
8910 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8911     init : function(s){
8912         this.basic.init(s);
8913     },
8914     
8915     getElementSize : function(s){
8916         return this.basic.getElementSize(s);
8917     },
8918     
8919     setElementSize : function(s, newSize, onComplete){
8920         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8921     },
8922     
8923     moveSplitter : function(s){
8924         var yes = Roo.SplitBar;
8925         switch(s.placement){
8926             case yes.LEFT:
8927                 s.el.setX(s.resizingEl.getRight());
8928                 break;
8929             case yes.RIGHT:
8930                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8931                 break;
8932             case yes.TOP:
8933                 s.el.setY(s.resizingEl.getBottom());
8934                 break;
8935             case yes.BOTTOM:
8936                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8937                 break;
8938         }
8939     }
8940 };
8941
8942 /**
8943  * Orientation constant - Create a vertical SplitBar
8944  * @static
8945  * @type Number
8946  */
8947 Roo.SplitBar.VERTICAL = 1;
8948
8949 /**
8950  * Orientation constant - Create a horizontal SplitBar
8951  * @static
8952  * @type Number
8953  */
8954 Roo.SplitBar.HORIZONTAL = 2;
8955
8956 /**
8957  * Placement constant - The resizing element is to the left of the splitter element
8958  * @static
8959  * @type Number
8960  */
8961 Roo.SplitBar.LEFT = 1;
8962
8963 /**
8964  * Placement constant - The resizing element is to the right of the splitter element
8965  * @static
8966  * @type Number
8967  */
8968 Roo.SplitBar.RIGHT = 2;
8969
8970 /**
8971  * Placement constant - The resizing element is positioned above the splitter element
8972  * @static
8973  * @type Number
8974  */
8975 Roo.SplitBar.TOP = 3;
8976
8977 /**
8978  * Placement constant - The resizing element is positioned under splitter element
8979  * @static
8980  * @type Number
8981  */
8982 Roo.SplitBar.BOTTOM = 4;
8983 /*
8984  * Based on:
8985  * Ext JS Library 1.1.1
8986  * Copyright(c) 2006-2007, Ext JS, LLC.
8987  *
8988  * Originally Released Under LGPL - original licence link has changed is not relivant.
8989  *
8990  * Fork - LGPL
8991  * <script type="text/javascript">
8992  */
8993
8994 /**
8995  * @class Roo.View
8996  * @extends Roo.util.Observable
8997  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
8998  * This class also supports single and multi selection modes. <br>
8999  * Create a data model bound view:
9000  <pre><code>
9001  var store = new Roo.data.Store(...);
9002
9003  var view = new Roo.View({
9004     el : "my-element",
9005     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9006  
9007     singleSelect: true,
9008     selectedClass: "ydataview-selected",
9009     store: store
9010  });
9011
9012  // listen for node click?
9013  view.on("click", function(vw, index, node, e){
9014  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9015  });
9016
9017  // load XML data
9018  dataModel.load("foobar.xml");
9019  </code></pre>
9020  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9021  * <br><br>
9022  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9023  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9024  * 
9025  * Note: old style constructor is still suported (container, template, config)
9026  * 
9027  * @constructor
9028  * Create a new View
9029  * @param {Object} config The config object
9030  * 
9031  */
9032 Roo.View = function(config, depreciated_tpl, depreciated_config){
9033     
9034     if (typeof(depreciated_tpl) == 'undefined') {
9035         // new way.. - universal constructor.
9036         Roo.apply(this, config);
9037         this.el  = Roo.get(this.el);
9038     } else {
9039         // old format..
9040         this.el  = Roo.get(config);
9041         this.tpl = depreciated_tpl;
9042         Roo.apply(this, depreciated_config);
9043     }
9044      
9045     
9046     if(typeof(this.tpl) == "string"){
9047         this.tpl = new Roo.Template(this.tpl);
9048     } else {
9049         // support xtype ctors..
9050         this.tpl = new Roo.factory(this.tpl, Roo);
9051     }
9052     
9053     
9054     this.tpl.compile();
9055    
9056
9057      
9058     /** @private */
9059     this.addEvents({
9060     /**
9061      * @event beforeclick
9062      * Fires before a click is processed. Returns false to cancel the default action.
9063      * @param {Roo.View} this
9064      * @param {Number} index The index of the target node
9065      * @param {HTMLElement} node The target node
9066      * @param {Roo.EventObject} e The raw event object
9067      */
9068         "beforeclick" : true,
9069     /**
9070      * @event click
9071      * Fires when a template node is clicked.
9072      * @param {Roo.View} this
9073      * @param {Number} index The index of the target node
9074      * @param {HTMLElement} node The target node
9075      * @param {Roo.EventObject} e The raw event object
9076      */
9077         "click" : true,
9078     /**
9079      * @event dblclick
9080      * Fires when a template node is double clicked.
9081      * @param {Roo.View} this
9082      * @param {Number} index The index of the target node
9083      * @param {HTMLElement} node The target node
9084      * @param {Roo.EventObject} e The raw event object
9085      */
9086         "dblclick" : true,
9087     /**
9088      * @event contextmenu
9089      * Fires when a template node is right clicked.
9090      * @param {Roo.View} this
9091      * @param {Number} index The index of the target node
9092      * @param {HTMLElement} node The target node
9093      * @param {Roo.EventObject} e The raw event object
9094      */
9095         "contextmenu" : true,
9096     /**
9097      * @event selectionchange
9098      * Fires when the selected nodes change.
9099      * @param {Roo.View} this
9100      * @param {Array} selections Array of the selected nodes
9101      */
9102         "selectionchange" : true,
9103
9104     /**
9105      * @event beforeselect
9106      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9107      * @param {Roo.View} this
9108      * @param {HTMLElement} node The node to be selected
9109      * @param {Array} selections Array of currently selected nodes
9110      */
9111         "beforeselect" : true
9112     });
9113
9114     this.el.on({
9115         "click": this.onClick,
9116         "dblclick": this.onDblClick,
9117         "contextmenu": this.onContextMenu,
9118         scope:this
9119     });
9120
9121     this.selections = [];
9122     this.nodes = [];
9123     this.cmp = new Roo.CompositeElementLite([]);
9124     if(this.store){
9125         this.store = Roo.factory(this.store, Roo.data);
9126         this.setStore(this.store, true);
9127     }
9128     Roo.View.superclass.constructor.call(this);
9129 };
9130
9131 Roo.extend(Roo.View, Roo.util.Observable, {
9132     
9133      /**
9134      * @cfg {Roo.data.Store} store Data store to load data from.
9135      */
9136     store : false,
9137     
9138     /**
9139      * @cfg {String|Roo.Element} el The container element.
9140      */
9141     el : '',
9142     
9143     /**
9144      * @cfg {String|Roo.Template} tpl The template used by this View 
9145      */
9146     tpl : false,
9147     
9148     /**
9149      * @cfg {String} selectedClass The css class to add to selected nodes
9150      */
9151     selectedClass : "x-view-selected",
9152      /**
9153      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9154      */
9155     emptyText : "",
9156     /**
9157      * @cfg {Boolean} multiSelect Allow multiple selection
9158      */
9159     
9160     multiSelect : false,
9161     /**
9162      * @cfg {Boolean} singleSelect Allow single selection
9163      */
9164     singleSelect:  false,
9165     
9166     /**
9167      * Returns the element this view is bound to.
9168      * @return {Roo.Element}
9169      */
9170     getEl : function(){
9171         return this.el;
9172     },
9173
9174     /**
9175      * Refreshes the view.
9176      */
9177     refresh : function(){
9178         var t = this.tpl;
9179         this.clearSelections();
9180         this.el.update("");
9181         var html = [];
9182         var records = this.store.getRange();
9183         if(records.length < 1){
9184             this.el.update(this.emptyText);
9185             return;
9186         }
9187         for(var i = 0, len = records.length; i < len; i++){
9188             var data = this.prepareData(records[i].data, i, records[i]);
9189             html[html.length] = t.apply(data);
9190         }
9191         this.el.update(html.join(""));
9192         this.nodes = this.el.dom.childNodes;
9193         this.updateIndexes(0);
9194     },
9195
9196     /**
9197      * Function to override to reformat the data that is sent to
9198      * the template for each node.
9199      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9200      * a JSON object for an UpdateManager bound view).
9201      */
9202     prepareData : function(data){
9203         return data;
9204     },
9205
9206     onUpdate : function(ds, record){
9207         this.clearSelections();
9208         var index = this.store.indexOf(record);
9209         var n = this.nodes[index];
9210         this.tpl.insertBefore(n, this.prepareData(record.data));
9211         n.parentNode.removeChild(n);
9212         this.updateIndexes(index, index);
9213     },
9214
9215     onAdd : function(ds, records, index){
9216         this.clearSelections();
9217         if(this.nodes.length == 0){
9218             this.refresh();
9219             return;
9220         }
9221         var n = this.nodes[index];
9222         for(var i = 0, len = records.length; i < len; i++){
9223             var d = this.prepareData(records[i].data);
9224             if(n){
9225                 this.tpl.insertBefore(n, d);
9226             }else{
9227                 this.tpl.append(this.el, d);
9228             }
9229         }
9230         this.updateIndexes(index);
9231     },
9232
9233     onRemove : function(ds, record, index){
9234         this.clearSelections();
9235         this.el.dom.removeChild(this.nodes[index]);
9236         this.updateIndexes(index);
9237     },
9238
9239     /**
9240      * Refresh an individual node.
9241      * @param {Number} index
9242      */
9243     refreshNode : function(index){
9244         this.onUpdate(this.store, this.store.getAt(index));
9245     },
9246
9247     updateIndexes : function(startIndex, endIndex){
9248         var ns = this.nodes;
9249         startIndex = startIndex || 0;
9250         endIndex = endIndex || ns.length - 1;
9251         for(var i = startIndex; i <= endIndex; i++){
9252             ns[i].nodeIndex = i;
9253         }
9254     },
9255
9256     /**
9257      * Changes the data store this view uses and refresh the view.
9258      * @param {Store} store
9259      */
9260     setStore : function(store, initial){
9261         if(!initial && this.store){
9262             this.store.un("datachanged", this.refresh);
9263             this.store.un("add", this.onAdd);
9264             this.store.un("remove", this.onRemove);
9265             this.store.un("update", this.onUpdate);
9266             this.store.un("clear", this.refresh);
9267         }
9268         if(store){
9269           
9270             store.on("datachanged", this.refresh, this);
9271             store.on("add", this.onAdd, this);
9272             store.on("remove", this.onRemove, this);
9273             store.on("update", this.onUpdate, this);
9274             store.on("clear", this.refresh, this);
9275         }
9276         
9277         if(store){
9278             this.refresh();
9279         }
9280     },
9281
9282     /**
9283      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9284      * @param {HTMLElement} node
9285      * @return {HTMLElement} The template node
9286      */
9287     findItemFromChild : function(node){
9288         var el = this.el.dom;
9289         if(!node || node.parentNode == el){
9290                     return node;
9291             }
9292             var p = node.parentNode;
9293             while(p && p != el){
9294             if(p.parentNode == el){
9295                 return p;
9296             }
9297             p = p.parentNode;
9298         }
9299             return null;
9300     },
9301
9302     /** @ignore */
9303     onClick : function(e){
9304         var item = this.findItemFromChild(e.getTarget());
9305         if(item){
9306             var index = this.indexOf(item);
9307             if(this.onItemClick(item, index, e) !== false){
9308                 this.fireEvent("click", this, index, item, e);
9309             }
9310         }else{
9311             this.clearSelections();
9312         }
9313     },
9314
9315     /** @ignore */
9316     onContextMenu : function(e){
9317         var item = this.findItemFromChild(e.getTarget());
9318         if(item){
9319             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9320         }
9321     },
9322
9323     /** @ignore */
9324     onDblClick : function(e){
9325         var item = this.findItemFromChild(e.getTarget());
9326         if(item){
9327             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9328         }
9329     },
9330
9331     onItemClick : function(item, index, e){
9332         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9333             return false;
9334         }
9335         if(this.multiSelect || this.singleSelect){
9336             if(this.multiSelect && e.shiftKey && this.lastSelection){
9337                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9338             }else{
9339                 this.select(item, this.multiSelect && e.ctrlKey);
9340                 this.lastSelection = item;
9341             }
9342             e.preventDefault();
9343         }
9344         return true;
9345     },
9346
9347     /**
9348      * Get the number of selected nodes.
9349      * @return {Number}
9350      */
9351     getSelectionCount : function(){
9352         return this.selections.length;
9353     },
9354
9355     /**
9356      * Get the currently selected nodes.
9357      * @return {Array} An array of HTMLElements
9358      */
9359     getSelectedNodes : function(){
9360         return this.selections;
9361     },
9362
9363     /**
9364      * Get the indexes of the selected nodes.
9365      * @return {Array}
9366      */
9367     getSelectedIndexes : function(){
9368         var indexes = [], s = this.selections;
9369         for(var i = 0, len = s.length; i < len; i++){
9370             indexes.push(s[i].nodeIndex);
9371         }
9372         return indexes;
9373     },
9374
9375     /**
9376      * Clear all selections
9377      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9378      */
9379     clearSelections : function(suppressEvent){
9380         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9381             this.cmp.elements = this.selections;
9382             this.cmp.removeClass(this.selectedClass);
9383             this.selections = [];
9384             if(!suppressEvent){
9385                 this.fireEvent("selectionchange", this, this.selections);
9386             }
9387         }
9388     },
9389
9390     /**
9391      * Returns true if the passed node is selected
9392      * @param {HTMLElement/Number} node The node or node index
9393      * @return {Boolean}
9394      */
9395     isSelected : function(node){
9396         var s = this.selections;
9397         if(s.length < 1){
9398             return false;
9399         }
9400         node = this.getNode(node);
9401         return s.indexOf(node) !== -1;
9402     },
9403
9404     /**
9405      * Selects nodes.
9406      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9407      * @param {Boolean} keepExisting (optional) true to keep existing selections
9408      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9409      */
9410     select : function(nodeInfo, keepExisting, suppressEvent){
9411         if(nodeInfo instanceof Array){
9412             if(!keepExisting){
9413                 this.clearSelections(true);
9414             }
9415             for(var i = 0, len = nodeInfo.length; i < len; i++){
9416                 this.select(nodeInfo[i], true, true);
9417             }
9418         } else{
9419             var node = this.getNode(nodeInfo);
9420             if(node && !this.isSelected(node)){
9421                 if(!keepExisting){
9422                     this.clearSelections(true);
9423                 }
9424                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9425                     Roo.fly(node).addClass(this.selectedClass);
9426                     this.selections.push(node);
9427                     if(!suppressEvent){
9428                         this.fireEvent("selectionchange", this, this.selections);
9429                     }
9430                 }
9431             }
9432         }
9433     },
9434
9435     /**
9436      * Gets a template node.
9437      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9438      * @return {HTMLElement} The node or null if it wasn't found
9439      */
9440     getNode : function(nodeInfo){
9441         if(typeof nodeInfo == "string"){
9442             return document.getElementById(nodeInfo);
9443         }else if(typeof nodeInfo == "number"){
9444             return this.nodes[nodeInfo];
9445         }
9446         return nodeInfo;
9447     },
9448
9449     /**
9450      * Gets a range template nodes.
9451      * @param {Number} startIndex
9452      * @param {Number} endIndex
9453      * @return {Array} An array of nodes
9454      */
9455     getNodes : function(start, end){
9456         var ns = this.nodes;
9457         start = start || 0;
9458         end = typeof end == "undefined" ? ns.length - 1 : end;
9459         var nodes = [];
9460         if(start <= end){
9461             for(var i = start; i <= end; i++){
9462                 nodes.push(ns[i]);
9463             }
9464         } else{
9465             for(var i = start; i >= end; i--){
9466                 nodes.push(ns[i]);
9467             }
9468         }
9469         return nodes;
9470     },
9471
9472     /**
9473      * Finds the index of the passed node
9474      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9475      * @return {Number} The index of the node or -1
9476      */
9477     indexOf : function(node){
9478         node = this.getNode(node);
9479         if(typeof node.nodeIndex == "number"){
9480             return node.nodeIndex;
9481         }
9482         var ns = this.nodes;
9483         for(var i = 0, len = ns.length; i < len; i++){
9484             if(ns[i] == node){
9485                 return i;
9486             }
9487         }
9488         return -1;
9489     }
9490 });
9491 /*
9492  * Based on:
9493  * Ext JS Library 1.1.1
9494  * Copyright(c) 2006-2007, Ext JS, LLC.
9495  *
9496  * Originally Released Under LGPL - original licence link has changed is not relivant.
9497  *
9498  * Fork - LGPL
9499  * <script type="text/javascript">
9500  */
9501
9502 /**
9503  * @class Roo.JsonView
9504  * @extends Roo.View
9505  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9506 <pre><code>
9507 var view = new Roo.JsonView({
9508     container: "my-element",
9509     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9510     multiSelect: true, 
9511     jsonRoot: "data" 
9512 });
9513
9514 // listen for node click?
9515 view.on("click", function(vw, index, node, e){
9516     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9517 });
9518
9519 // direct load of JSON data
9520 view.load("foobar.php");
9521
9522 // Example from my blog list
9523 var tpl = new Roo.Template(
9524     '&lt;div class="entry"&gt;' +
9525     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9526     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9527     "&lt;/div&gt;&lt;hr /&gt;"
9528 );
9529
9530 var moreView = new Roo.JsonView({
9531     container :  "entry-list", 
9532     template : tpl,
9533     jsonRoot: "posts"
9534 });
9535 moreView.on("beforerender", this.sortEntries, this);
9536 moreView.load({
9537     url: "/blog/get-posts.php",
9538     params: "allposts=true",
9539     text: "Loading Blog Entries..."
9540 });
9541 </code></pre>
9542
9543 * Note: old code is supported with arguments : (container, template, config)
9544
9545
9546  * @constructor
9547  * Create a new JsonView
9548  * 
9549  * @param {Object} config The config object
9550  * 
9551  */
9552 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9553     
9554     
9555     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9556
9557     var um = this.el.getUpdateManager();
9558     um.setRenderer(this);
9559     um.on("update", this.onLoad, this);
9560     um.on("failure", this.onLoadException, this);
9561
9562     /**
9563      * @event beforerender
9564      * Fires before rendering of the downloaded JSON data.
9565      * @param {Roo.JsonView} this
9566      * @param {Object} data The JSON data loaded
9567      */
9568     /**
9569      * @event load
9570      * Fires when data is loaded.
9571      * @param {Roo.JsonView} this
9572      * @param {Object} data The JSON data loaded
9573      * @param {Object} response The raw Connect response object
9574      */
9575     /**
9576      * @event loadexception
9577      * Fires when loading fails.
9578      * @param {Roo.JsonView} this
9579      * @param {Object} response The raw Connect response object
9580      */
9581     this.addEvents({
9582         'beforerender' : true,
9583         'load' : true,
9584         'loadexception' : true
9585     });
9586 };
9587 Roo.extend(Roo.JsonView, Roo.View, {
9588     /**
9589      * @type {String} The root property in the loaded JSON object that contains the data
9590      */
9591     jsonRoot : "",
9592
9593     /**
9594      * Refreshes the view.
9595      */
9596     refresh : function(){
9597         this.clearSelections();
9598         this.el.update("");
9599         var html = [];
9600         var o = this.jsonData;
9601         if(o && o.length > 0){
9602             for(var i = 0, len = o.length; i < len; i++){
9603                 var data = this.prepareData(o[i], i, o);
9604                 html[html.length] = this.tpl.apply(data);
9605             }
9606         }else{
9607             html.push(this.emptyText);
9608         }
9609         this.el.update(html.join(""));
9610         this.nodes = this.el.dom.childNodes;
9611         this.updateIndexes(0);
9612     },
9613
9614     /**
9615      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9616      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9617      <pre><code>
9618      view.load({
9619          url: "your-url.php",
9620          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9621          callback: yourFunction,
9622          scope: yourObject, //(optional scope)
9623          discardUrl: false,
9624          nocache: false,
9625          text: "Loading...",
9626          timeout: 30,
9627          scripts: false
9628      });
9629      </code></pre>
9630      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9631      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9632      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9633      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9634      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9635      */
9636     load : function(){
9637         var um = this.el.getUpdateManager();
9638         um.update.apply(um, arguments);
9639     },
9640
9641     render : function(el, response){
9642         this.clearSelections();
9643         this.el.update("");
9644         var o;
9645         try{
9646             o = Roo.util.JSON.decode(response.responseText);
9647             if(this.jsonRoot){
9648                 
9649                 o = o[this.jsonRoot];
9650             }
9651         } catch(e){
9652         }
9653         /**
9654          * The current JSON data or null
9655          */
9656         this.jsonData = o;
9657         this.beforeRender();
9658         this.refresh();
9659     },
9660
9661 /**
9662  * Get the number of records in the current JSON dataset
9663  * @return {Number}
9664  */
9665     getCount : function(){
9666         return this.jsonData ? this.jsonData.length : 0;
9667     },
9668
9669 /**
9670  * Returns the JSON object for the specified node(s)
9671  * @param {HTMLElement/Array} node The node or an array of nodes
9672  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9673  * you get the JSON object for the node
9674  */
9675     getNodeData : function(node){
9676         if(node instanceof Array){
9677             var data = [];
9678             for(var i = 0, len = node.length; i < len; i++){
9679                 data.push(this.getNodeData(node[i]));
9680             }
9681             return data;
9682         }
9683         return this.jsonData[this.indexOf(node)] || null;
9684     },
9685
9686     beforeRender : function(){
9687         this.snapshot = this.jsonData;
9688         if(this.sortInfo){
9689             this.sort.apply(this, this.sortInfo);
9690         }
9691         this.fireEvent("beforerender", this, this.jsonData);
9692     },
9693
9694     onLoad : function(el, o){
9695         this.fireEvent("load", this, this.jsonData, o);
9696     },
9697
9698     onLoadException : function(el, o){
9699         this.fireEvent("loadexception", this, o);
9700     },
9701
9702 /**
9703  * Filter the data by a specific property.
9704  * @param {String} property A property on your JSON objects
9705  * @param {String/RegExp} value Either string that the property values
9706  * should start with, or a RegExp to test against the property
9707  */
9708     filter : function(property, value){
9709         if(this.jsonData){
9710             var data = [];
9711             var ss = this.snapshot;
9712             if(typeof value == "string"){
9713                 var vlen = value.length;
9714                 if(vlen == 0){
9715                     this.clearFilter();
9716                     return;
9717                 }
9718                 value = value.toLowerCase();
9719                 for(var i = 0, len = ss.length; i < len; i++){
9720                     var o = ss[i];
9721                     if(o[property].substr(0, vlen).toLowerCase() == value){
9722                         data.push(o);
9723                     }
9724                 }
9725             } else if(value.exec){ // regex?
9726                 for(var i = 0, len = ss.length; i < len; i++){
9727                     var o = ss[i];
9728                     if(value.test(o[property])){
9729                         data.push(o);
9730                     }
9731                 }
9732             } else{
9733                 return;
9734             }
9735             this.jsonData = data;
9736             this.refresh();
9737         }
9738     },
9739
9740 /**
9741  * Filter by a function. The passed function will be called with each
9742  * object in the current dataset. If the function returns true the value is kept,
9743  * otherwise it is filtered.
9744  * @param {Function} fn
9745  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9746  */
9747     filterBy : function(fn, scope){
9748         if(this.jsonData){
9749             var data = [];
9750             var ss = this.snapshot;
9751             for(var i = 0, len = ss.length; i < len; i++){
9752                 var o = ss[i];
9753                 if(fn.call(scope || this, o)){
9754                     data.push(o);
9755                 }
9756             }
9757             this.jsonData = data;
9758             this.refresh();
9759         }
9760     },
9761
9762 /**
9763  * Clears the current filter.
9764  */
9765     clearFilter : function(){
9766         if(this.snapshot && this.jsonData != this.snapshot){
9767             this.jsonData = this.snapshot;
9768             this.refresh();
9769         }
9770     },
9771
9772
9773 /**
9774  * Sorts the data for this view and refreshes it.
9775  * @param {String} property A property on your JSON objects to sort on
9776  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9777  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9778  */
9779     sort : function(property, dir, sortType){
9780         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9781         if(this.jsonData){
9782             var p = property;
9783             var dsc = dir && dir.toLowerCase() == "desc";
9784             var f = function(o1, o2){
9785                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9786                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9787                 ;
9788                 if(v1 < v2){
9789                     return dsc ? +1 : -1;
9790                 } else if(v1 > v2){
9791                     return dsc ? -1 : +1;
9792                 } else{
9793                     return 0;
9794                 }
9795             };
9796             this.jsonData.sort(f);
9797             this.refresh();
9798             if(this.jsonData != this.snapshot){
9799                 this.snapshot.sort(f);
9800             }
9801         }
9802     }
9803 });/*
9804  * Based on:
9805  * Ext JS Library 1.1.1
9806  * Copyright(c) 2006-2007, Ext JS, LLC.
9807  *
9808  * Originally Released Under LGPL - original licence link has changed is not relivant.
9809  *
9810  * Fork - LGPL
9811  * <script type="text/javascript">
9812  */
9813  
9814
9815 /**
9816  * @class Roo.ColorPalette
9817  * @extends Roo.Component
9818  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9819  * Here's an example of typical usage:
9820  * <pre><code>
9821 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9822 cp.render('my-div');
9823
9824 cp.on('select', function(palette, selColor){
9825     // do something with selColor
9826 });
9827 </code></pre>
9828  * @constructor
9829  * Create a new ColorPalette
9830  * @param {Object} config The config object
9831  */
9832 Roo.ColorPalette = function(config){
9833     Roo.ColorPalette.superclass.constructor.call(this, config);
9834     this.addEvents({
9835         /**
9836              * @event select
9837              * Fires when a color is selected
9838              * @param {ColorPalette} this
9839              * @param {String} color The 6-digit color hex code (without the # symbol)
9840              */
9841         select: true
9842     });
9843
9844     if(this.handler){
9845         this.on("select", this.handler, this.scope, true);
9846     }
9847 };
9848 Roo.extend(Roo.ColorPalette, Roo.Component, {
9849     /**
9850      * @cfg {String} itemCls
9851      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9852      */
9853     itemCls : "x-color-palette",
9854     /**
9855      * @cfg {String} value
9856      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9857      * the hex codes are case-sensitive.
9858      */
9859     value : null,
9860     clickEvent:'click',
9861     // private
9862     ctype: "Roo.ColorPalette",
9863
9864     /**
9865      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9866      */
9867     allowReselect : false,
9868
9869     /**
9870      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9871      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9872      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9873      * of colors with the width setting until the box is symmetrical.</p>
9874      * <p>You can override individual colors if needed:</p>
9875      * <pre><code>
9876 var cp = new Roo.ColorPalette();
9877 cp.colors[0] = "FF0000";  // change the first box to red
9878 </code></pre>
9879
9880 Or you can provide a custom array of your own for complete control:
9881 <pre><code>
9882 var cp = new Roo.ColorPalette();
9883 cp.colors = ["000000", "993300", "333300"];
9884 </code></pre>
9885      * @type Array
9886      */
9887     colors : [
9888         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9889         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9890         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9891         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9892         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9893     ],
9894
9895     // private
9896     onRender : function(container, position){
9897         var t = new Roo.MasterTemplate(
9898             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9899         );
9900         var c = this.colors;
9901         for(var i = 0, len = c.length; i < len; i++){
9902             t.add([c[i]]);
9903         }
9904         var el = document.createElement("div");
9905         el.className = this.itemCls;
9906         t.overwrite(el);
9907         container.dom.insertBefore(el, position);
9908         this.el = Roo.get(el);
9909         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9910         if(this.clickEvent != 'click'){
9911             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9912         }
9913     },
9914
9915     // private
9916     afterRender : function(){
9917         Roo.ColorPalette.superclass.afterRender.call(this);
9918         if(this.value){
9919             var s = this.value;
9920             this.value = null;
9921             this.select(s);
9922         }
9923     },
9924
9925     // private
9926     handleClick : function(e, t){
9927         e.preventDefault();
9928         if(!this.disabled){
9929             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9930             this.select(c.toUpperCase());
9931         }
9932     },
9933
9934     /**
9935      * Selects the specified color in the palette (fires the select event)
9936      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9937      */
9938     select : function(color){
9939         color = color.replace("#", "");
9940         if(color != this.value || this.allowReselect){
9941             var el = this.el;
9942             if(this.value){
9943                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
9944             }
9945             el.child("a.color-"+color).addClass("x-color-palette-sel");
9946             this.value = color;
9947             this.fireEvent("select", this, color);
9948         }
9949     }
9950 });/*
9951  * Based on:
9952  * Ext JS Library 1.1.1
9953  * Copyright(c) 2006-2007, Ext JS, LLC.
9954  *
9955  * Originally Released Under LGPL - original licence link has changed is not relivant.
9956  *
9957  * Fork - LGPL
9958  * <script type="text/javascript">
9959  */
9960  
9961 /**
9962  * @class Roo.DatePicker
9963  * @extends Roo.Component
9964  * Simple date picker class.
9965  * @constructor
9966  * Create a new DatePicker
9967  * @param {Object} config The config object
9968  */
9969 Roo.DatePicker = function(config){
9970     Roo.DatePicker.superclass.constructor.call(this, config);
9971
9972     this.value = config && config.value ?
9973                  config.value.clearTime() : new Date().clearTime();
9974
9975     this.addEvents({
9976         /**
9977              * @event select
9978              * Fires when a date is selected
9979              * @param {DatePicker} this
9980              * @param {Date} date The selected date
9981              */
9982         select: true
9983     });
9984
9985     if(this.handler){
9986         this.on("select", this.handler,  this.scope || this);
9987     }
9988     // build the disabledDatesRE
9989     if(!this.disabledDatesRE && this.disabledDates){
9990         var dd = this.disabledDates;
9991         var re = "(?:";
9992         for(var i = 0; i < dd.length; i++){
9993             re += dd[i];
9994             if(i != dd.length-1) re += "|";
9995         }
9996         this.disabledDatesRE = new RegExp(re + ")");
9997     }
9998 };
9999
10000 Roo.extend(Roo.DatePicker, Roo.Component, {
10001     /**
10002      * @cfg {String} todayText
10003      * The text to display on the button that selects the current date (defaults to "Today")
10004      */
10005     todayText : "Today",
10006     /**
10007      * @cfg {String} okText
10008      * The text to display on the ok button
10009      */
10010     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10011     /**
10012      * @cfg {String} cancelText
10013      * The text to display on the cancel button
10014      */
10015     cancelText : "Cancel",
10016     /**
10017      * @cfg {String} todayTip
10018      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10019      */
10020     todayTip : "{0} (Spacebar)",
10021     /**
10022      * @cfg {Date} minDate
10023      * Minimum allowable date (JavaScript date object, defaults to null)
10024      */
10025     minDate : null,
10026     /**
10027      * @cfg {Date} maxDate
10028      * Maximum allowable date (JavaScript date object, defaults to null)
10029      */
10030     maxDate : null,
10031     /**
10032      * @cfg {String} minText
10033      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10034      */
10035     minText : "This date is before the minimum date",
10036     /**
10037      * @cfg {String} maxText
10038      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10039      */
10040     maxText : "This date is after the maximum date",
10041     /**
10042      * @cfg {String} format
10043      * The default date format string which can be overriden for localization support.  The format must be
10044      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10045      */
10046     format : "m/d/y",
10047     /**
10048      * @cfg {Array} disabledDays
10049      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10050      */
10051     disabledDays : null,
10052     /**
10053      * @cfg {String} disabledDaysText
10054      * The tooltip to display when the date falls on a disabled day (defaults to "")
10055      */
10056     disabledDaysText : "",
10057     /**
10058      * @cfg {RegExp} disabledDatesRE
10059      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10060      */
10061     disabledDatesRE : null,
10062     /**
10063      * @cfg {String} disabledDatesText
10064      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10065      */
10066     disabledDatesText : "",
10067     /**
10068      * @cfg {Boolean} constrainToViewport
10069      * True to constrain the date picker to the viewport (defaults to true)
10070      */
10071     constrainToViewport : true,
10072     /**
10073      * @cfg {Array} monthNames
10074      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10075      */
10076     monthNames : Date.monthNames,
10077     /**
10078      * @cfg {Array} dayNames
10079      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10080      */
10081     dayNames : Date.dayNames,
10082     /**
10083      * @cfg {String} nextText
10084      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10085      */
10086     nextText: 'Next Month (Control+Right)',
10087     /**
10088      * @cfg {String} prevText
10089      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10090      */
10091     prevText: 'Previous Month (Control+Left)',
10092     /**
10093      * @cfg {String} monthYearText
10094      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10095      */
10096     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10097     /**
10098      * @cfg {Number} startDay
10099      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10100      */
10101     startDay : 0,
10102     /**
10103      * @cfg {Bool} showClear
10104      * Show a clear button (usefull for date form elements that can be blank.)
10105      */
10106     
10107     showClear: false,
10108     
10109     /**
10110      * Sets the value of the date field
10111      * @param {Date} value The date to set
10112      */
10113     setValue : function(value){
10114         var old = this.value;
10115         this.value = value.clearTime(true);
10116         if(this.el){
10117             this.update(this.value);
10118         }
10119     },
10120
10121     /**
10122      * Gets the current selected value of the date field
10123      * @return {Date} The selected date
10124      */
10125     getValue : function(){
10126         return this.value;
10127     },
10128
10129     // private
10130     focus : function(){
10131         if(this.el){
10132             this.update(this.activeDate);
10133         }
10134     },
10135
10136     // private
10137     onRender : function(container, position){
10138         var m = [
10139              '<table cellspacing="0">',
10140                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
10141                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10142         var dn = this.dayNames;
10143         for(var i = 0; i < 7; i++){
10144             var d = this.startDay+i;
10145             if(d > 6){
10146                 d = d-7;
10147             }
10148             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10149         }
10150         m[m.length] = "</tr></thead><tbody><tr>";
10151         for(var i = 0; i < 42; i++) {
10152             if(i % 7 == 0 && i != 0){
10153                 m[m.length] = "</tr><tr>";
10154             }
10155             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10156         }
10157         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10158             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10159
10160         var el = document.createElement("div");
10161         el.className = "x-date-picker";
10162         el.innerHTML = m.join("");
10163
10164         container.dom.insertBefore(el, position);
10165
10166         this.el = Roo.get(el);
10167         this.eventEl = Roo.get(el.firstChild);
10168
10169         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10170             handler: this.showPrevMonth,
10171             scope: this,
10172             preventDefault:true,
10173             stopDefault:true
10174         });
10175
10176         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10177             handler: this.showNextMonth,
10178             scope: this,
10179             preventDefault:true,
10180             stopDefault:true
10181         });
10182
10183         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10184
10185         this.monthPicker = this.el.down('div.x-date-mp');
10186         this.monthPicker.enableDisplayMode('block');
10187         
10188         var kn = new Roo.KeyNav(this.eventEl, {
10189             "left" : function(e){
10190                 e.ctrlKey ?
10191                     this.showPrevMonth() :
10192                     this.update(this.activeDate.add("d", -1));
10193             },
10194
10195             "right" : function(e){
10196                 e.ctrlKey ?
10197                     this.showNextMonth() :
10198                     this.update(this.activeDate.add("d", 1));
10199             },
10200
10201             "up" : function(e){
10202                 e.ctrlKey ?
10203                     this.showNextYear() :
10204                     this.update(this.activeDate.add("d", -7));
10205             },
10206
10207             "down" : function(e){
10208                 e.ctrlKey ?
10209                     this.showPrevYear() :
10210                     this.update(this.activeDate.add("d", 7));
10211             },
10212
10213             "pageUp" : function(e){
10214                 this.showNextMonth();
10215             },
10216
10217             "pageDown" : function(e){
10218                 this.showPrevMonth();
10219             },
10220
10221             "enter" : function(e){
10222                 e.stopPropagation();
10223                 return true;
10224             },
10225
10226             scope : this
10227         });
10228
10229         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10230
10231         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10232
10233         this.el.unselectable();
10234         
10235         this.cells = this.el.select("table.x-date-inner tbody td");
10236         this.textNodes = this.el.query("table.x-date-inner tbody span");
10237
10238         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10239             text: "&#160;",
10240             tooltip: this.monthYearText
10241         });
10242
10243         this.mbtn.on('click', this.showMonthPicker, this);
10244         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10245
10246
10247         var today = (new Date()).dateFormat(this.format);
10248         
10249         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10250         if (this.showClear) {
10251             baseTb.add( new Roo.Toolbar.Fill());
10252         }
10253         baseTb.add({
10254             text: String.format(this.todayText, today),
10255             tooltip: String.format(this.todayTip, today),
10256             handler: this.selectToday,
10257             scope: this
10258         });
10259         
10260         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10261             
10262         //});
10263         if (this.showClear) {
10264             
10265             baseTb.add( new Roo.Toolbar.Fill());
10266             baseTb.add({
10267                 text: '&#160;',
10268                 cls: 'x-btn-icon x-btn-clear',
10269                 handler: function() {
10270                     //this.value = '';
10271                     this.fireEvent("select", this, '');
10272                 },
10273                 scope: this
10274             });
10275         }
10276         
10277         
10278         if(Roo.isIE){
10279             this.el.repaint();
10280         }
10281         this.update(this.value);
10282     },
10283
10284     createMonthPicker : function(){
10285         if(!this.monthPicker.dom.firstChild){
10286             var buf = ['<table border="0" cellspacing="0">'];
10287             for(var i = 0; i < 6; i++){
10288                 buf.push(
10289                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10290                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10291                     i == 0 ?
10292                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
10293                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10294                 );
10295             }
10296             buf.push(
10297                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10298                     this.okText,
10299                     '</button><button type="button" class="x-date-mp-cancel">',
10300                     this.cancelText,
10301                     '</button></td></tr>',
10302                 '</table>'
10303             );
10304             this.monthPicker.update(buf.join(''));
10305             this.monthPicker.on('click', this.onMonthClick, this);
10306             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10307
10308             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10309             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10310
10311             this.mpMonths.each(function(m, a, i){
10312                 i += 1;
10313                 if((i%2) == 0){
10314                     m.dom.xmonth = 5 + Math.round(i * .5);
10315                 }else{
10316                     m.dom.xmonth = Math.round((i-1) * .5);
10317                 }
10318             });
10319         }
10320     },
10321
10322     showMonthPicker : function(){
10323         this.createMonthPicker();
10324         var size = this.el.getSize();
10325         this.monthPicker.setSize(size);
10326         this.monthPicker.child('table').setSize(size);
10327
10328         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10329         this.updateMPMonth(this.mpSelMonth);
10330         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10331         this.updateMPYear(this.mpSelYear);
10332
10333         this.monthPicker.slideIn('t', {duration:.2});
10334     },
10335
10336     updateMPYear : function(y){
10337         this.mpyear = y;
10338         var ys = this.mpYears.elements;
10339         for(var i = 1; i <= 10; i++){
10340             var td = ys[i-1], y2;
10341             if((i%2) == 0){
10342                 y2 = y + Math.round(i * .5);
10343                 td.firstChild.innerHTML = y2;
10344                 td.xyear = y2;
10345             }else{
10346                 y2 = y - (5-Math.round(i * .5));
10347                 td.firstChild.innerHTML = y2;
10348                 td.xyear = y2;
10349             }
10350             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10351         }
10352     },
10353
10354     updateMPMonth : function(sm){
10355         this.mpMonths.each(function(m, a, i){
10356             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10357         });
10358     },
10359
10360     selectMPMonth: function(m){
10361         
10362     },
10363
10364     onMonthClick : function(e, t){
10365         e.stopEvent();
10366         var el = new Roo.Element(t), pn;
10367         if(el.is('button.x-date-mp-cancel')){
10368             this.hideMonthPicker();
10369         }
10370         else if(el.is('button.x-date-mp-ok')){
10371             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10372             this.hideMonthPicker();
10373         }
10374         else if(pn = el.up('td.x-date-mp-month', 2)){
10375             this.mpMonths.removeClass('x-date-mp-sel');
10376             pn.addClass('x-date-mp-sel');
10377             this.mpSelMonth = pn.dom.xmonth;
10378         }
10379         else if(pn = el.up('td.x-date-mp-year', 2)){
10380             this.mpYears.removeClass('x-date-mp-sel');
10381             pn.addClass('x-date-mp-sel');
10382             this.mpSelYear = pn.dom.xyear;
10383         }
10384         else if(el.is('a.x-date-mp-prev')){
10385             this.updateMPYear(this.mpyear-10);
10386         }
10387         else if(el.is('a.x-date-mp-next')){
10388             this.updateMPYear(this.mpyear+10);
10389         }
10390     },
10391
10392     onMonthDblClick : function(e, t){
10393         e.stopEvent();
10394         var el = new Roo.Element(t), pn;
10395         if(pn = el.up('td.x-date-mp-month', 2)){
10396             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10397             this.hideMonthPicker();
10398         }
10399         else if(pn = el.up('td.x-date-mp-year', 2)){
10400             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10401             this.hideMonthPicker();
10402         }
10403     },
10404
10405     hideMonthPicker : function(disableAnim){
10406         if(this.monthPicker){
10407             if(disableAnim === true){
10408                 this.monthPicker.hide();
10409             }else{
10410                 this.monthPicker.slideOut('t', {duration:.2});
10411             }
10412         }
10413     },
10414
10415     // private
10416     showPrevMonth : function(e){
10417         this.update(this.activeDate.add("mo", -1));
10418     },
10419
10420     // private
10421     showNextMonth : function(e){
10422         this.update(this.activeDate.add("mo", 1));
10423     },
10424
10425     // private
10426     showPrevYear : function(){
10427         this.update(this.activeDate.add("y", -1));
10428     },
10429
10430     // private
10431     showNextYear : function(){
10432         this.update(this.activeDate.add("y", 1));
10433     },
10434
10435     // private
10436     handleMouseWheel : function(e){
10437         var delta = e.getWheelDelta();
10438         if(delta > 0){
10439             this.showPrevMonth();
10440             e.stopEvent();
10441         } else if(delta < 0){
10442             this.showNextMonth();
10443             e.stopEvent();
10444         }
10445     },
10446
10447     // private
10448     handleDateClick : function(e, t){
10449         e.stopEvent();
10450         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10451             this.setValue(new Date(t.dateValue));
10452             this.fireEvent("select", this, this.value);
10453         }
10454     },
10455
10456     // private
10457     selectToday : function(){
10458         this.setValue(new Date().clearTime());
10459         this.fireEvent("select", this, this.value);
10460     },
10461
10462     // private
10463     update : function(date){
10464         var vd = this.activeDate;
10465         this.activeDate = date;
10466         if(vd && this.el){
10467             var t = date.getTime();
10468             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10469                 this.cells.removeClass("x-date-selected");
10470                 this.cells.each(function(c){
10471                    if(c.dom.firstChild.dateValue == t){
10472                        c.addClass("x-date-selected");
10473                        setTimeout(function(){
10474                             try{c.dom.firstChild.focus();}catch(e){}
10475                        }, 50);
10476                        return false;
10477                    }
10478                 });
10479                 return;
10480             }
10481         }
10482         var days = date.getDaysInMonth();
10483         var firstOfMonth = date.getFirstDateOfMonth();
10484         var startingPos = firstOfMonth.getDay()-this.startDay;
10485
10486         if(startingPos <= this.startDay){
10487             startingPos += 7;
10488         }
10489
10490         var pm = date.add("mo", -1);
10491         var prevStart = pm.getDaysInMonth()-startingPos;
10492
10493         var cells = this.cells.elements;
10494         var textEls = this.textNodes;
10495         days += startingPos;
10496
10497         // convert everything to numbers so it's fast
10498         var day = 86400000;
10499         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10500         var today = new Date().clearTime().getTime();
10501         var sel = date.clearTime().getTime();
10502         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10503         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10504         var ddMatch = this.disabledDatesRE;
10505         var ddText = this.disabledDatesText;
10506         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10507         var ddaysText = this.disabledDaysText;
10508         var format = this.format;
10509
10510         var setCellClass = function(cal, cell){
10511             cell.title = "";
10512             var t = d.getTime();
10513             cell.firstChild.dateValue = t;
10514             if(t == today){
10515                 cell.className += " x-date-today";
10516                 cell.title = cal.todayText;
10517             }
10518             if(t == sel){
10519                 cell.className += " x-date-selected";
10520                 setTimeout(function(){
10521                     try{cell.firstChild.focus();}catch(e){}
10522                 }, 50);
10523             }
10524             // disabling
10525             if(t < min) {
10526                 cell.className = " x-date-disabled";
10527                 cell.title = cal.minText;
10528                 return;
10529             }
10530             if(t > max) {
10531                 cell.className = " x-date-disabled";
10532                 cell.title = cal.maxText;
10533                 return;
10534             }
10535             if(ddays){
10536                 if(ddays.indexOf(d.getDay()) != -1){
10537                     cell.title = ddaysText;
10538                     cell.className = " x-date-disabled";
10539                 }
10540             }
10541             if(ddMatch && format){
10542                 var fvalue = d.dateFormat(format);
10543                 if(ddMatch.test(fvalue)){
10544                     cell.title = ddText.replace("%0", fvalue);
10545                     cell.className = " x-date-disabled";
10546                 }
10547             }
10548         };
10549
10550         var i = 0;
10551         for(; i < startingPos; i++) {
10552             textEls[i].innerHTML = (++prevStart);
10553             d.setDate(d.getDate()+1);
10554             cells[i].className = "x-date-prevday";
10555             setCellClass(this, cells[i]);
10556         }
10557         for(; i < days; i++){
10558             intDay = i - startingPos + 1;
10559             textEls[i].innerHTML = (intDay);
10560             d.setDate(d.getDate()+1);
10561             cells[i].className = "x-date-active";
10562             setCellClass(this, cells[i]);
10563         }
10564         var extraDays = 0;
10565         for(; i < 42; i++) {
10566              textEls[i].innerHTML = (++extraDays);
10567              d.setDate(d.getDate()+1);
10568              cells[i].className = "x-date-nextday";
10569              setCellClass(this, cells[i]);
10570         }
10571
10572         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10573
10574         if(!this.internalRender){
10575             var main = this.el.dom.firstChild;
10576             var w = main.offsetWidth;
10577             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10578             Roo.fly(main).setWidth(w);
10579             this.internalRender = true;
10580             // opera does not respect the auto grow header center column
10581             // then, after it gets a width opera refuses to recalculate
10582             // without a second pass
10583             if(Roo.isOpera && !this.secondPass){
10584                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10585                 this.secondPass = true;
10586                 this.update.defer(10, this, [date]);
10587             }
10588         }
10589     }
10590 });/*
10591  * Based on:
10592  * Ext JS Library 1.1.1
10593  * Copyright(c) 2006-2007, Ext JS, LLC.
10594  *
10595  * Originally Released Under LGPL - original licence link has changed is not relivant.
10596  *
10597  * Fork - LGPL
10598  * <script type="text/javascript">
10599  */
10600 /**
10601  * @class Roo.TabPanel
10602  * @extends Roo.util.Observable
10603  * A lightweight tab container.
10604  * <br><br>
10605  * Usage:
10606  * <pre><code>
10607 // basic tabs 1, built from existing content
10608 var tabs = new Roo.TabPanel("tabs1");
10609 tabs.addTab("script", "View Script");
10610 tabs.addTab("markup", "View Markup");
10611 tabs.activate("script");
10612
10613 // more advanced tabs, built from javascript
10614 var jtabs = new Roo.TabPanel("jtabs");
10615 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10616
10617 // set up the UpdateManager
10618 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10619 var updater = tab2.getUpdateManager();
10620 updater.setDefaultUrl("ajax1.htm");
10621 tab2.on('activate', updater.refresh, updater, true);
10622
10623 // Use setUrl for Ajax loading
10624 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10625 tab3.setUrl("ajax2.htm", null, true);
10626
10627 // Disabled tab
10628 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10629 tab4.disable();
10630
10631 jtabs.activate("jtabs-1");
10632  * </code></pre>
10633  * @constructor
10634  * Create a new TabPanel.
10635  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10636  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10637  */
10638 Roo.TabPanel = function(container, config){
10639     /**
10640     * The container element for this TabPanel.
10641     * @type Roo.Element
10642     */
10643     this.el = Roo.get(container, true);
10644     if(config){
10645         if(typeof config == "boolean"){
10646             this.tabPosition = config ? "bottom" : "top";
10647         }else{
10648             Roo.apply(this, config);
10649         }
10650     }
10651     if(this.tabPosition == "bottom"){
10652         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10653         this.el.addClass("x-tabs-bottom");
10654     }
10655     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10656     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10657     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10658     if(Roo.isIE){
10659         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10660     }
10661     if(this.tabPosition != "bottom"){
10662     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10663      * @type Roo.Element
10664      */
10665       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10666       this.el.addClass("x-tabs-top");
10667     }
10668     this.items = [];
10669
10670     this.bodyEl.setStyle("position", "relative");
10671
10672     this.active = null;
10673     this.activateDelegate = this.activate.createDelegate(this);
10674
10675     this.addEvents({
10676         /**
10677          * @event tabchange
10678          * Fires when the active tab changes
10679          * @param {Roo.TabPanel} this
10680          * @param {Roo.TabPanelItem} activePanel The new active tab
10681          */
10682         "tabchange": true,
10683         /**
10684          * @event beforetabchange
10685          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10686          * @param {Roo.TabPanel} this
10687          * @param {Object} e Set cancel to true on this object to cancel the tab change
10688          * @param {Roo.TabPanelItem} tab The tab being changed to
10689          */
10690         "beforetabchange" : true
10691     });
10692
10693     Roo.EventManager.onWindowResize(this.onResize, this);
10694     this.cpad = this.el.getPadding("lr");
10695     this.hiddenCount = 0;
10696
10697     Roo.TabPanel.superclass.constructor.call(this);
10698 };
10699
10700 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10701         /*
10702          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10703          */
10704     tabPosition : "top",
10705         /*
10706          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10707          */
10708     currentTabWidth : 0,
10709         /*
10710          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10711          */
10712     minTabWidth : 40,
10713         /*
10714          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10715          */
10716     maxTabWidth : 250,
10717         /*
10718          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10719          */
10720     preferredTabWidth : 175,
10721         /*
10722          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10723          */
10724     resizeTabs : false,
10725         /*
10726          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10727          */
10728     monitorResize : true,
10729
10730     /**
10731      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10732      * @param {String} id The id of the div to use <b>or create</b>
10733      * @param {String} text The text for the tab
10734      * @param {String} content (optional) Content to put in the TabPanelItem body
10735      * @param {Boolean} closable (optional) True to create a close icon on the tab
10736      * @return {Roo.TabPanelItem} The created TabPanelItem
10737      */
10738     addTab : function(id, text, content, closable){
10739         var item = new Roo.TabPanelItem(this, id, text, closable);
10740         this.addTabItem(item);
10741         if(content){
10742             item.setContent(content);
10743         }
10744         return item;
10745     },
10746
10747     /**
10748      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10749      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10750      * @return {Roo.TabPanelItem}
10751      */
10752     getTab : function(id){
10753         return this.items[id];
10754     },
10755
10756     /**
10757      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10758      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10759      */
10760     hideTab : function(id){
10761         var t = this.items[id];
10762         if(!t.isHidden()){
10763            t.setHidden(true);
10764            this.hiddenCount++;
10765            this.autoSizeTabs();
10766         }
10767     },
10768
10769     /**
10770      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10771      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10772      */
10773     unhideTab : function(id){
10774         var t = this.items[id];
10775         if(t.isHidden()){
10776            t.setHidden(false);
10777            this.hiddenCount--;
10778            this.autoSizeTabs();
10779         }
10780     },
10781
10782     /**
10783      * Adds an existing {@link Roo.TabPanelItem}.
10784      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10785      */
10786     addTabItem : function(item){
10787         this.items[item.id] = item;
10788         this.items.push(item);
10789         if(this.resizeTabs){
10790            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10791            this.autoSizeTabs();
10792         }else{
10793             item.autoSize();
10794         }
10795     },
10796
10797     /**
10798      * Removes a {@link Roo.TabPanelItem}.
10799      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10800      */
10801     removeTab : function(id){
10802         var items = this.items;
10803         var tab = items[id];
10804         if(!tab) { return; }
10805         var index = items.indexOf(tab);
10806         if(this.active == tab && items.length > 1){
10807             var newTab = this.getNextAvailable(index);
10808             if(newTab) {
10809                 newTab.activate();
10810             }
10811         }
10812         this.stripEl.dom.removeChild(tab.pnode.dom);
10813         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10814             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10815         }
10816         items.splice(index, 1);
10817         delete this.items[tab.id];
10818         tab.fireEvent("close", tab);
10819         tab.purgeListeners();
10820         this.autoSizeTabs();
10821     },
10822
10823     getNextAvailable : function(start){
10824         var items = this.items;
10825         var index = start;
10826         // look for a next tab that will slide over to
10827         // replace the one being removed
10828         while(index < items.length){
10829             var item = items[++index];
10830             if(item && !item.isHidden()){
10831                 return item;
10832             }
10833         }
10834         // if one isn't found select the previous tab (on the left)
10835         index = start;
10836         while(index >= 0){
10837             var item = items[--index];
10838             if(item && !item.isHidden()){
10839                 return item;
10840             }
10841         }
10842         return null;
10843     },
10844
10845     /**
10846      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10847      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10848      */
10849     disableTab : function(id){
10850         var tab = this.items[id];
10851         if(tab && this.active != tab){
10852             tab.disable();
10853         }
10854     },
10855
10856     /**
10857      * Enables a {@link Roo.TabPanelItem} that is disabled.
10858      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10859      */
10860     enableTab : function(id){
10861         var tab = this.items[id];
10862         tab.enable();
10863     },
10864
10865     /**
10866      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10867      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10868      * @return {Roo.TabPanelItem} The TabPanelItem.
10869      */
10870     activate : function(id){
10871         var tab = this.items[id];
10872         if(!tab){
10873             return null;
10874         }
10875         if(tab == this.active || tab.disabled){
10876             return tab;
10877         }
10878         var e = {};
10879         this.fireEvent("beforetabchange", this, e, tab);
10880         if(e.cancel !== true && !tab.disabled){
10881             if(this.active){
10882                 this.active.hide();
10883             }
10884             this.active = this.items[id];
10885             this.active.show();
10886             this.fireEvent("tabchange", this, this.active);
10887         }
10888         return tab;
10889     },
10890
10891     /**
10892      * Gets the active {@link Roo.TabPanelItem}.
10893      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10894      */
10895     getActiveTab : function(){
10896         return this.active;
10897     },
10898
10899     /**
10900      * Updates the tab body element to fit the height of the container element
10901      * for overflow scrolling
10902      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10903      */
10904     syncHeight : function(targetHeight){
10905         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10906         var bm = this.bodyEl.getMargins();
10907         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10908         this.bodyEl.setHeight(newHeight);
10909         return newHeight;
10910     },
10911
10912     onResize : function(){
10913         if(this.monitorResize){
10914             this.autoSizeTabs();
10915         }
10916     },
10917
10918     /**
10919      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10920      */
10921     beginUpdate : function(){
10922         this.updating = true;
10923     },
10924
10925     /**
10926      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10927      */
10928     endUpdate : function(){
10929         this.updating = false;
10930         this.autoSizeTabs();
10931     },
10932
10933     /**
10934      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10935      */
10936     autoSizeTabs : function(){
10937         var count = this.items.length;
10938         var vcount = count - this.hiddenCount;
10939         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10940         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10941         var availWidth = Math.floor(w / vcount);
10942         var b = this.stripBody;
10943         if(b.getWidth() > w){
10944             var tabs = this.items;
10945             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
10946             if(availWidth < this.minTabWidth){
10947                 /*if(!this.sleft){    // incomplete scrolling code
10948                     this.createScrollButtons();
10949                 }
10950                 this.showScroll();
10951                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
10952             }
10953         }else{
10954             if(this.currentTabWidth < this.preferredTabWidth){
10955                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
10956             }
10957         }
10958     },
10959
10960     /**
10961      * Returns the number of tabs in this TabPanel.
10962      * @return {Number}
10963      */
10964      getCount : function(){
10965          return this.items.length;
10966      },
10967
10968     /**
10969      * Resizes all the tabs to the passed width
10970      * @param {Number} The new width
10971      */
10972     setTabWidth : function(width){
10973         this.currentTabWidth = width;
10974         for(var i = 0, len = this.items.length; i < len; i++) {
10975                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
10976         }
10977     },
10978
10979     /**
10980      * Destroys this TabPanel
10981      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
10982      */
10983     destroy : function(removeEl){
10984         Roo.EventManager.removeResizeListener(this.onResize, this);
10985         for(var i = 0, len = this.items.length; i < len; i++){
10986             this.items[i].purgeListeners();
10987         }
10988         if(removeEl === true){
10989             this.el.update("");
10990             this.el.remove();
10991         }
10992     }
10993 });
10994
10995 /**
10996  * @class Roo.TabPanelItem
10997  * @extends Roo.util.Observable
10998  * Represents an individual item (tab plus body) in a TabPanel.
10999  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11000  * @param {String} id The id of this TabPanelItem
11001  * @param {String} text The text for the tab of this TabPanelItem
11002  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11003  */
11004 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11005     /**
11006      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11007      * @type Roo.TabPanel
11008      */
11009     this.tabPanel = tabPanel;
11010     /**
11011      * The id for this TabPanelItem
11012      * @type String
11013      */
11014     this.id = id;
11015     /** @private */
11016     this.disabled = false;
11017     /** @private */
11018     this.text = text;
11019     /** @private */
11020     this.loaded = false;
11021     this.closable = closable;
11022
11023     /**
11024      * The body element for this TabPanelItem.
11025      * @type Roo.Element
11026      */
11027     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11028     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11029     this.bodyEl.setStyle("display", "block");
11030     this.bodyEl.setStyle("zoom", "1");
11031     this.hideAction();
11032
11033     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11034     /** @private */
11035     this.el = Roo.get(els.el, true);
11036     this.inner = Roo.get(els.inner, true);
11037     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11038     this.pnode = Roo.get(els.el.parentNode, true);
11039     this.el.on("mousedown", this.onTabMouseDown, this);
11040     this.el.on("click", this.onTabClick, this);
11041     /** @private */
11042     if(closable){
11043         var c = Roo.get(els.close, true);
11044         c.dom.title = this.closeText;
11045         c.addClassOnOver("close-over");
11046         c.on("click", this.closeClick, this);
11047      }
11048
11049     this.addEvents({
11050          /**
11051          * @event activate
11052          * Fires when this tab becomes the active tab.
11053          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11054          * @param {Roo.TabPanelItem} this
11055          */
11056         "activate": true,
11057         /**
11058          * @event beforeclose
11059          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11060          * @param {Roo.TabPanelItem} this
11061          * @param {Object} e Set cancel to true on this object to cancel the close.
11062          */
11063         "beforeclose": true,
11064         /**
11065          * @event close
11066          * Fires when this tab is closed.
11067          * @param {Roo.TabPanelItem} this
11068          */
11069          "close": true,
11070         /**
11071          * @event deactivate
11072          * Fires when this tab is no longer the active tab.
11073          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11074          * @param {Roo.TabPanelItem} this
11075          */
11076          "deactivate" : true
11077     });
11078     this.hidden = false;
11079
11080     Roo.TabPanelItem.superclass.constructor.call(this);
11081 };
11082
11083 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11084     purgeListeners : function(){
11085        Roo.util.Observable.prototype.purgeListeners.call(this);
11086        this.el.removeAllListeners();
11087     },
11088     /**
11089      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11090      */
11091     show : function(){
11092         this.pnode.addClass("on");
11093         this.showAction();
11094         if(Roo.isOpera){
11095             this.tabPanel.stripWrap.repaint();
11096         }
11097         this.fireEvent("activate", this.tabPanel, this);
11098     },
11099
11100     /**
11101      * Returns true if this tab is the active tab.
11102      * @return {Boolean}
11103      */
11104     isActive : function(){
11105         return this.tabPanel.getActiveTab() == this;
11106     },
11107
11108     /**
11109      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11110      */
11111     hide : function(){
11112         this.pnode.removeClass("on");
11113         this.hideAction();
11114         this.fireEvent("deactivate", this.tabPanel, this);
11115     },
11116
11117     hideAction : function(){
11118         this.bodyEl.hide();
11119         this.bodyEl.setStyle("position", "absolute");
11120         this.bodyEl.setLeft("-20000px");
11121         this.bodyEl.setTop("-20000px");
11122     },
11123
11124     showAction : function(){
11125         this.bodyEl.setStyle("position", "relative");
11126         this.bodyEl.setTop("");
11127         this.bodyEl.setLeft("");
11128         this.bodyEl.show();
11129     },
11130
11131     /**
11132      * Set the tooltip for the tab.
11133      * @param {String} tooltip The tab's tooltip
11134      */
11135     setTooltip : function(text){
11136         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11137             this.textEl.dom.qtip = text;
11138             this.textEl.dom.removeAttribute('title');
11139         }else{
11140             this.textEl.dom.title = text;
11141         }
11142     },
11143
11144     onTabClick : function(e){
11145         e.preventDefault();
11146         this.tabPanel.activate(this.id);
11147     },
11148
11149     onTabMouseDown : function(e){
11150         e.preventDefault();
11151         this.tabPanel.activate(this.id);
11152     },
11153
11154     getWidth : function(){
11155         return this.inner.getWidth();
11156     },
11157
11158     setWidth : function(width){
11159         var iwidth = width - this.pnode.getPadding("lr");
11160         this.inner.setWidth(iwidth);
11161         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11162         this.pnode.setWidth(width);
11163     },
11164
11165     /**
11166      * Show or hide the tab
11167      * @param {Boolean} hidden True to hide or false to show.
11168      */
11169     setHidden : function(hidden){
11170         this.hidden = hidden;
11171         this.pnode.setStyle("display", hidden ? "none" : "");
11172     },
11173
11174     /**
11175      * Returns true if this tab is "hidden"
11176      * @return {Boolean}
11177      */
11178     isHidden : function(){
11179         return this.hidden;
11180     },
11181
11182     /**
11183      * Returns the text for this tab
11184      * @return {String}
11185      */
11186     getText : function(){
11187         return this.text;
11188     },
11189
11190     autoSize : function(){
11191         //this.el.beginMeasure();
11192         this.textEl.setWidth(1);
11193         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11194         //this.el.endMeasure();
11195     },
11196
11197     /**
11198      * Sets the text for the tab (Note: this also sets the tooltip text)
11199      * @param {String} text The tab's text and tooltip
11200      */
11201     setText : function(text){
11202         this.text = text;
11203         this.textEl.update(text);
11204         this.setTooltip(text);
11205         if(!this.tabPanel.resizeTabs){
11206             this.autoSize();
11207         }
11208     },
11209     /**
11210      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11211      */
11212     activate : function(){
11213         this.tabPanel.activate(this.id);
11214     },
11215
11216     /**
11217      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11218      */
11219     disable : function(){
11220         if(this.tabPanel.active != this){
11221             this.disabled = true;
11222             this.pnode.addClass("disabled");
11223         }
11224     },
11225
11226     /**
11227      * Enables this TabPanelItem if it was previously disabled.
11228      */
11229     enable : function(){
11230         this.disabled = false;
11231         this.pnode.removeClass("disabled");
11232     },
11233
11234     /**
11235      * Sets the content for this TabPanelItem.
11236      * @param {String} content The content
11237      * @param {Boolean} loadScripts true to look for and load scripts
11238      */
11239     setContent : function(content, loadScripts){
11240         this.bodyEl.update(content, loadScripts);
11241     },
11242
11243     /**
11244      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11245      * @return {Roo.UpdateManager} The UpdateManager
11246      */
11247     getUpdateManager : function(){
11248         return this.bodyEl.getUpdateManager();
11249     },
11250
11251     /**
11252      * Set a URL to be used to load the content for this TabPanelItem.
11253      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11254      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11255      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11256      * @return {Roo.UpdateManager} The UpdateManager
11257      */
11258     setUrl : function(url, params, loadOnce){
11259         if(this.refreshDelegate){
11260             this.un('activate', this.refreshDelegate);
11261         }
11262         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11263         this.on("activate", this.refreshDelegate);
11264         return this.bodyEl.getUpdateManager();
11265     },
11266
11267     /** @private */
11268     _handleRefresh : function(url, params, loadOnce){
11269         if(!loadOnce || !this.loaded){
11270             var updater = this.bodyEl.getUpdateManager();
11271             updater.update(url, params, this._setLoaded.createDelegate(this));
11272         }
11273     },
11274
11275     /**
11276      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11277      *   Will fail silently if the setUrl method has not been called.
11278      *   This does not activate the panel, just updates its content.
11279      */
11280     refresh : function(){
11281         if(this.refreshDelegate){
11282            this.loaded = false;
11283            this.refreshDelegate();
11284         }
11285     },
11286
11287     /** @private */
11288     _setLoaded : function(){
11289         this.loaded = true;
11290     },
11291
11292     /** @private */
11293     closeClick : function(e){
11294         var o = {};
11295         e.stopEvent();
11296         this.fireEvent("beforeclose", this, o);
11297         if(o.cancel !== true){
11298             this.tabPanel.removeTab(this.id);
11299         }
11300     },
11301     /**
11302      * The text displayed in the tooltip for the close icon.
11303      * @type String
11304      */
11305     closeText : "Close this tab"
11306 });
11307
11308 /** @private */
11309 Roo.TabPanel.prototype.createStrip = function(container){
11310     var strip = document.createElement("div");
11311     strip.className = "x-tabs-wrap";
11312     container.appendChild(strip);
11313     return strip;
11314 };
11315 /** @private */
11316 Roo.TabPanel.prototype.createStripList = function(strip){
11317     // div wrapper for retard IE
11318     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11319     return strip.firstChild.firstChild.firstChild.firstChild;
11320 };
11321 /** @private */
11322 Roo.TabPanel.prototype.createBody = function(container){
11323     var body = document.createElement("div");
11324     Roo.id(body, "tab-body");
11325     Roo.fly(body).addClass("x-tabs-body");
11326     container.appendChild(body);
11327     return body;
11328 };
11329 /** @private */
11330 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11331     var body = Roo.getDom(id);
11332     if(!body){
11333         body = document.createElement("div");
11334         body.id = id;
11335     }
11336     Roo.fly(body).addClass("x-tabs-item-body");
11337     bodyEl.insertBefore(body, bodyEl.firstChild);
11338     return body;
11339 };
11340 /** @private */
11341 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11342     var td = document.createElement("td");
11343     stripEl.appendChild(td);
11344     if(closable){
11345         td.className = "x-tabs-closable";
11346         if(!this.closeTpl){
11347             this.closeTpl = new Roo.Template(
11348                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11349                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11350                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11351             );
11352         }
11353         var el = this.closeTpl.overwrite(td, {"text": text});
11354         var close = el.getElementsByTagName("div")[0];
11355         var inner = el.getElementsByTagName("em")[0];
11356         return {"el": el, "close": close, "inner": inner};
11357     } else {
11358         if(!this.tabTpl){
11359             this.tabTpl = new Roo.Template(
11360                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11361                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11362             );
11363         }
11364         var el = this.tabTpl.overwrite(td, {"text": text});
11365         var inner = el.getElementsByTagName("em")[0];
11366         return {"el": el, "inner": inner};
11367     }
11368 };/*
11369  * Based on:
11370  * Ext JS Library 1.1.1
11371  * Copyright(c) 2006-2007, Ext JS, LLC.
11372  *
11373  * Originally Released Under LGPL - original licence link has changed is not relivant.
11374  *
11375  * Fork - LGPL
11376  * <script type="text/javascript">
11377  */
11378
11379 /**
11380  * @class Roo.Button
11381  * @extends Roo.util.Observable
11382  * Simple Button class
11383  * @cfg {String} text The button text
11384  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11385  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11386  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11387  * @cfg {Object} scope The scope of the handler
11388  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11389  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11390  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11391  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11392  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11393  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11394    applies if enableToggle = true)
11395  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11396  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11397   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11398  * @constructor
11399  * Create a new button
11400  * @param {Object} config The config object
11401  */
11402 Roo.Button = function(renderTo, config)
11403 {
11404     if (!config) {
11405         config = renderTo;
11406         renderTo = config.renderTo || false;
11407     }
11408     
11409     Roo.apply(this, config);
11410     this.addEvents({
11411         /**
11412              * @event click
11413              * Fires when this button is clicked
11414              * @param {Button} this
11415              * @param {EventObject} e The click event
11416              */
11417             "click" : true,
11418         /**
11419              * @event toggle
11420              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11421              * @param {Button} this
11422              * @param {Boolean} pressed
11423              */
11424             "toggle" : true,
11425         /**
11426              * @event mouseover
11427              * Fires when the mouse hovers over the button
11428              * @param {Button} this
11429              * @param {Event} e The event object
11430              */
11431         'mouseover' : true,
11432         /**
11433              * @event mouseout
11434              * Fires when the mouse exits the button
11435              * @param {Button} this
11436              * @param {Event} e The event object
11437              */
11438         'mouseout': true,
11439          /**
11440              * @event render
11441              * Fires when the button is rendered
11442              * @param {Button} this
11443              */
11444         'render': true
11445     });
11446     if(this.menu){
11447         this.menu = Roo.menu.MenuMgr.get(this.menu);
11448     }
11449     // register listeners first!!  - so render can be captured..
11450     Roo.util.Observable.call(this);
11451     if(renderTo){
11452         this.render(renderTo);
11453     }
11454     
11455   
11456 };
11457
11458 Roo.extend(Roo.Button, Roo.util.Observable, {
11459     /**
11460      * 
11461      */
11462     
11463     /**
11464      * Read-only. True if this button is hidden
11465      * @type Boolean
11466      */
11467     hidden : false,
11468     /**
11469      * Read-only. True if this button is disabled
11470      * @type Boolean
11471      */
11472     disabled : false,
11473     /**
11474      * Read-only. True if this button is pressed (only if enableToggle = true)
11475      * @type Boolean
11476      */
11477     pressed : false,
11478
11479     /**
11480      * @cfg {Number} tabIndex 
11481      * The DOM tabIndex for this button (defaults to undefined)
11482      */
11483     tabIndex : undefined,
11484
11485     /**
11486      * @cfg {Boolean} enableToggle
11487      * True to enable pressed/not pressed toggling (defaults to false)
11488      */
11489     enableToggle: false,
11490     /**
11491      * @cfg {Mixed} menu
11492      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11493      */
11494     menu : undefined,
11495     /**
11496      * @cfg {String} menuAlign
11497      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11498      */
11499     menuAlign : "tl-bl?",
11500
11501     /**
11502      * @cfg {String} iconCls
11503      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11504      */
11505     iconCls : undefined,
11506     /**
11507      * @cfg {String} type
11508      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11509      */
11510     type : 'button',
11511
11512     // private
11513     menuClassTarget: 'tr',
11514
11515     /**
11516      * @cfg {String} clickEvent
11517      * The type of event to map to the button's event handler (defaults to 'click')
11518      */
11519     clickEvent : 'click',
11520
11521     /**
11522      * @cfg {Boolean} handleMouseEvents
11523      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11524      */
11525     handleMouseEvents : true,
11526
11527     /**
11528      * @cfg {String} tooltipType
11529      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11530      */
11531     tooltipType : 'qtip',
11532
11533     /**
11534      * @cfg {String} cls
11535      * A CSS class to apply to the button's main element.
11536      */
11537     
11538     /**
11539      * @cfg {Roo.Template} template (Optional)
11540      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11541      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11542      * require code modifications if required elements (e.g. a button) aren't present.
11543      */
11544
11545     // private
11546     render : function(renderTo){
11547         var btn;
11548         if(this.hideParent){
11549             this.parentEl = Roo.get(renderTo);
11550         }
11551         if(!this.dhconfig){
11552             if(!this.template){
11553                 if(!Roo.Button.buttonTemplate){
11554                     // hideous table template
11555                     Roo.Button.buttonTemplate = new Roo.Template(
11556                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11557                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11558                         "</tr></tbody></table>");
11559                 }
11560                 this.template = Roo.Button.buttonTemplate;
11561             }
11562             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11563             var btnEl = btn.child("button:first");
11564             btnEl.on('focus', this.onFocus, this);
11565             btnEl.on('blur', this.onBlur, this);
11566             if(this.cls){
11567                 btn.addClass(this.cls);
11568             }
11569             if(this.icon){
11570                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11571             }
11572             if(this.iconCls){
11573                 btnEl.addClass(this.iconCls);
11574                 if(!this.cls){
11575                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11576                 }
11577             }
11578             if(this.tabIndex !== undefined){
11579                 btnEl.dom.tabIndex = this.tabIndex;
11580             }
11581             if(this.tooltip){
11582                 if(typeof this.tooltip == 'object'){
11583                     Roo.QuickTips.tips(Roo.apply({
11584                           target: btnEl.id
11585                     }, this.tooltip));
11586                 } else {
11587                     btnEl.dom[this.tooltipType] = this.tooltip;
11588                 }
11589             }
11590         }else{
11591             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11592         }
11593         this.el = btn;
11594         if(this.id){
11595             this.el.dom.id = this.el.id = this.id;
11596         }
11597         if(this.menu){
11598             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11599             this.menu.on("show", this.onMenuShow, this);
11600             this.menu.on("hide", this.onMenuHide, this);
11601         }
11602         btn.addClass("x-btn");
11603         if(Roo.isIE && !Roo.isIE7){
11604             this.autoWidth.defer(1, this);
11605         }else{
11606             this.autoWidth();
11607         }
11608         if(this.handleMouseEvents){
11609             btn.on("mouseover", this.onMouseOver, this);
11610             btn.on("mouseout", this.onMouseOut, this);
11611             btn.on("mousedown", this.onMouseDown, this);
11612         }
11613         btn.on(this.clickEvent, this.onClick, this);
11614         //btn.on("mouseup", this.onMouseUp, this);
11615         if(this.hidden){
11616             this.hide();
11617         }
11618         if(this.disabled){
11619             this.disable();
11620         }
11621         Roo.ButtonToggleMgr.register(this);
11622         if(this.pressed){
11623             this.el.addClass("x-btn-pressed");
11624         }
11625         if(this.repeat){
11626             var repeater = new Roo.util.ClickRepeater(btn,
11627                 typeof this.repeat == "object" ? this.repeat : {}
11628             );
11629             repeater.on("click", this.onClick,  this);
11630         }
11631         
11632         this.fireEvent('render', this);
11633         
11634     },
11635     /**
11636      * Returns the button's underlying element
11637      * @return {Roo.Element} The element
11638      */
11639     getEl : function(){
11640         return this.el;  
11641     },
11642     
11643     /**
11644      * Destroys this Button and removes any listeners.
11645      */
11646     destroy : function(){
11647         Roo.ButtonToggleMgr.unregister(this);
11648         this.el.removeAllListeners();
11649         this.purgeListeners();
11650         this.el.remove();
11651     },
11652
11653     // private
11654     autoWidth : function(){
11655         if(this.el){
11656             this.el.setWidth("auto");
11657             if(Roo.isIE7 && Roo.isStrict){
11658                 var ib = this.el.child('button');
11659                 if(ib && ib.getWidth() > 20){
11660                     ib.clip();
11661                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11662                 }
11663             }
11664             if(this.minWidth){
11665                 if(this.hidden){
11666                     this.el.beginMeasure();
11667                 }
11668                 if(this.el.getWidth() < this.minWidth){
11669                     this.el.setWidth(this.minWidth);
11670                 }
11671                 if(this.hidden){
11672                     this.el.endMeasure();
11673                 }
11674             }
11675         }
11676     },
11677
11678     /**
11679      * Assigns this button's click handler
11680      * @param {Function} handler The function to call when the button is clicked
11681      * @param {Object} scope (optional) Scope for the function passed in
11682      */
11683     setHandler : function(handler, scope){
11684         this.handler = handler;
11685         this.scope = scope;  
11686     },
11687     
11688     /**
11689      * Sets this button's text
11690      * @param {String} text The button text
11691      */
11692     setText : function(text){
11693         this.text = text;
11694         if(this.el){
11695             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11696         }
11697         this.autoWidth();
11698     },
11699     
11700     /**
11701      * Gets the text for this button
11702      * @return {String} The button text
11703      */
11704     getText : function(){
11705         return this.text;  
11706     },
11707     
11708     /**
11709      * Show this button
11710      */
11711     show: function(){
11712         this.hidden = false;
11713         if(this.el){
11714             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11715         }
11716     },
11717     
11718     /**
11719      * Hide this button
11720      */
11721     hide: function(){
11722         this.hidden = true;
11723         if(this.el){
11724             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11725         }
11726     },
11727     
11728     /**
11729      * Convenience function for boolean show/hide
11730      * @param {Boolean} visible True to show, false to hide
11731      */
11732     setVisible: function(visible){
11733         if(visible) {
11734             this.show();
11735         }else{
11736             this.hide();
11737         }
11738     },
11739     
11740     /**
11741      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11742      * @param {Boolean} state (optional) Force a particular state
11743      */
11744     toggle : function(state){
11745         state = state === undefined ? !this.pressed : state;
11746         if(state != this.pressed){
11747             if(state){
11748                 this.el.addClass("x-btn-pressed");
11749                 this.pressed = true;
11750                 this.fireEvent("toggle", this, true);
11751             }else{
11752                 this.el.removeClass("x-btn-pressed");
11753                 this.pressed = false;
11754                 this.fireEvent("toggle", this, false);
11755             }
11756             if(this.toggleHandler){
11757                 this.toggleHandler.call(this.scope || this, this, state);
11758             }
11759         }
11760     },
11761     
11762     /**
11763      * Focus the button
11764      */
11765     focus : function(){
11766         this.el.child('button:first').focus();
11767     },
11768     
11769     /**
11770      * Disable this button
11771      */
11772     disable : function(){
11773         if(this.el){
11774             this.el.addClass("x-btn-disabled");
11775         }
11776         this.disabled = true;
11777     },
11778     
11779     /**
11780      * Enable this button
11781      */
11782     enable : function(){
11783         if(this.el){
11784             this.el.removeClass("x-btn-disabled");
11785         }
11786         this.disabled = false;
11787     },
11788
11789     /**
11790      * Convenience function for boolean enable/disable
11791      * @param {Boolean} enabled True to enable, false to disable
11792      */
11793     setDisabled : function(v){
11794         this[v !== true ? "enable" : "disable"]();
11795     },
11796
11797     // private
11798     onClick : function(e){
11799         if(e){
11800             e.preventDefault();
11801         }
11802         if(e.button != 0){
11803             return;
11804         }
11805         if(!this.disabled){
11806             if(this.enableToggle){
11807                 this.toggle();
11808             }
11809             if(this.menu && !this.menu.isVisible()){
11810                 this.menu.show(this.el, this.menuAlign);
11811             }
11812             this.fireEvent("click", this, e);
11813             if(this.handler){
11814                 this.el.removeClass("x-btn-over");
11815                 this.handler.call(this.scope || this, this, e);
11816             }
11817         }
11818     },
11819     // private
11820     onMouseOver : function(e){
11821         if(!this.disabled){
11822             this.el.addClass("x-btn-over");
11823             this.fireEvent('mouseover', this, e);
11824         }
11825     },
11826     // private
11827     onMouseOut : function(e){
11828         if(!e.within(this.el,  true)){
11829             this.el.removeClass("x-btn-over");
11830             this.fireEvent('mouseout', this, e);
11831         }
11832     },
11833     // private
11834     onFocus : function(e){
11835         if(!this.disabled){
11836             this.el.addClass("x-btn-focus");
11837         }
11838     },
11839     // private
11840     onBlur : function(e){
11841         this.el.removeClass("x-btn-focus");
11842     },
11843     // private
11844     onMouseDown : function(e){
11845         if(!this.disabled && e.button == 0){
11846             this.el.addClass("x-btn-click");
11847             Roo.get(document).on('mouseup', this.onMouseUp, this);
11848         }
11849     },
11850     // private
11851     onMouseUp : function(e){
11852         if(e.button == 0){
11853             this.el.removeClass("x-btn-click");
11854             Roo.get(document).un('mouseup', this.onMouseUp, this);
11855         }
11856     },
11857     // private
11858     onMenuShow : function(e){
11859         this.el.addClass("x-btn-menu-active");
11860     },
11861     // private
11862     onMenuHide : function(e){
11863         this.el.removeClass("x-btn-menu-active");
11864     }   
11865 });
11866
11867 // Private utility class used by Button
11868 Roo.ButtonToggleMgr = function(){
11869    var groups = {};
11870    
11871    function toggleGroup(btn, state){
11872        if(state){
11873            var g = groups[btn.toggleGroup];
11874            for(var i = 0, l = g.length; i < l; i++){
11875                if(g[i] != btn){
11876                    g[i].toggle(false);
11877                }
11878            }
11879        }
11880    }
11881    
11882    return {
11883        register : function(btn){
11884            if(!btn.toggleGroup){
11885                return;
11886            }
11887            var g = groups[btn.toggleGroup];
11888            if(!g){
11889                g = groups[btn.toggleGroup] = [];
11890            }
11891            g.push(btn);
11892            btn.on("toggle", toggleGroup);
11893        },
11894        
11895        unregister : function(btn){
11896            if(!btn.toggleGroup){
11897                return;
11898            }
11899            var g = groups[btn.toggleGroup];
11900            if(g){
11901                g.remove(btn);
11902                btn.un("toggle", toggleGroup);
11903            }
11904        }
11905    };
11906 }();/*
11907  * Based on:
11908  * Ext JS Library 1.1.1
11909  * Copyright(c) 2006-2007, Ext JS, LLC.
11910  *
11911  * Originally Released Under LGPL - original licence link has changed is not relivant.
11912  *
11913  * Fork - LGPL
11914  * <script type="text/javascript">
11915  */
11916  
11917 /**
11918  * @class Roo.SplitButton
11919  * @extends Roo.Button
11920  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11921  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11922  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11923  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11924  * @cfg {String} arrowTooltip The title attribute of the arrow
11925  * @constructor
11926  * Create a new menu button
11927  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11928  * @param {Object} config The config object
11929  */
11930 Roo.SplitButton = function(renderTo, config){
11931     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11932     /**
11933      * @event arrowclick
11934      * Fires when this button's arrow is clicked
11935      * @param {SplitButton} this
11936      * @param {EventObject} e The click event
11937      */
11938     this.addEvents({"arrowclick":true});
11939 };
11940
11941 Roo.extend(Roo.SplitButton, Roo.Button, {
11942     render : function(renderTo){
11943         // this is one sweet looking template!
11944         var tpl = new Roo.Template(
11945             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
11946             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
11947             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
11948             "</tbody></table></td><td>",
11949             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
11950             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
11951             "</tbody></table></td></tr></table>"
11952         );
11953         var btn = tpl.append(renderTo, [this.text, this.type], true);
11954         var btnEl = btn.child("button");
11955         if(this.cls){
11956             btn.addClass(this.cls);
11957         }
11958         if(this.icon){
11959             btnEl.setStyle('background-image', 'url(' +this.icon +')');
11960         }
11961         if(this.iconCls){
11962             btnEl.addClass(this.iconCls);
11963             if(!this.cls){
11964                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11965             }
11966         }
11967         this.el = btn;
11968         if(this.handleMouseEvents){
11969             btn.on("mouseover", this.onMouseOver, this);
11970             btn.on("mouseout", this.onMouseOut, this);
11971             btn.on("mousedown", this.onMouseDown, this);
11972             btn.on("mouseup", this.onMouseUp, this);
11973         }
11974         btn.on(this.clickEvent, this.onClick, this);
11975         if(this.tooltip){
11976             if(typeof this.tooltip == 'object'){
11977                 Roo.QuickTips.tips(Roo.apply({
11978                       target: btnEl.id
11979                 }, this.tooltip));
11980             } else {
11981                 btnEl.dom[this.tooltipType] = this.tooltip;
11982             }
11983         }
11984         if(this.arrowTooltip){
11985             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
11986         }
11987         if(this.hidden){
11988             this.hide();
11989         }
11990         if(this.disabled){
11991             this.disable();
11992         }
11993         if(this.pressed){
11994             this.el.addClass("x-btn-pressed");
11995         }
11996         if(Roo.isIE && !Roo.isIE7){
11997             this.autoWidth.defer(1, this);
11998         }else{
11999             this.autoWidth();
12000         }
12001         if(this.menu){
12002             this.menu.on("show", this.onMenuShow, this);
12003             this.menu.on("hide", this.onMenuHide, this);
12004         }
12005         this.fireEvent('render', this);
12006     },
12007
12008     // private
12009     autoWidth : function(){
12010         if(this.el){
12011             var tbl = this.el.child("table:first");
12012             var tbl2 = this.el.child("table:last");
12013             this.el.setWidth("auto");
12014             tbl.setWidth("auto");
12015             if(Roo.isIE7 && Roo.isStrict){
12016                 var ib = this.el.child('button:first');
12017                 if(ib && ib.getWidth() > 20){
12018                     ib.clip();
12019                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12020                 }
12021             }
12022             if(this.minWidth){
12023                 if(this.hidden){
12024                     this.el.beginMeasure();
12025                 }
12026                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12027                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12028                 }
12029                 if(this.hidden){
12030                     this.el.endMeasure();
12031                 }
12032             }
12033             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12034         } 
12035     },
12036     /**
12037      * Sets this button's click handler
12038      * @param {Function} handler The function to call when the button is clicked
12039      * @param {Object} scope (optional) Scope for the function passed above
12040      */
12041     setHandler : function(handler, scope){
12042         this.handler = handler;
12043         this.scope = scope;  
12044     },
12045     
12046     /**
12047      * Sets this button's arrow click handler
12048      * @param {Function} handler The function to call when the arrow is clicked
12049      * @param {Object} scope (optional) Scope for the function passed above
12050      */
12051     setArrowHandler : function(handler, scope){
12052         this.arrowHandler = handler;
12053         this.scope = scope;  
12054     },
12055     
12056     /**
12057      * Focus the button
12058      */
12059     focus : function(){
12060         if(this.el){
12061             this.el.child("button:first").focus();
12062         }
12063     },
12064
12065     // private
12066     onClick : function(e){
12067         e.preventDefault();
12068         if(!this.disabled){
12069             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12070                 if(this.menu && !this.menu.isVisible()){
12071                     this.menu.show(this.el, this.menuAlign);
12072                 }
12073                 this.fireEvent("arrowclick", this, e);
12074                 if(this.arrowHandler){
12075                     this.arrowHandler.call(this.scope || this, this, e);
12076                 }
12077             }else{
12078                 this.fireEvent("click", this, e);
12079                 if(this.handler){
12080                     this.handler.call(this.scope || this, this, e);
12081                 }
12082             }
12083         }
12084     },
12085     // private
12086     onMouseDown : function(e){
12087         if(!this.disabled){
12088             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12089         }
12090     },
12091     // private
12092     onMouseUp : function(e){
12093         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12094     }   
12095 });
12096
12097
12098 // backwards compat
12099 Roo.MenuButton = Roo.SplitButton;/*
12100  * Based on:
12101  * Ext JS Library 1.1.1
12102  * Copyright(c) 2006-2007, Ext JS, LLC.
12103  *
12104  * Originally Released Under LGPL - original licence link has changed is not relivant.
12105  *
12106  * Fork - LGPL
12107  * <script type="text/javascript">
12108  */
12109
12110 /**
12111  * @class Roo.Toolbar
12112  * Basic Toolbar class.
12113  * @constructor
12114  * Creates a new Toolbar
12115  * @param {Object} config The config object
12116  */ 
12117 Roo.Toolbar = function(container, buttons, config)
12118 {
12119     /// old consturctor format still supported..
12120     if(container instanceof Array){ // omit the container for later rendering
12121         buttons = container;
12122         config = buttons;
12123         container = null;
12124     }
12125     if (typeof(container) == 'object' && container.xtype) {
12126         config = container;
12127         container = config.container;
12128         buttons = config.buttons; // not really - use items!!
12129     }
12130     var xitems = [];
12131     if (config && config.items) {
12132         xitems = config.items;
12133         delete config.items;
12134     }
12135     Roo.apply(this, config);
12136     this.buttons = buttons;
12137     
12138     if(container){
12139         this.render(container);
12140     }
12141     Roo.each(xitems, function(b) {
12142         this.add(b);
12143     }, this);
12144     
12145 };
12146
12147 Roo.Toolbar.prototype = {
12148     /**
12149      * @cfg {Roo.data.Store} items
12150      * array of button configs or elements to add
12151      */
12152     
12153     /**
12154      * @cfg {String/HTMLElement/Element} container
12155      * The id or element that will contain the toolbar
12156      */
12157     // private
12158     render : function(ct){
12159         this.el = Roo.get(ct);
12160         if(this.cls){
12161             this.el.addClass(this.cls);
12162         }
12163         // using a table allows for vertical alignment
12164         // 100% width is needed by Safari...
12165         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12166         this.tr = this.el.child("tr", true);
12167         var autoId = 0;
12168         this.items = new Roo.util.MixedCollection(false, function(o){
12169             return o.id || ("item" + (++autoId));
12170         });
12171         if(this.buttons){
12172             this.add.apply(this, this.buttons);
12173             delete this.buttons;
12174         }
12175     },
12176
12177     /**
12178      * Adds element(s) to the toolbar -- this function takes a variable number of 
12179      * arguments of mixed type and adds them to the toolbar.
12180      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12181      * <ul>
12182      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12183      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12184      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12185      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12186      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12187      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12188      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12189      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12190      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12191      * </ul>
12192      * @param {Mixed} arg2
12193      * @param {Mixed} etc.
12194      */
12195     add : function(){
12196         var a = arguments, l = a.length;
12197         for(var i = 0; i < l; i++){
12198             this._add(a[i]);
12199         }
12200     },
12201     // private..
12202     _add : function(el) {
12203         
12204         if (el.xtype) {
12205             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12206         }
12207         
12208         if (el.applyTo){ // some kind of form field
12209             return this.addField(el);
12210         } 
12211         if (el.render){ // some kind of Toolbar.Item
12212             return this.addItem(el);
12213         }
12214         if (typeof el == "string"){ // string
12215             if(el == "separator" || el == "-"){
12216                 return this.addSeparator();
12217             }
12218             if (el == " "){
12219                 return this.addSpacer();
12220             }
12221             if(el == "->"){
12222                 return this.addFill();
12223             }
12224             return this.addText(el);
12225             
12226         }
12227         if(el.tagName){ // element
12228             return this.addElement(el);
12229         }
12230         if(typeof el == "object"){ // must be button config?
12231             return this.addButton(el);
12232         }
12233         // and now what?!?!
12234         return false;
12235         
12236     },
12237     
12238     /**
12239      * Add an Xtype element
12240      * @param {Object} xtype Xtype Object
12241      * @return {Object} created Object
12242      */
12243     addxtype : function(e){
12244         return this.add(e);  
12245     },
12246     
12247     /**
12248      * Returns the Element for this toolbar.
12249      * @return {Roo.Element}
12250      */
12251     getEl : function(){
12252         return this.el;  
12253     },
12254     
12255     /**
12256      * Adds a separator
12257      * @return {Roo.Toolbar.Item} The separator item
12258      */
12259     addSeparator : function(){
12260         return this.addItem(new Roo.Toolbar.Separator());
12261     },
12262
12263     /**
12264      * Adds a spacer element
12265      * @return {Roo.Toolbar.Spacer} The spacer item
12266      */
12267     addSpacer : function(){
12268         return this.addItem(new Roo.Toolbar.Spacer());
12269     },
12270
12271     /**
12272      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12273      * @return {Roo.Toolbar.Fill} The fill item
12274      */
12275     addFill : function(){
12276         return this.addItem(new Roo.Toolbar.Fill());
12277     },
12278
12279     /**
12280      * Adds any standard HTML element to the toolbar
12281      * @param {String/HTMLElement/Element} el The element or id of the element to add
12282      * @return {Roo.Toolbar.Item} The element's item
12283      */
12284     addElement : function(el){
12285         return this.addItem(new Roo.Toolbar.Item(el));
12286     },
12287     /**
12288      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12289      * @type Roo.util.MixedCollection  
12290      */
12291     items : false,
12292      
12293     /**
12294      * Adds any Toolbar.Item or subclass
12295      * @param {Roo.Toolbar.Item} item
12296      * @return {Roo.Toolbar.Item} The item
12297      */
12298     addItem : function(item){
12299         var td = this.nextBlock();
12300         item.render(td);
12301         this.items.add(item);
12302         return item;
12303     },
12304     
12305     /**
12306      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12307      * @param {Object/Array} config A button config or array of configs
12308      * @return {Roo.Toolbar.Button/Array}
12309      */
12310     addButton : function(config){
12311         if(config instanceof Array){
12312             var buttons = [];
12313             for(var i = 0, len = config.length; i < len; i++) {
12314                 buttons.push(this.addButton(config[i]));
12315             }
12316             return buttons;
12317         }
12318         var b = config;
12319         if(!(config instanceof Roo.Toolbar.Button)){
12320             b = config.split ?
12321                 new Roo.Toolbar.SplitButton(config) :
12322                 new Roo.Toolbar.Button(config);
12323         }
12324         var td = this.nextBlock();
12325         b.render(td);
12326         this.items.add(b);
12327         return b;
12328     },
12329     
12330     /**
12331      * Adds text to the toolbar
12332      * @param {String} text The text to add
12333      * @return {Roo.Toolbar.Item} The element's item
12334      */
12335     addText : function(text){
12336         return this.addItem(new Roo.Toolbar.TextItem(text));
12337     },
12338     
12339     /**
12340      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12341      * @param {Number} index The index where the item is to be inserted
12342      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12343      * @return {Roo.Toolbar.Button/Item}
12344      */
12345     insertButton : function(index, item){
12346         if(item instanceof Array){
12347             var buttons = [];
12348             for(var i = 0, len = item.length; i < len; i++) {
12349                buttons.push(this.insertButton(index + i, item[i]));
12350             }
12351             return buttons;
12352         }
12353         if (!(item instanceof Roo.Toolbar.Button)){
12354            item = new Roo.Toolbar.Button(item);
12355         }
12356         var td = document.createElement("td");
12357         this.tr.insertBefore(td, this.tr.childNodes[index]);
12358         item.render(td);
12359         this.items.insert(index, item);
12360         return item;
12361     },
12362     
12363     /**
12364      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12365      * @param {Object} config
12366      * @return {Roo.Toolbar.Item} The element's item
12367      */
12368     addDom : function(config, returnEl){
12369         var td = this.nextBlock();
12370         Roo.DomHelper.overwrite(td, config);
12371         var ti = new Roo.Toolbar.Item(td.firstChild);
12372         ti.render(td);
12373         this.items.add(ti);
12374         return ti;
12375     },
12376
12377     /**
12378      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12379      * @type Roo.util.MixedCollection  
12380      */
12381     fields : false,
12382     
12383     /**
12384      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12385      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12386      * @param {Roo.form.Field} field
12387      * @return {Roo.ToolbarItem}
12388      */
12389      
12390       
12391     addField : function(field) {
12392         if (!this.fields) {
12393             var autoId = 0;
12394             this.fields = new Roo.util.MixedCollection(false, function(o){
12395                 return o.id || ("item" + (++autoId));
12396             });
12397
12398         }
12399         
12400         var td = this.nextBlock();
12401         field.render(td);
12402         var ti = new Roo.Toolbar.Item(td.firstChild);
12403         ti.render(td);
12404         this.items.add(ti);
12405         this.fields.add(field);
12406         return ti;
12407     },
12408     /**
12409      * Hide the toolbar
12410      * @method hide
12411      */
12412      
12413       
12414     hide : function()
12415     {
12416         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12417         this.el.child('div').hide();
12418     },
12419     /**
12420      * Show the toolbar
12421      * @method show
12422      */
12423     show : function()
12424     {
12425         this.el.child('div').show();
12426     },
12427       
12428     // private
12429     nextBlock : function(){
12430         var td = document.createElement("td");
12431         this.tr.appendChild(td);
12432         return td;
12433     },
12434
12435     // private
12436     destroy : function(){
12437         if(this.items){ // rendered?
12438             Roo.destroy.apply(Roo, this.items.items);
12439         }
12440         if(this.fields){ // rendered?
12441             Roo.destroy.apply(Roo, this.fields.items);
12442         }
12443         Roo.Element.uncache(this.el, this.tr);
12444     }
12445 };
12446
12447 /**
12448  * @class Roo.Toolbar.Item
12449  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12450  * @constructor
12451  * Creates a new Item
12452  * @param {HTMLElement} el 
12453  */
12454 Roo.Toolbar.Item = function(el){
12455     this.el = Roo.getDom(el);
12456     this.id = Roo.id(this.el);
12457     this.hidden = false;
12458 };
12459
12460 Roo.Toolbar.Item.prototype = {
12461     
12462     /**
12463      * Get this item's HTML Element
12464      * @return {HTMLElement}
12465      */
12466     getEl : function(){
12467        return this.el;  
12468     },
12469
12470     // private
12471     render : function(td){
12472         this.td = td;
12473         td.appendChild(this.el);
12474     },
12475     
12476     /**
12477      * Removes and destroys this item.
12478      */
12479     destroy : function(){
12480         this.td.parentNode.removeChild(this.td);
12481     },
12482     
12483     /**
12484      * Shows this item.
12485      */
12486     show: function(){
12487         this.hidden = false;
12488         this.td.style.display = "";
12489     },
12490     
12491     /**
12492      * Hides this item.
12493      */
12494     hide: function(){
12495         this.hidden = true;
12496         this.td.style.display = "none";
12497     },
12498     
12499     /**
12500      * Convenience function for boolean show/hide.
12501      * @param {Boolean} visible true to show/false to hide
12502      */
12503     setVisible: function(visible){
12504         if(visible) {
12505             this.show();
12506         }else{
12507             this.hide();
12508         }
12509     },
12510     
12511     /**
12512      * Try to focus this item.
12513      */
12514     focus : function(){
12515         Roo.fly(this.el).focus();
12516     },
12517     
12518     /**
12519      * Disables this item.
12520      */
12521     disable : function(){
12522         Roo.fly(this.td).addClass("x-item-disabled");
12523         this.disabled = true;
12524         this.el.disabled = true;
12525     },
12526     
12527     /**
12528      * Enables this item.
12529      */
12530     enable : function(){
12531         Roo.fly(this.td).removeClass("x-item-disabled");
12532         this.disabled = false;
12533         this.el.disabled = false;
12534     }
12535 };
12536
12537
12538 /**
12539  * @class Roo.Toolbar.Separator
12540  * @extends Roo.Toolbar.Item
12541  * A simple toolbar separator class
12542  * @constructor
12543  * Creates a new Separator
12544  */
12545 Roo.Toolbar.Separator = function(){
12546     var s = document.createElement("span");
12547     s.className = "ytb-sep";
12548     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12549 };
12550 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12551     enable:Roo.emptyFn,
12552     disable:Roo.emptyFn,
12553     focus:Roo.emptyFn
12554 });
12555
12556 /**
12557  * @class Roo.Toolbar.Spacer
12558  * @extends Roo.Toolbar.Item
12559  * A simple element that adds extra horizontal space to a toolbar.
12560  * @constructor
12561  * Creates a new Spacer
12562  */
12563 Roo.Toolbar.Spacer = function(){
12564     var s = document.createElement("div");
12565     s.className = "ytb-spacer";
12566     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12567 };
12568 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12569     enable:Roo.emptyFn,
12570     disable:Roo.emptyFn,
12571     focus:Roo.emptyFn
12572 });
12573
12574 /**
12575  * @class Roo.Toolbar.Fill
12576  * @extends Roo.Toolbar.Spacer
12577  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12578  * @constructor
12579  * Creates a new Spacer
12580  */
12581 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12582     // private
12583     render : function(td){
12584         td.style.width = '100%';
12585         Roo.Toolbar.Fill.superclass.render.call(this, td);
12586     }
12587 });
12588
12589 /**
12590  * @class Roo.Toolbar.TextItem
12591  * @extends Roo.Toolbar.Item
12592  * A simple class that renders text directly into a toolbar.
12593  * @constructor
12594  * Creates a new TextItem
12595  * @param {String} text
12596  */
12597 Roo.Toolbar.TextItem = function(text){
12598     if (typeof(text) == 'object') {
12599         text = text.text;
12600     }
12601     var s = document.createElement("span");
12602     s.className = "ytb-text";
12603     s.innerHTML = text;
12604     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12605 };
12606 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12607     enable:Roo.emptyFn,
12608     disable:Roo.emptyFn,
12609     focus:Roo.emptyFn
12610 });
12611
12612 /**
12613  * @class Roo.Toolbar.Button
12614  * @extends Roo.Button
12615  * A button that renders into a toolbar.
12616  * @constructor
12617  * Creates a new Button
12618  * @param {Object} config A standard {@link Roo.Button} config object
12619  */
12620 Roo.Toolbar.Button = function(config){
12621     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12622 };
12623 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12624     render : function(td){
12625         this.td = td;
12626         Roo.Toolbar.Button.superclass.render.call(this, td);
12627     },
12628     
12629     /**
12630      * Removes and destroys this button
12631      */
12632     destroy : function(){
12633         Roo.Toolbar.Button.superclass.destroy.call(this);
12634         this.td.parentNode.removeChild(this.td);
12635     },
12636     
12637     /**
12638      * Shows this button
12639      */
12640     show: function(){
12641         this.hidden = false;
12642         this.td.style.display = "";
12643     },
12644     
12645     /**
12646      * Hides this button
12647      */
12648     hide: function(){
12649         this.hidden = true;
12650         this.td.style.display = "none";
12651     },
12652
12653     /**
12654      * Disables this item
12655      */
12656     disable : function(){
12657         Roo.fly(this.td).addClass("x-item-disabled");
12658         this.disabled = true;
12659     },
12660
12661     /**
12662      * Enables this item
12663      */
12664     enable : function(){
12665         Roo.fly(this.td).removeClass("x-item-disabled");
12666         this.disabled = false;
12667     }
12668 });
12669 // backwards compat
12670 Roo.ToolbarButton = Roo.Toolbar.Button;
12671
12672 /**
12673  * @class Roo.Toolbar.SplitButton
12674  * @extends Roo.SplitButton
12675  * A menu button that renders into a toolbar.
12676  * @constructor
12677  * Creates a new SplitButton
12678  * @param {Object} config A standard {@link Roo.SplitButton} config object
12679  */
12680 Roo.Toolbar.SplitButton = function(config){
12681     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12682 };
12683 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12684     render : function(td){
12685         this.td = td;
12686         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12687     },
12688     
12689     /**
12690      * Removes and destroys this button
12691      */
12692     destroy : function(){
12693         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12694         this.td.parentNode.removeChild(this.td);
12695     },
12696     
12697     /**
12698      * Shows this button
12699      */
12700     show: function(){
12701         this.hidden = false;
12702         this.td.style.display = "";
12703     },
12704     
12705     /**
12706      * Hides this button
12707      */
12708     hide: function(){
12709         this.hidden = true;
12710         this.td.style.display = "none";
12711     }
12712 });
12713
12714 // backwards compat
12715 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12716  * Based on:
12717  * Ext JS Library 1.1.1
12718  * Copyright(c) 2006-2007, Ext JS, LLC.
12719  *
12720  * Originally Released Under LGPL - original licence link has changed is not relivant.
12721  *
12722  * Fork - LGPL
12723  * <script type="text/javascript">
12724  */
12725  
12726 /**
12727  * @class Roo.PagingToolbar
12728  * @extends Roo.Toolbar
12729  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12730  * @constructor
12731  * Create a new PagingToolbar
12732  * @param {Object} config The config object
12733  */
12734 Roo.PagingToolbar = function(el, ds, config)
12735 {
12736     // old args format still supported... - xtype is prefered..
12737     if (typeof(el) == 'object' && el.xtype) {
12738         // created from xtype...
12739         config = el;
12740         ds = el.dataSource;
12741         el = config.container;
12742     }
12743     
12744     
12745     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12746     this.ds = ds;
12747     this.cursor = 0;
12748     this.renderButtons(this.el);
12749     this.bind(ds);
12750 };
12751
12752 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12753     /**
12754      * @cfg {Roo.data.Store} dataSource
12755      * The underlying data store providing the paged data
12756      */
12757     /**
12758      * @cfg {String/HTMLElement/Element} container
12759      * container The id or element that will contain the toolbar
12760      */
12761     /**
12762      * @cfg {Boolean} displayInfo
12763      * True to display the displayMsg (defaults to false)
12764      */
12765     /**
12766      * @cfg {Number} pageSize
12767      * The number of records to display per page (defaults to 20)
12768      */
12769     pageSize: 20,
12770     /**
12771      * @cfg {String} displayMsg
12772      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12773      */
12774     displayMsg : 'Displaying {0} - {1} of {2}',
12775     /**
12776      * @cfg {String} emptyMsg
12777      * The message to display when no records are found (defaults to "No data to display")
12778      */
12779     emptyMsg : 'No data to display',
12780     /**
12781      * Customizable piece of the default paging text (defaults to "Page")
12782      * @type String
12783      */
12784     beforePageText : "Page",
12785     /**
12786      * Customizable piece of the default paging text (defaults to "of %0")
12787      * @type String
12788      */
12789     afterPageText : "of {0}",
12790     /**
12791      * Customizable piece of the default paging text (defaults to "First Page")
12792      * @type String
12793      */
12794     firstText : "First Page",
12795     /**
12796      * Customizable piece of the default paging text (defaults to "Previous Page")
12797      * @type String
12798      */
12799     prevText : "Previous Page",
12800     /**
12801      * Customizable piece of the default paging text (defaults to "Next Page")
12802      * @type String
12803      */
12804     nextText : "Next Page",
12805     /**
12806      * Customizable piece of the default paging text (defaults to "Last Page")
12807      * @type String
12808      */
12809     lastText : "Last Page",
12810     /**
12811      * Customizable piece of the default paging text (defaults to "Refresh")
12812      * @type String
12813      */
12814     refreshText : "Refresh",
12815
12816     // private
12817     renderButtons : function(el){
12818         Roo.PagingToolbar.superclass.render.call(this, el);
12819         this.first = this.addButton({
12820             tooltip: this.firstText,
12821             cls: "x-btn-icon x-grid-page-first",
12822             disabled: true,
12823             handler: this.onClick.createDelegate(this, ["first"])
12824         });
12825         this.prev = this.addButton({
12826             tooltip: this.prevText,
12827             cls: "x-btn-icon x-grid-page-prev",
12828             disabled: true,
12829             handler: this.onClick.createDelegate(this, ["prev"])
12830         });
12831         //this.addSeparator();
12832         this.add(this.beforePageText);
12833         this.field = Roo.get(this.addDom({
12834            tag: "input",
12835            type: "text",
12836            size: "3",
12837            value: "1",
12838            cls: "x-grid-page-number"
12839         }).el);
12840         this.field.on("keydown", this.onPagingKeydown, this);
12841         this.field.on("focus", function(){this.dom.select();});
12842         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12843         this.field.setHeight(18);
12844         //this.addSeparator();
12845         this.next = this.addButton({
12846             tooltip: this.nextText,
12847             cls: "x-btn-icon x-grid-page-next",
12848             disabled: true,
12849             handler: this.onClick.createDelegate(this, ["next"])
12850         });
12851         this.last = this.addButton({
12852             tooltip: this.lastText,
12853             cls: "x-btn-icon x-grid-page-last",
12854             disabled: true,
12855             handler: this.onClick.createDelegate(this, ["last"])
12856         });
12857         //this.addSeparator();
12858         this.loading = this.addButton({
12859             tooltip: this.refreshText,
12860             cls: "x-btn-icon x-grid-loading",
12861             handler: this.onClick.createDelegate(this, ["refresh"])
12862         });
12863
12864         if(this.displayInfo){
12865             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12866         }
12867     },
12868
12869     // private
12870     updateInfo : function(){
12871         if(this.displayEl){
12872             var count = this.ds.getCount();
12873             var msg = count == 0 ?
12874                 this.emptyMsg :
12875                 String.format(
12876                     this.displayMsg,
12877                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12878                 );
12879             this.displayEl.update(msg);
12880         }
12881     },
12882
12883     // private
12884     onLoad : function(ds, r, o){
12885        this.cursor = o.params ? o.params.start : 0;
12886        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12887
12888        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12889        this.field.dom.value = ap;
12890        this.first.setDisabled(ap == 1);
12891        this.prev.setDisabled(ap == 1);
12892        this.next.setDisabled(ap == ps);
12893        this.last.setDisabled(ap == ps);
12894        this.loading.enable();
12895        this.updateInfo();
12896     },
12897
12898     // private
12899     getPageData : function(){
12900         var total = this.ds.getTotalCount();
12901         return {
12902             total : total,
12903             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12904             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12905         };
12906     },
12907
12908     // private
12909     onLoadError : function(){
12910         this.loading.enable();
12911     },
12912
12913     // private
12914     onPagingKeydown : function(e){
12915         var k = e.getKey();
12916         var d = this.getPageData();
12917         if(k == e.RETURN){
12918             var v = this.field.dom.value, pageNum;
12919             if(!v || isNaN(pageNum = parseInt(v, 10))){
12920                 this.field.dom.value = d.activePage;
12921                 return;
12922             }
12923             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12924             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12925             e.stopEvent();
12926         }
12927         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))
12928         {
12929           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12930           this.field.dom.value = pageNum;
12931           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
12932           e.stopEvent();
12933         }
12934         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12935         {
12936           var v = this.field.dom.value, pageNum; 
12937           var increment = (e.shiftKey) ? 10 : 1;
12938           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
12939             increment *= -1;
12940           if(!v || isNaN(pageNum = parseInt(v, 10))) {
12941             this.field.dom.value = d.activePage;
12942             return;
12943           }
12944           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
12945           {
12946             this.field.dom.value = parseInt(v, 10) + increment;
12947             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
12948             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12949           }
12950           e.stopEvent();
12951         }
12952     },
12953
12954     // private
12955     beforeLoad : function(){
12956         if(this.loading){
12957             this.loading.disable();
12958         }
12959     },
12960
12961     // private
12962     onClick : function(which){
12963         var ds = this.ds;
12964         switch(which){
12965             case "first":
12966                 ds.load({params:{start: 0, limit: this.pageSize}});
12967             break;
12968             case "prev":
12969                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
12970             break;
12971             case "next":
12972                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
12973             break;
12974             case "last":
12975                 var total = ds.getTotalCount();
12976                 var extra = total % this.pageSize;
12977                 var lastStart = extra ? (total - extra) : total-this.pageSize;
12978                 ds.load({params:{start: lastStart, limit: this.pageSize}});
12979             break;
12980             case "refresh":
12981                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
12982             break;
12983         }
12984     },
12985
12986     /**
12987      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
12988      * @param {Roo.data.Store} store The data store to unbind
12989      */
12990     unbind : function(ds){
12991         ds.un("beforeload", this.beforeLoad, this);
12992         ds.un("load", this.onLoad, this);
12993         ds.un("loadexception", this.onLoadError, this);
12994         ds.un("remove", this.updateInfo, this);
12995         ds.un("add", this.updateInfo, this);
12996         this.ds = undefined;
12997     },
12998
12999     /**
13000      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13001      * @param {Roo.data.Store} store The data store to bind
13002      */
13003     bind : function(ds){
13004         ds.on("beforeload", this.beforeLoad, this);
13005         ds.on("load", this.onLoad, this);
13006         ds.on("loadexception", this.onLoadError, this);
13007         ds.on("remove", this.updateInfo, this);
13008         ds.on("add", this.updateInfo, this);
13009         this.ds = ds;
13010     }
13011 });/*
13012  * Based on:
13013  * Ext JS Library 1.1.1
13014  * Copyright(c) 2006-2007, Ext JS, LLC.
13015  *
13016  * Originally Released Under LGPL - original licence link has changed is not relivant.
13017  *
13018  * Fork - LGPL
13019  * <script type="text/javascript">
13020  */
13021
13022 /**
13023  * @class Roo.Resizable
13024  * @extends Roo.util.Observable
13025  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13026  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13027  * 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
13028  * the element will be wrapped for you automatically.</p>
13029  * <p>Here is the list of valid resize handles:</p>
13030  * <pre>
13031 Value   Description
13032 ------  -------------------
13033  'n'     north
13034  's'     south
13035  'e'     east
13036  'w'     west
13037  'nw'    northwest
13038  'sw'    southwest
13039  'se'    southeast
13040  'ne'    northeast
13041  'hd'    horizontal drag
13042  'all'   all
13043 </pre>
13044  * <p>Here's an example showing the creation of a typical Resizable:</p>
13045  * <pre><code>
13046 var resizer = new Roo.Resizable("element-id", {
13047     handles: 'all',
13048     minWidth: 200,
13049     minHeight: 100,
13050     maxWidth: 500,
13051     maxHeight: 400,
13052     pinned: true
13053 });
13054 resizer.on("resize", myHandler);
13055 </code></pre>
13056  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13057  * resizer.east.setDisplayed(false);</p>
13058  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13059  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13060  * resize operation's new size (defaults to [0, 0])
13061  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13062  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13063  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13064  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13065  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13066  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13067  * @cfg {Number} width The width of the element in pixels (defaults to null)
13068  * @cfg {Number} height The height of the element in pixels (defaults to null)
13069  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13070  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13071  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13072  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13073  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13074  * in favor of the handles config option (defaults to false)
13075  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13076  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13077  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13078  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13079  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13080  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13081  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13082  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13083  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13084  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13085  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13086  * @constructor
13087  * Create a new resizable component
13088  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13089  * @param {Object} config configuration options
13090   */
13091 Roo.Resizable = function(el, config)
13092 {
13093     this.el = Roo.get(el);
13094
13095     if(config && config.wrap){
13096         config.resizeChild = this.el;
13097         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13098         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13099         this.el.setStyle("overflow", "hidden");
13100         this.el.setPositioning(config.resizeChild.getPositioning());
13101         config.resizeChild.clearPositioning();
13102         if(!config.width || !config.height){
13103             var csize = config.resizeChild.getSize();
13104             this.el.setSize(csize.width, csize.height);
13105         }
13106         if(config.pinned && !config.adjustments){
13107             config.adjustments = "auto";
13108         }
13109     }
13110
13111     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13112     this.proxy.unselectable();
13113     this.proxy.enableDisplayMode('block');
13114
13115     Roo.apply(this, config);
13116
13117     if(this.pinned){
13118         this.disableTrackOver = true;
13119         this.el.addClass("x-resizable-pinned");
13120     }
13121     // if the element isn't positioned, make it relative
13122     var position = this.el.getStyle("position");
13123     if(position != "absolute" && position != "fixed"){
13124         this.el.setStyle("position", "relative");
13125     }
13126     if(!this.handles){ // no handles passed, must be legacy style
13127         this.handles = 's,e,se';
13128         if(this.multiDirectional){
13129             this.handles += ',n,w';
13130         }
13131     }
13132     if(this.handles == "all"){
13133         this.handles = "n s e w ne nw se sw";
13134     }
13135     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13136     var ps = Roo.Resizable.positions;
13137     for(var i = 0, len = hs.length; i < len; i++){
13138         if(hs[i] && ps[hs[i]]){
13139             var pos = ps[hs[i]];
13140             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13141         }
13142     }
13143     // legacy
13144     this.corner = this.southeast;
13145     
13146     // updateBox = the box can move..
13147     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13148         this.updateBox = true;
13149     }
13150
13151     this.activeHandle = null;
13152
13153     if(this.resizeChild){
13154         if(typeof this.resizeChild == "boolean"){
13155             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13156         }else{
13157             this.resizeChild = Roo.get(this.resizeChild, true);
13158         }
13159     }
13160     
13161     if(this.adjustments == "auto"){
13162         var rc = this.resizeChild;
13163         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13164         if(rc && (hw || hn)){
13165             rc.position("relative");
13166             rc.setLeft(hw ? hw.el.getWidth() : 0);
13167             rc.setTop(hn ? hn.el.getHeight() : 0);
13168         }
13169         this.adjustments = [
13170             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13171             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13172         ];
13173     }
13174
13175     if(this.draggable){
13176         this.dd = this.dynamic ?
13177             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13178         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13179     }
13180
13181     // public events
13182     this.addEvents({
13183         /**
13184          * @event beforeresize
13185          * Fired before resize is allowed. Set enabled to false to cancel resize.
13186          * @param {Roo.Resizable} this
13187          * @param {Roo.EventObject} e The mousedown event
13188          */
13189         "beforeresize" : true,
13190         /**
13191          * @event resize
13192          * Fired after a resize.
13193          * @param {Roo.Resizable} this
13194          * @param {Number} width The new width
13195          * @param {Number} height The new height
13196          * @param {Roo.EventObject} e The mouseup event
13197          */
13198         "resize" : true
13199     });
13200
13201     if(this.width !== null && this.height !== null){
13202         this.resizeTo(this.width, this.height);
13203     }else{
13204         this.updateChildSize();
13205     }
13206     if(Roo.isIE){
13207         this.el.dom.style.zoom = 1;
13208     }
13209     Roo.Resizable.superclass.constructor.call(this);
13210 };
13211
13212 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13213         resizeChild : false,
13214         adjustments : [0, 0],
13215         minWidth : 5,
13216         minHeight : 5,
13217         maxWidth : 10000,
13218         maxHeight : 10000,
13219         enabled : true,
13220         animate : false,
13221         duration : .35,
13222         dynamic : false,
13223         handles : false,
13224         multiDirectional : false,
13225         disableTrackOver : false,
13226         easing : 'easeOutStrong',
13227         widthIncrement : 0,
13228         heightIncrement : 0,
13229         pinned : false,
13230         width : null,
13231         height : null,
13232         preserveRatio : false,
13233         transparent: false,
13234         minX: 0,
13235         minY: 0,
13236         draggable: false,
13237
13238         /**
13239          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13240          */
13241         constrainTo: undefined,
13242         /**
13243          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13244          */
13245         resizeRegion: undefined,
13246
13247
13248     /**
13249      * Perform a manual resize
13250      * @param {Number} width
13251      * @param {Number} height
13252      */
13253     resizeTo : function(width, height){
13254         this.el.setSize(width, height);
13255         this.updateChildSize();
13256         this.fireEvent("resize", this, width, height, null);
13257     },
13258
13259     // private
13260     startSizing : function(e, handle){
13261         this.fireEvent("beforeresize", this, e);
13262         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13263
13264             if(!this.overlay){
13265                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13266                 this.overlay.unselectable();
13267                 this.overlay.enableDisplayMode("block");
13268                 this.overlay.on("mousemove", this.onMouseMove, this);
13269                 this.overlay.on("mouseup", this.onMouseUp, this);
13270             }
13271             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13272
13273             this.resizing = true;
13274             this.startBox = this.el.getBox();
13275             this.startPoint = e.getXY();
13276             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13277                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13278
13279             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13280             this.overlay.show();
13281
13282             if(this.constrainTo) {
13283                 var ct = Roo.get(this.constrainTo);
13284                 this.resizeRegion = ct.getRegion().adjust(
13285                     ct.getFrameWidth('t'),
13286                     ct.getFrameWidth('l'),
13287                     -ct.getFrameWidth('b'),
13288                     -ct.getFrameWidth('r')
13289                 );
13290             }
13291
13292             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13293             this.proxy.show();
13294             this.proxy.setBox(this.startBox);
13295             if(!this.dynamic){
13296                 this.proxy.setStyle('visibility', 'visible');
13297             }
13298         }
13299     },
13300
13301     // private
13302     onMouseDown : function(handle, e){
13303         if(this.enabled){
13304             e.stopEvent();
13305             this.activeHandle = handle;
13306             this.startSizing(e, handle);
13307         }
13308     },
13309
13310     // private
13311     onMouseUp : function(e){
13312         var size = this.resizeElement();
13313         this.resizing = false;
13314         this.handleOut();
13315         this.overlay.hide();
13316         this.proxy.hide();
13317         this.fireEvent("resize", this, size.width, size.height, e);
13318     },
13319
13320     // private
13321     updateChildSize : function(){
13322         if(this.resizeChild){
13323             var el = this.el;
13324             var child = this.resizeChild;
13325             var adj = this.adjustments;
13326             if(el.dom.offsetWidth){
13327                 var b = el.getSize(true);
13328                 child.setSize(b.width+adj[0], b.height+adj[1]);
13329             }
13330             // Second call here for IE
13331             // The first call enables instant resizing and
13332             // the second call corrects scroll bars if they
13333             // exist
13334             if(Roo.isIE){
13335                 setTimeout(function(){
13336                     if(el.dom.offsetWidth){
13337                         var b = el.getSize(true);
13338                         child.setSize(b.width+adj[0], b.height+adj[1]);
13339                     }
13340                 }, 10);
13341             }
13342         }
13343     },
13344
13345     // private
13346     snap : function(value, inc, min){
13347         if(!inc || !value) return value;
13348         var newValue = value;
13349         var m = value % inc;
13350         if(m > 0){
13351             if(m > (inc/2)){
13352                 newValue = value + (inc-m);
13353             }else{
13354                 newValue = value - m;
13355             }
13356         }
13357         return Math.max(min, newValue);
13358     },
13359
13360     // private
13361     resizeElement : function(){
13362         var box = this.proxy.getBox();
13363         if(this.updateBox){
13364             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13365         }else{
13366             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13367         }
13368         this.updateChildSize();
13369         if(!this.dynamic){
13370             this.proxy.hide();
13371         }
13372         return box;
13373     },
13374
13375     // private
13376     constrain : function(v, diff, m, mx){
13377         if(v - diff < m){
13378             diff = v - m;
13379         }else if(v - diff > mx){
13380             diff = mx - v;
13381         }
13382         return diff;
13383     },
13384
13385     // private
13386     onMouseMove : function(e){
13387         if(this.enabled){
13388             try{// try catch so if something goes wrong the user doesn't get hung
13389
13390             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13391                 return;
13392             }
13393
13394             //var curXY = this.startPoint;
13395             var curSize = this.curSize || this.startBox;
13396             var x = this.startBox.x, y = this.startBox.y;
13397             var ox = x, oy = y;
13398             var w = curSize.width, h = curSize.height;
13399             var ow = w, oh = h;
13400             var mw = this.minWidth, mh = this.minHeight;
13401             var mxw = this.maxWidth, mxh = this.maxHeight;
13402             var wi = this.widthIncrement;
13403             var hi = this.heightIncrement;
13404
13405             var eventXY = e.getXY();
13406             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13407             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13408
13409             var pos = this.activeHandle.position;
13410
13411             switch(pos){
13412                 case "east":
13413                     w += diffX;
13414                     w = Math.min(Math.max(mw, w), mxw);
13415                     break;
13416              
13417                 case "south":
13418                     h += diffY;
13419                     h = Math.min(Math.max(mh, h), mxh);
13420                     break;
13421                 case "southeast":
13422                     w += diffX;
13423                     h += diffY;
13424                     w = Math.min(Math.max(mw, w), mxw);
13425                     h = Math.min(Math.max(mh, h), mxh);
13426                     break;
13427                 case "north":
13428                     diffY = this.constrain(h, diffY, mh, mxh);
13429                     y += diffY;
13430                     h -= diffY;
13431                     break;
13432                 case "hdrag":
13433                     
13434                     if (wi) {
13435                         var adiffX = Math.abs(diffX);
13436                         var sub = (adiffX % wi); // how much 
13437                         if (sub > (wi/2)) { // far enough to snap
13438                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13439                         } else {
13440                             // remove difference.. 
13441                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13442                         }
13443                     }
13444                     x += diffX;
13445                     x = Math.max(this.minX, x);
13446                     break;
13447                 case "west":
13448                     diffX = this.constrain(w, diffX, mw, mxw);
13449                     x += diffX;
13450                     w -= diffX;
13451                     break;
13452                 case "northeast":
13453                     w += diffX;
13454                     w = Math.min(Math.max(mw, w), mxw);
13455                     diffY = this.constrain(h, diffY, mh, mxh);
13456                     y += diffY;
13457                     h -= diffY;
13458                     break;
13459                 case "northwest":
13460                     diffX = this.constrain(w, diffX, mw, mxw);
13461                     diffY = this.constrain(h, diffY, mh, mxh);
13462                     y += diffY;
13463                     h -= diffY;
13464                     x += diffX;
13465                     w -= diffX;
13466                     break;
13467                case "southwest":
13468                     diffX = this.constrain(w, diffX, mw, mxw);
13469                     h += diffY;
13470                     h = Math.min(Math.max(mh, h), mxh);
13471                     x += diffX;
13472                     w -= diffX;
13473                     break;
13474             }
13475
13476             var sw = this.snap(w, wi, mw);
13477             var sh = this.snap(h, hi, mh);
13478             if(sw != w || sh != h){
13479                 switch(pos){
13480                     case "northeast":
13481                         y -= sh - h;
13482                     break;
13483                     case "north":
13484                         y -= sh - h;
13485                         break;
13486                     case "southwest":
13487                         x -= sw - w;
13488                     break;
13489                     case "west":
13490                         x -= sw - w;
13491                         break;
13492                     case "northwest":
13493                         x -= sw - w;
13494                         y -= sh - h;
13495                     break;
13496                 }
13497                 w = sw;
13498                 h = sh;
13499             }
13500
13501             if(this.preserveRatio){
13502                 switch(pos){
13503                     case "southeast":
13504                     case "east":
13505                         h = oh * (w/ow);
13506                         h = Math.min(Math.max(mh, h), mxh);
13507                         w = ow * (h/oh);
13508                        break;
13509                     case "south":
13510                         w = ow * (h/oh);
13511                         w = Math.min(Math.max(mw, w), mxw);
13512                         h = oh * (w/ow);
13513                         break;
13514                     case "northeast":
13515                         w = ow * (h/oh);
13516                         w = Math.min(Math.max(mw, w), mxw);
13517                         h = oh * (w/ow);
13518                     break;
13519                     case "north":
13520                         var tw = w;
13521                         w = ow * (h/oh);
13522                         w = Math.min(Math.max(mw, w), mxw);
13523                         h = oh * (w/ow);
13524                         x += (tw - w) / 2;
13525                         break;
13526                     case "southwest":
13527                         h = oh * (w/ow);
13528                         h = Math.min(Math.max(mh, h), mxh);
13529                         var tw = w;
13530                         w = ow * (h/oh);
13531                         x += tw - w;
13532                         break;
13533                     case "west":
13534                         var th = h;
13535                         h = oh * (w/ow);
13536                         h = Math.min(Math.max(mh, h), mxh);
13537                         y += (th - h) / 2;
13538                         var tw = w;
13539                         w = ow * (h/oh);
13540                         x += tw - w;
13541                        break;
13542                     case "northwest":
13543                         var tw = w;
13544                         var th = h;
13545                         h = oh * (w/ow);
13546                         h = Math.min(Math.max(mh, h), mxh);
13547                         w = ow * (h/oh);
13548                         y += th - h;
13549                         x += tw - w;
13550                        break;
13551
13552                 }
13553             }
13554             if (pos == 'hdrag') {
13555                 w = ow;
13556             }
13557             this.proxy.setBounds(x, y, w, h);
13558             if(this.dynamic){
13559                 this.resizeElement();
13560             }
13561             }catch(e){}
13562         }
13563     },
13564
13565     // private
13566     handleOver : function(){
13567         if(this.enabled){
13568             this.el.addClass("x-resizable-over");
13569         }
13570     },
13571
13572     // private
13573     handleOut : function(){
13574         if(!this.resizing){
13575             this.el.removeClass("x-resizable-over");
13576         }
13577     },
13578
13579     /**
13580      * Returns the element this component is bound to.
13581      * @return {Roo.Element}
13582      */
13583     getEl : function(){
13584         return this.el;
13585     },
13586
13587     /**
13588      * Returns the resizeChild element (or null).
13589      * @return {Roo.Element}
13590      */
13591     getResizeChild : function(){
13592         return this.resizeChild;
13593     },
13594
13595     /**
13596      * Destroys this resizable. If the element was wrapped and
13597      * removeEl is not true then the element remains.
13598      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13599      */
13600     destroy : function(removeEl){
13601         this.proxy.remove();
13602         if(this.overlay){
13603             this.overlay.removeAllListeners();
13604             this.overlay.remove();
13605         }
13606         var ps = Roo.Resizable.positions;
13607         for(var k in ps){
13608             if(typeof ps[k] != "function" && this[ps[k]]){
13609                 var h = this[ps[k]];
13610                 h.el.removeAllListeners();
13611                 h.el.remove();
13612             }
13613         }
13614         if(removeEl){
13615             this.el.update("");
13616             this.el.remove();
13617         }
13618     }
13619 });
13620
13621 // private
13622 // hash to map config positions to true positions
13623 Roo.Resizable.positions = {
13624     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13625     hd: "hdrag"
13626 };
13627
13628 // private
13629 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13630     if(!this.tpl){
13631         // only initialize the template if resizable is used
13632         var tpl = Roo.DomHelper.createTemplate(
13633             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13634         );
13635         tpl.compile();
13636         Roo.Resizable.Handle.prototype.tpl = tpl;
13637     }
13638     this.position = pos;
13639     this.rz = rz;
13640     // show north drag fro topdra
13641     var handlepos = pos == 'hdrag' ? 'north' : pos;
13642     
13643     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13644     if (pos == 'hdrag') {
13645         this.el.setStyle('cursor', 'pointer');
13646     }
13647     this.el.unselectable();
13648     if(transparent){
13649         this.el.setOpacity(0);
13650     }
13651     this.el.on("mousedown", this.onMouseDown, this);
13652     if(!disableTrackOver){
13653         this.el.on("mouseover", this.onMouseOver, this);
13654         this.el.on("mouseout", this.onMouseOut, this);
13655     }
13656 };
13657
13658 // private
13659 Roo.Resizable.Handle.prototype = {
13660     afterResize : function(rz){
13661         // do nothing
13662     },
13663     // private
13664     onMouseDown : function(e){
13665         this.rz.onMouseDown(this, e);
13666     },
13667     // private
13668     onMouseOver : function(e){
13669         this.rz.handleOver(this, e);
13670     },
13671     // private
13672     onMouseOut : function(e){
13673         this.rz.handleOut(this, e);
13674     }
13675 };/*
13676  * Based on:
13677  * Ext JS Library 1.1.1
13678  * Copyright(c) 2006-2007, Ext JS, LLC.
13679  *
13680  * Originally Released Under LGPL - original licence link has changed is not relivant.
13681  *
13682  * Fork - LGPL
13683  * <script type="text/javascript">
13684  */
13685
13686 /**
13687  * @class Roo.Editor
13688  * @extends Roo.Component
13689  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13690  * @constructor
13691  * Create a new Editor
13692  * @param {Roo.form.Field} field The Field object (or descendant)
13693  * @param {Object} config The config object
13694  */
13695 Roo.Editor = function(field, config){
13696     Roo.Editor.superclass.constructor.call(this, config);
13697     this.field = field;
13698     this.addEvents({
13699         /**
13700              * @event beforestartedit
13701              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13702              * false from the handler of this event.
13703              * @param {Editor} this
13704              * @param {Roo.Element} boundEl The underlying element bound to this editor
13705              * @param {Mixed} value The field value being set
13706              */
13707         "beforestartedit" : true,
13708         /**
13709              * @event startedit
13710              * Fires when this editor is displayed
13711              * @param {Roo.Element} boundEl The underlying element bound to this editor
13712              * @param {Mixed} value The starting field value
13713              */
13714         "startedit" : true,
13715         /**
13716              * @event beforecomplete
13717              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13718              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13719              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13720              * event will not fire since no edit actually occurred.
13721              * @param {Editor} this
13722              * @param {Mixed} value The current field value
13723              * @param {Mixed} startValue The original field value
13724              */
13725         "beforecomplete" : true,
13726         /**
13727              * @event complete
13728              * Fires after editing is complete and any changed value has been written to the underlying field.
13729              * @param {Editor} this
13730              * @param {Mixed} value The current field value
13731              * @param {Mixed} startValue The original field value
13732              */
13733         "complete" : true,
13734         /**
13735          * @event specialkey
13736          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13737          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13738          * @param {Roo.form.Field} this
13739          * @param {Roo.EventObject} e The event object
13740          */
13741         "specialkey" : true
13742     });
13743 };
13744
13745 Roo.extend(Roo.Editor, Roo.Component, {
13746     /**
13747      * @cfg {Boolean/String} autosize
13748      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13749      * or "height" to adopt the height only (defaults to false)
13750      */
13751     /**
13752      * @cfg {Boolean} revertInvalid
13753      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13754      * validation fails (defaults to true)
13755      */
13756     /**
13757      * @cfg {Boolean} ignoreNoChange
13758      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13759      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13760      * will never be ignored.
13761      */
13762     /**
13763      * @cfg {Boolean} hideEl
13764      * False to keep the bound element visible while the editor is displayed (defaults to true)
13765      */
13766     /**
13767      * @cfg {Mixed} value
13768      * The data value of the underlying field (defaults to "")
13769      */
13770     value : "",
13771     /**
13772      * @cfg {String} alignment
13773      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13774      */
13775     alignment: "c-c?",
13776     /**
13777      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13778      * for bottom-right shadow (defaults to "frame")
13779      */
13780     shadow : "frame",
13781     /**
13782      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13783      */
13784     constrain : false,
13785     /**
13786      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13787      */
13788     completeOnEnter : false,
13789     /**
13790      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13791      */
13792     cancelOnEsc : false,
13793     /**
13794      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13795      */
13796     updateEl : false,
13797
13798     // private
13799     onRender : function(ct, position){
13800         this.el = new Roo.Layer({
13801             shadow: this.shadow,
13802             cls: "x-editor",
13803             parentEl : ct,
13804             shim : this.shim,
13805             shadowOffset:4,
13806             id: this.id,
13807             constrain: this.constrain
13808         });
13809         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13810         if(this.field.msgTarget != 'title'){
13811             this.field.msgTarget = 'qtip';
13812         }
13813         this.field.render(this.el);
13814         if(Roo.isGecko){
13815             this.field.el.dom.setAttribute('autocomplete', 'off');
13816         }
13817         this.field.on("specialkey", this.onSpecialKey, this);
13818         if(this.swallowKeys){
13819             this.field.el.swallowEvent(['keydown','keypress']);
13820         }
13821         this.field.show();
13822         this.field.on("blur", this.onBlur, this);
13823         if(this.field.grow){
13824             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13825         }
13826     },
13827
13828     onSpecialKey : function(field, e){
13829         //Roo.log('editor onSpecialKey');
13830         if(this.completeOnEnter && e.getKey() == e.ENTER){
13831             e.stopEvent();
13832             this.completeEdit();
13833         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13834             this.cancelEdit();
13835         }else{
13836             this.fireEvent('specialkey', field, e);
13837         }
13838     },
13839
13840     /**
13841      * Starts the editing process and shows the editor.
13842      * @param {String/HTMLElement/Element} el The element to edit
13843      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13844       * to the innerHTML of el.
13845      */
13846     startEdit : function(el, value){
13847         if(this.editing){
13848             this.completeEdit();
13849         }
13850         this.boundEl = Roo.get(el);
13851         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13852         if(!this.rendered){
13853             this.render(this.parentEl || document.body);
13854         }
13855         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13856             return;
13857         }
13858         this.startValue = v;
13859         this.field.setValue(v);
13860         if(this.autoSize){
13861             var sz = this.boundEl.getSize();
13862             switch(this.autoSize){
13863                 case "width":
13864                 this.setSize(sz.width,  "");
13865                 break;
13866                 case "height":
13867                 this.setSize("",  sz.height);
13868                 break;
13869                 default:
13870                 this.setSize(sz.width,  sz.height);
13871             }
13872         }
13873         this.el.alignTo(this.boundEl, this.alignment);
13874         this.editing = true;
13875         if(Roo.QuickTips){
13876             Roo.QuickTips.disable();
13877         }
13878         this.show();
13879     },
13880
13881     /**
13882      * Sets the height and width of this editor.
13883      * @param {Number} width The new width
13884      * @param {Number} height The new height
13885      */
13886     setSize : function(w, h){
13887         this.field.setSize(w, h);
13888         if(this.el){
13889             this.el.sync();
13890         }
13891     },
13892
13893     /**
13894      * Realigns the editor to the bound field based on the current alignment config value.
13895      */
13896     realign : function(){
13897         this.el.alignTo(this.boundEl, this.alignment);
13898     },
13899
13900     /**
13901      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13902      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13903      */
13904     completeEdit : function(remainVisible){
13905         if(!this.editing){
13906             return;
13907         }
13908         var v = this.getValue();
13909         if(this.revertInvalid !== false && !this.field.isValid()){
13910             v = this.startValue;
13911             this.cancelEdit(true);
13912         }
13913         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13914             this.editing = false;
13915             this.hide();
13916             return;
13917         }
13918         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13919             this.editing = false;
13920             if(this.updateEl && this.boundEl){
13921                 this.boundEl.update(v);
13922             }
13923             if(remainVisible !== true){
13924                 this.hide();
13925             }
13926             this.fireEvent("complete", this, v, this.startValue);
13927         }
13928     },
13929
13930     // private
13931     onShow : function(){
13932         this.el.show();
13933         if(this.hideEl !== false){
13934             this.boundEl.hide();
13935         }
13936         this.field.show();
13937         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
13938             this.fixIEFocus = true;
13939             this.deferredFocus.defer(50, this);
13940         }else{
13941             this.field.focus();
13942         }
13943         this.fireEvent("startedit", this.boundEl, this.startValue);
13944     },
13945
13946     deferredFocus : function(){
13947         if(this.editing){
13948             this.field.focus();
13949         }
13950     },
13951
13952     /**
13953      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
13954      * reverted to the original starting value.
13955      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
13956      * cancel (defaults to false)
13957      */
13958     cancelEdit : function(remainVisible){
13959         if(this.editing){
13960             this.setValue(this.startValue);
13961             if(remainVisible !== true){
13962                 this.hide();
13963             }
13964         }
13965     },
13966
13967     // private
13968     onBlur : function(){
13969         if(this.allowBlur !== true && this.editing){
13970             this.completeEdit();
13971         }
13972     },
13973
13974     // private
13975     onHide : function(){
13976         if(this.editing){
13977             this.completeEdit();
13978             return;
13979         }
13980         this.field.blur();
13981         if(this.field.collapse){
13982             this.field.collapse();
13983         }
13984         this.el.hide();
13985         if(this.hideEl !== false){
13986             this.boundEl.show();
13987         }
13988         if(Roo.QuickTips){
13989             Roo.QuickTips.enable();
13990         }
13991     },
13992
13993     /**
13994      * Sets the data value of the editor
13995      * @param {Mixed} value Any valid value supported by the underlying field
13996      */
13997     setValue : function(v){
13998         this.field.setValue(v);
13999     },
14000
14001     /**
14002      * Gets the data value of the editor
14003      * @return {Mixed} The data value
14004      */
14005     getValue : function(){
14006         return this.field.getValue();
14007     }
14008 });/*
14009  * Based on:
14010  * Ext JS Library 1.1.1
14011  * Copyright(c) 2006-2007, Ext JS, LLC.
14012  *
14013  * Originally Released Under LGPL - original licence link has changed is not relivant.
14014  *
14015  * Fork - LGPL
14016  * <script type="text/javascript">
14017  */
14018  
14019 /**
14020  * @class Roo.BasicDialog
14021  * @extends Roo.util.Observable
14022  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14023  * <pre><code>
14024 var dlg = new Roo.BasicDialog("my-dlg", {
14025     height: 200,
14026     width: 300,
14027     minHeight: 100,
14028     minWidth: 150,
14029     modal: true,
14030     proxyDrag: true,
14031     shadow: true
14032 });
14033 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14034 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14035 dlg.addButton('Cancel', dlg.hide, dlg);
14036 dlg.show();
14037 </code></pre>
14038   <b>A Dialog should always be a direct child of the body element.</b>
14039  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14040  * @cfg {String} title Default text to display in the title bar (defaults to null)
14041  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14042  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14043  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14044  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14045  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14046  * (defaults to null with no animation)
14047  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14048  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14049  * property for valid values (defaults to 'all')
14050  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14051  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14052  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14053  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14054  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14055  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14056  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14057  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14058  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14059  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14060  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14061  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14062  * draggable = true (defaults to false)
14063  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14064  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14065  * shadow (defaults to false)
14066  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14067  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14068  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14069  * @cfg {Array} buttons Array of buttons
14070  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14071  * @constructor
14072  * Create a new BasicDialog.
14073  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14074  * @param {Object} config Configuration options
14075  */
14076 Roo.BasicDialog = function(el, config){
14077     this.el = Roo.get(el);
14078     var dh = Roo.DomHelper;
14079     if(!this.el && config && config.autoCreate){
14080         if(typeof config.autoCreate == "object"){
14081             if(!config.autoCreate.id){
14082                 config.autoCreate.id = el;
14083             }
14084             this.el = dh.append(document.body,
14085                         config.autoCreate, true);
14086         }else{
14087             this.el = dh.append(document.body,
14088                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14089         }
14090     }
14091     el = this.el;
14092     el.setDisplayed(true);
14093     el.hide = this.hideAction;
14094     this.id = el.id;
14095     el.addClass("x-dlg");
14096
14097     Roo.apply(this, config);
14098
14099     this.proxy = el.createProxy("x-dlg-proxy");
14100     this.proxy.hide = this.hideAction;
14101     this.proxy.setOpacity(.5);
14102     this.proxy.hide();
14103
14104     if(config.width){
14105         el.setWidth(config.width);
14106     }
14107     if(config.height){
14108         el.setHeight(config.height);
14109     }
14110     this.size = el.getSize();
14111     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14112         this.xy = [config.x,config.y];
14113     }else{
14114         this.xy = el.getCenterXY(true);
14115     }
14116     /** The header element @type Roo.Element */
14117     this.header = el.child("> .x-dlg-hd");
14118     /** The body element @type Roo.Element */
14119     this.body = el.child("> .x-dlg-bd");
14120     /** The footer element @type Roo.Element */
14121     this.footer = el.child("> .x-dlg-ft");
14122
14123     if(!this.header){
14124         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14125     }
14126     if(!this.body){
14127         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14128     }
14129
14130     this.header.unselectable();
14131     if(this.title){
14132         this.header.update(this.title);
14133     }
14134     // this element allows the dialog to be focused for keyboard event
14135     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14136     this.focusEl.swallowEvent("click", true);
14137
14138     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14139
14140     // wrap the body and footer for special rendering
14141     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14142     if(this.footer){
14143         this.bwrap.dom.appendChild(this.footer.dom);
14144     }
14145
14146     this.bg = this.el.createChild({
14147         tag: "div", cls:"x-dlg-bg",
14148         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14149     });
14150     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14151
14152
14153     if(this.autoScroll !== false && !this.autoTabs){
14154         this.body.setStyle("overflow", "auto");
14155     }
14156
14157     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14158
14159     if(this.closable !== false){
14160         this.el.addClass("x-dlg-closable");
14161         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14162         this.close.on("click", this.closeClick, this);
14163         this.close.addClassOnOver("x-dlg-close-over");
14164     }
14165     if(this.collapsible !== false){
14166         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14167         this.collapseBtn.on("click", this.collapseClick, this);
14168         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14169         this.header.on("dblclick", this.collapseClick, this);
14170     }
14171     if(this.resizable !== false){
14172         this.el.addClass("x-dlg-resizable");
14173         this.resizer = new Roo.Resizable(el, {
14174             minWidth: this.minWidth || 80,
14175             minHeight:this.minHeight || 80,
14176             handles: this.resizeHandles || "all",
14177             pinned: true
14178         });
14179         this.resizer.on("beforeresize", this.beforeResize, this);
14180         this.resizer.on("resize", this.onResize, this);
14181     }
14182     if(this.draggable !== false){
14183         el.addClass("x-dlg-draggable");
14184         if (!this.proxyDrag) {
14185             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14186         }
14187         else {
14188             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14189         }
14190         dd.setHandleElId(this.header.id);
14191         dd.endDrag = this.endMove.createDelegate(this);
14192         dd.startDrag = this.startMove.createDelegate(this);
14193         dd.onDrag = this.onDrag.createDelegate(this);
14194         dd.scroll = false;
14195         this.dd = dd;
14196     }
14197     if(this.modal){
14198         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14199         this.mask.enableDisplayMode("block");
14200         this.mask.hide();
14201         this.el.addClass("x-dlg-modal");
14202     }
14203     if(this.shadow){
14204         this.shadow = new Roo.Shadow({
14205             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14206             offset : this.shadowOffset
14207         });
14208     }else{
14209         this.shadowOffset = 0;
14210     }
14211     if(Roo.useShims && this.shim !== false){
14212         this.shim = this.el.createShim();
14213         this.shim.hide = this.hideAction;
14214         this.shim.hide();
14215     }else{
14216         this.shim = false;
14217     }
14218     if(this.autoTabs){
14219         this.initTabs();
14220     }
14221     if (this.buttons) { 
14222         var bts= this.buttons;
14223         this.buttons = [];
14224         Roo.each(bts, function(b) {
14225             this.addButton(b);
14226         }, this);
14227     }
14228     
14229     
14230     this.addEvents({
14231         /**
14232          * @event keydown
14233          * Fires when a key is pressed
14234          * @param {Roo.BasicDialog} this
14235          * @param {Roo.EventObject} e
14236          */
14237         "keydown" : true,
14238         /**
14239          * @event move
14240          * Fires when this dialog is moved by the user.
14241          * @param {Roo.BasicDialog} this
14242          * @param {Number} x The new page X
14243          * @param {Number} y The new page Y
14244          */
14245         "move" : true,
14246         /**
14247          * @event resize
14248          * Fires when this dialog is resized by the user.
14249          * @param {Roo.BasicDialog} this
14250          * @param {Number} width The new width
14251          * @param {Number} height The new height
14252          */
14253         "resize" : true,
14254         /**
14255          * @event beforehide
14256          * Fires before this dialog is hidden.
14257          * @param {Roo.BasicDialog} this
14258          */
14259         "beforehide" : true,
14260         /**
14261          * @event hide
14262          * Fires when this dialog is hidden.
14263          * @param {Roo.BasicDialog} this
14264          */
14265         "hide" : true,
14266         /**
14267          * @event beforeshow
14268          * Fires before this dialog is shown.
14269          * @param {Roo.BasicDialog} this
14270          */
14271         "beforeshow" : true,
14272         /**
14273          * @event show
14274          * Fires when this dialog is shown.
14275          * @param {Roo.BasicDialog} this
14276          */
14277         "show" : true
14278     });
14279     el.on("keydown", this.onKeyDown, this);
14280     el.on("mousedown", this.toFront, this);
14281     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14282     this.el.hide();
14283     Roo.DialogManager.register(this);
14284     Roo.BasicDialog.superclass.constructor.call(this);
14285 };
14286
14287 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14288     shadowOffset: Roo.isIE ? 6 : 5,
14289     minHeight: 80,
14290     minWidth: 200,
14291     minButtonWidth: 75,
14292     defaultButton: null,
14293     buttonAlign: "right",
14294     tabTag: 'div',
14295     firstShow: true,
14296
14297     /**
14298      * Sets the dialog title text
14299      * @param {String} text The title text to display
14300      * @return {Roo.BasicDialog} this
14301      */
14302     setTitle : function(text){
14303         this.header.update(text);
14304         return this;
14305     },
14306
14307     // private
14308     closeClick : function(){
14309         this.hide();
14310     },
14311
14312     // private
14313     collapseClick : function(){
14314         this[this.collapsed ? "expand" : "collapse"]();
14315     },
14316
14317     /**
14318      * Collapses the dialog to its minimized state (only the title bar is visible).
14319      * Equivalent to the user clicking the collapse dialog button.
14320      */
14321     collapse : function(){
14322         if(!this.collapsed){
14323             this.collapsed = true;
14324             this.el.addClass("x-dlg-collapsed");
14325             this.restoreHeight = this.el.getHeight();
14326             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14327         }
14328     },
14329
14330     /**
14331      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14332      * clicking the expand dialog button.
14333      */
14334     expand : function(){
14335         if(this.collapsed){
14336             this.collapsed = false;
14337             this.el.removeClass("x-dlg-collapsed");
14338             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14339         }
14340     },
14341
14342     /**
14343      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14344      * @return {Roo.TabPanel} The tabs component
14345      */
14346     initTabs : function(){
14347         var tabs = this.getTabs();
14348         while(tabs.getTab(0)){
14349             tabs.removeTab(0);
14350         }
14351         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14352             var dom = el.dom;
14353             tabs.addTab(Roo.id(dom), dom.title);
14354             dom.title = "";
14355         });
14356         tabs.activate(0);
14357         return tabs;
14358     },
14359
14360     // private
14361     beforeResize : function(){
14362         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14363     },
14364
14365     // private
14366     onResize : function(){
14367         this.refreshSize();
14368         this.syncBodyHeight();
14369         this.adjustAssets();
14370         this.focus();
14371         this.fireEvent("resize", this, this.size.width, this.size.height);
14372     },
14373
14374     // private
14375     onKeyDown : function(e){
14376         if(this.isVisible()){
14377             this.fireEvent("keydown", this, e);
14378         }
14379     },
14380
14381     /**
14382      * Resizes the dialog.
14383      * @param {Number} width
14384      * @param {Number} height
14385      * @return {Roo.BasicDialog} this
14386      */
14387     resizeTo : function(width, height){
14388         this.el.setSize(width, height);
14389         this.size = {width: width, height: height};
14390         this.syncBodyHeight();
14391         if(this.fixedcenter){
14392             this.center();
14393         }
14394         if(this.isVisible()){
14395             this.constrainXY();
14396             this.adjustAssets();
14397         }
14398         this.fireEvent("resize", this, width, height);
14399         return this;
14400     },
14401
14402
14403     /**
14404      * Resizes the dialog to fit the specified content size.
14405      * @param {Number} width
14406      * @param {Number} height
14407      * @return {Roo.BasicDialog} this
14408      */
14409     setContentSize : function(w, h){
14410         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14411         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14412         //if(!this.el.isBorderBox()){
14413             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14414             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14415         //}
14416         if(this.tabs){
14417             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14418             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14419         }
14420         this.resizeTo(w, h);
14421         return this;
14422     },
14423
14424     /**
14425      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14426      * executed in response to a particular key being pressed while the dialog is active.
14427      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14428      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14429      * @param {Function} fn The function to call
14430      * @param {Object} scope (optional) The scope of the function
14431      * @return {Roo.BasicDialog} this
14432      */
14433     addKeyListener : function(key, fn, scope){
14434         var keyCode, shift, ctrl, alt;
14435         if(typeof key == "object" && !(key instanceof Array)){
14436             keyCode = key["key"];
14437             shift = key["shift"];
14438             ctrl = key["ctrl"];
14439             alt = key["alt"];
14440         }else{
14441             keyCode = key;
14442         }
14443         var handler = function(dlg, e){
14444             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14445                 var k = e.getKey();
14446                 if(keyCode instanceof Array){
14447                     for(var i = 0, len = keyCode.length; i < len; i++){
14448                         if(keyCode[i] == k){
14449                           fn.call(scope || window, dlg, k, e);
14450                           return;
14451                         }
14452                     }
14453                 }else{
14454                     if(k == keyCode){
14455                         fn.call(scope || window, dlg, k, e);
14456                     }
14457                 }
14458             }
14459         };
14460         this.on("keydown", handler);
14461         return this;
14462     },
14463
14464     /**
14465      * Returns the TabPanel component (creates it if it doesn't exist).
14466      * Note: If you wish to simply check for the existence of tabs without creating them,
14467      * check for a null 'tabs' property.
14468      * @return {Roo.TabPanel} The tabs component
14469      */
14470     getTabs : function(){
14471         if(!this.tabs){
14472             this.el.addClass("x-dlg-auto-tabs");
14473             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14474             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14475         }
14476         return this.tabs;
14477     },
14478
14479     /**
14480      * Adds a button to the footer section of the dialog.
14481      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14482      * object or a valid Roo.DomHelper element config
14483      * @param {Function} handler The function called when the button is clicked
14484      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14485      * @return {Roo.Button} The new button
14486      */
14487     addButton : function(config, handler, scope){
14488         var dh = Roo.DomHelper;
14489         if(!this.footer){
14490             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14491         }
14492         if(!this.btnContainer){
14493             var tb = this.footer.createChild({
14494
14495                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14496                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14497             }, null, true);
14498             this.btnContainer = tb.firstChild.firstChild.firstChild;
14499         }
14500         var bconfig = {
14501             handler: handler,
14502             scope: scope,
14503             minWidth: this.minButtonWidth,
14504             hideParent:true
14505         };
14506         if(typeof config == "string"){
14507             bconfig.text = config;
14508         }else{
14509             if(config.tag){
14510                 bconfig.dhconfig = config;
14511             }else{
14512                 Roo.apply(bconfig, config);
14513             }
14514         }
14515         var fc = false;
14516         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14517             bconfig.position = Math.max(0, bconfig.position);
14518             fc = this.btnContainer.childNodes[bconfig.position];
14519         }
14520          
14521         var btn = new Roo.Button(
14522             fc ? 
14523                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14524                 : this.btnContainer.appendChild(document.createElement("td")),
14525             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14526             bconfig
14527         );
14528         this.syncBodyHeight();
14529         if(!this.buttons){
14530             /**
14531              * Array of all the buttons that have been added to this dialog via addButton
14532              * @type Array
14533              */
14534             this.buttons = [];
14535         }
14536         this.buttons.push(btn);
14537         return btn;
14538     },
14539
14540     /**
14541      * Sets the default button to be focused when the dialog is displayed.
14542      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14543      * @return {Roo.BasicDialog} this
14544      */
14545     setDefaultButton : function(btn){
14546         this.defaultButton = btn;
14547         return this;
14548     },
14549
14550     // private
14551     getHeaderFooterHeight : function(safe){
14552         var height = 0;
14553         if(this.header){
14554            height += this.header.getHeight();
14555         }
14556         if(this.footer){
14557            var fm = this.footer.getMargins();
14558             height += (this.footer.getHeight()+fm.top+fm.bottom);
14559         }
14560         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14561         height += this.centerBg.getPadding("tb");
14562         return height;
14563     },
14564
14565     // private
14566     syncBodyHeight : function(){
14567         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14568         var height = this.size.height - this.getHeaderFooterHeight(false);
14569         bd.setHeight(height-bd.getMargins("tb"));
14570         var hh = this.header.getHeight();
14571         var h = this.size.height-hh;
14572         cb.setHeight(h);
14573         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14574         bw.setHeight(h-cb.getPadding("tb"));
14575         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14576         bd.setWidth(bw.getWidth(true));
14577         if(this.tabs){
14578             this.tabs.syncHeight();
14579             if(Roo.isIE){
14580                 this.tabs.el.repaint();
14581             }
14582         }
14583     },
14584
14585     /**
14586      * Restores the previous state of the dialog if Roo.state is configured.
14587      * @return {Roo.BasicDialog} this
14588      */
14589     restoreState : function(){
14590         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14591         if(box && box.width){
14592             this.xy = [box.x, box.y];
14593             this.resizeTo(box.width, box.height);
14594         }
14595         return this;
14596     },
14597
14598     // private
14599     beforeShow : function(){
14600         this.expand();
14601         if(this.fixedcenter){
14602             this.xy = this.el.getCenterXY(true);
14603         }
14604         if(this.modal){
14605             Roo.get(document.body).addClass("x-body-masked");
14606             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14607             this.mask.show();
14608         }
14609         this.constrainXY();
14610     },
14611
14612     // private
14613     animShow : function(){
14614         var b = Roo.get(this.animateTarget).getBox();
14615         this.proxy.setSize(b.width, b.height);
14616         this.proxy.setLocation(b.x, b.y);
14617         this.proxy.show();
14618         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14619                     true, .35, this.showEl.createDelegate(this));
14620     },
14621
14622     /**
14623      * Shows the dialog.
14624      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14625      * @return {Roo.BasicDialog} this
14626      */
14627     show : function(animateTarget){
14628         if (this.fireEvent("beforeshow", this) === false){
14629             return;
14630         }
14631         if(this.syncHeightBeforeShow){
14632             this.syncBodyHeight();
14633         }else if(this.firstShow){
14634             this.firstShow = false;
14635             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14636         }
14637         this.animateTarget = animateTarget || this.animateTarget;
14638         if(!this.el.isVisible()){
14639             this.beforeShow();
14640             if(this.animateTarget && Roo.get(this.animateTarget)){
14641                 this.animShow();
14642             }else{
14643                 this.showEl();
14644             }
14645         }
14646         return this;
14647     },
14648
14649     // private
14650     showEl : function(){
14651         this.proxy.hide();
14652         this.el.setXY(this.xy);
14653         this.el.show();
14654         this.adjustAssets(true);
14655         this.toFront();
14656         this.focus();
14657         // IE peekaboo bug - fix found by Dave Fenwick
14658         if(Roo.isIE){
14659             this.el.repaint();
14660         }
14661         this.fireEvent("show", this);
14662     },
14663
14664     /**
14665      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14666      * dialog itself will receive focus.
14667      */
14668     focus : function(){
14669         if(this.defaultButton){
14670             this.defaultButton.focus();
14671         }else{
14672             this.focusEl.focus();
14673         }
14674     },
14675
14676     // private
14677     constrainXY : function(){
14678         if(this.constraintoviewport !== false){
14679             if(!this.viewSize){
14680                 if(this.container){
14681                     var s = this.container.getSize();
14682                     this.viewSize = [s.width, s.height];
14683                 }else{
14684                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14685                 }
14686             }
14687             var s = Roo.get(this.container||document).getScroll();
14688
14689             var x = this.xy[0], y = this.xy[1];
14690             var w = this.size.width, h = this.size.height;
14691             var vw = this.viewSize[0], vh = this.viewSize[1];
14692             // only move it if it needs it
14693             var moved = false;
14694             // first validate right/bottom
14695             if(x + w > vw+s.left){
14696                 x = vw - w;
14697                 moved = true;
14698             }
14699             if(y + h > vh+s.top){
14700                 y = vh - h;
14701                 moved = true;
14702             }
14703             // then make sure top/left isn't negative
14704             if(x < s.left){
14705                 x = s.left;
14706                 moved = true;
14707             }
14708             if(y < s.top){
14709                 y = s.top;
14710                 moved = true;
14711             }
14712             if(moved){
14713                 // cache xy
14714                 this.xy = [x, y];
14715                 if(this.isVisible()){
14716                     this.el.setLocation(x, y);
14717                     this.adjustAssets();
14718                 }
14719             }
14720         }
14721     },
14722
14723     // private
14724     onDrag : function(){
14725         if(!this.proxyDrag){
14726             this.xy = this.el.getXY();
14727             this.adjustAssets();
14728         }
14729     },
14730
14731     // private
14732     adjustAssets : function(doShow){
14733         var x = this.xy[0], y = this.xy[1];
14734         var w = this.size.width, h = this.size.height;
14735         if(doShow === true){
14736             if(this.shadow){
14737                 this.shadow.show(this.el);
14738             }
14739             if(this.shim){
14740                 this.shim.show();
14741             }
14742         }
14743         if(this.shadow && this.shadow.isVisible()){
14744             this.shadow.show(this.el);
14745         }
14746         if(this.shim && this.shim.isVisible()){
14747             this.shim.setBounds(x, y, w, h);
14748         }
14749     },
14750
14751     // private
14752     adjustViewport : function(w, h){
14753         if(!w || !h){
14754             w = Roo.lib.Dom.getViewWidth();
14755             h = Roo.lib.Dom.getViewHeight();
14756         }
14757         // cache the size
14758         this.viewSize = [w, h];
14759         if(this.modal && this.mask.isVisible()){
14760             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14761             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14762         }
14763         if(this.isVisible()){
14764             this.constrainXY();
14765         }
14766     },
14767
14768     /**
14769      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14770      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14771      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14772      */
14773     destroy : function(removeEl){
14774         if(this.isVisible()){
14775             this.animateTarget = null;
14776             this.hide();
14777         }
14778         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14779         if(this.tabs){
14780             this.tabs.destroy(removeEl);
14781         }
14782         Roo.destroy(
14783              this.shim,
14784              this.proxy,
14785              this.resizer,
14786              this.close,
14787              this.mask
14788         );
14789         if(this.dd){
14790             this.dd.unreg();
14791         }
14792         if(this.buttons){
14793            for(var i = 0, len = this.buttons.length; i < len; i++){
14794                this.buttons[i].destroy();
14795            }
14796         }
14797         this.el.removeAllListeners();
14798         if(removeEl === true){
14799             this.el.update("");
14800             this.el.remove();
14801         }
14802         Roo.DialogManager.unregister(this);
14803     },
14804
14805     // private
14806     startMove : function(){
14807         if(this.proxyDrag){
14808             this.proxy.show();
14809         }
14810         if(this.constraintoviewport !== false){
14811             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14812         }
14813     },
14814
14815     // private
14816     endMove : function(){
14817         if(!this.proxyDrag){
14818             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14819         }else{
14820             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14821             this.proxy.hide();
14822         }
14823         this.refreshSize();
14824         this.adjustAssets();
14825         this.focus();
14826         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14827     },
14828
14829     /**
14830      * Brings this dialog to the front of any other visible dialogs
14831      * @return {Roo.BasicDialog} this
14832      */
14833     toFront : function(){
14834         Roo.DialogManager.bringToFront(this);
14835         return this;
14836     },
14837
14838     /**
14839      * Sends this dialog to the back (under) of any other visible dialogs
14840      * @return {Roo.BasicDialog} this
14841      */
14842     toBack : function(){
14843         Roo.DialogManager.sendToBack(this);
14844         return this;
14845     },
14846
14847     /**
14848      * Centers this dialog in the viewport
14849      * @return {Roo.BasicDialog} this
14850      */
14851     center : function(){
14852         var xy = this.el.getCenterXY(true);
14853         this.moveTo(xy[0], xy[1]);
14854         return this;
14855     },
14856
14857     /**
14858      * Moves the dialog's top-left corner to the specified point
14859      * @param {Number} x
14860      * @param {Number} y
14861      * @return {Roo.BasicDialog} this
14862      */
14863     moveTo : function(x, y){
14864         this.xy = [x,y];
14865         if(this.isVisible()){
14866             this.el.setXY(this.xy);
14867             this.adjustAssets();
14868         }
14869         return this;
14870     },
14871
14872     /**
14873      * Aligns the dialog to the specified element
14874      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14875      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14876      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14877      * @return {Roo.BasicDialog} this
14878      */
14879     alignTo : function(element, position, offsets){
14880         this.xy = this.el.getAlignToXY(element, position, offsets);
14881         if(this.isVisible()){
14882             this.el.setXY(this.xy);
14883             this.adjustAssets();
14884         }
14885         return this;
14886     },
14887
14888     /**
14889      * Anchors an element to another element and realigns it when the window is resized.
14890      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14891      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14892      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14893      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14894      * is a number, it is used as the buffer delay (defaults to 50ms).
14895      * @return {Roo.BasicDialog} this
14896      */
14897     anchorTo : function(el, alignment, offsets, monitorScroll){
14898         var action = function(){
14899             this.alignTo(el, alignment, offsets);
14900         };
14901         Roo.EventManager.onWindowResize(action, this);
14902         var tm = typeof monitorScroll;
14903         if(tm != 'undefined'){
14904             Roo.EventManager.on(window, 'scroll', action, this,
14905                 {buffer: tm == 'number' ? monitorScroll : 50});
14906         }
14907         action.call(this);
14908         return this;
14909     },
14910
14911     /**
14912      * Returns true if the dialog is visible
14913      * @return {Boolean}
14914      */
14915     isVisible : function(){
14916         return this.el.isVisible();
14917     },
14918
14919     // private
14920     animHide : function(callback){
14921         var b = Roo.get(this.animateTarget).getBox();
14922         this.proxy.show();
14923         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14924         this.el.hide();
14925         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14926                     this.hideEl.createDelegate(this, [callback]));
14927     },
14928
14929     /**
14930      * Hides the dialog.
14931      * @param {Function} callback (optional) Function to call when the dialog is hidden
14932      * @return {Roo.BasicDialog} this
14933      */
14934     hide : function(callback){
14935         if (this.fireEvent("beforehide", this) === false){
14936             return;
14937         }
14938         if(this.shadow){
14939             this.shadow.hide();
14940         }
14941         if(this.shim) {
14942           this.shim.hide();
14943         }
14944         // sometimes animateTarget seems to get set.. causing problems...
14945         // this just double checks..
14946         if(this.animateTarget && Roo.get(this.animateTarget)) {
14947            this.animHide(callback);
14948         }else{
14949             this.el.hide();
14950             this.hideEl(callback);
14951         }
14952         return this;
14953     },
14954
14955     // private
14956     hideEl : function(callback){
14957         this.proxy.hide();
14958         if(this.modal){
14959             this.mask.hide();
14960             Roo.get(document.body).removeClass("x-body-masked");
14961         }
14962         this.fireEvent("hide", this);
14963         if(typeof callback == "function"){
14964             callback();
14965         }
14966     },
14967
14968     // private
14969     hideAction : function(){
14970         this.setLeft("-10000px");
14971         this.setTop("-10000px");
14972         this.setStyle("visibility", "hidden");
14973     },
14974
14975     // private
14976     refreshSize : function(){
14977         this.size = this.el.getSize();
14978         this.xy = this.el.getXY();
14979         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
14980     },
14981
14982     // private
14983     // z-index is managed by the DialogManager and may be overwritten at any time
14984     setZIndex : function(index){
14985         if(this.modal){
14986             this.mask.setStyle("z-index", index);
14987         }
14988         if(this.shim){
14989             this.shim.setStyle("z-index", ++index);
14990         }
14991         if(this.shadow){
14992             this.shadow.setZIndex(++index);
14993         }
14994         this.el.setStyle("z-index", ++index);
14995         if(this.proxy){
14996             this.proxy.setStyle("z-index", ++index);
14997         }
14998         if(this.resizer){
14999             this.resizer.proxy.setStyle("z-index", ++index);
15000         }
15001
15002         this.lastZIndex = index;
15003     },
15004
15005     /**
15006      * Returns the element for this dialog
15007      * @return {Roo.Element} The underlying dialog Element
15008      */
15009     getEl : function(){
15010         return this.el;
15011     }
15012 });
15013
15014 /**
15015  * @class Roo.DialogManager
15016  * Provides global access to BasicDialogs that have been created and
15017  * support for z-indexing (layering) multiple open dialogs.
15018  */
15019 Roo.DialogManager = function(){
15020     var list = {};
15021     var accessList = [];
15022     var front = null;
15023
15024     // private
15025     var sortDialogs = function(d1, d2){
15026         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15027     };
15028
15029     // private
15030     var orderDialogs = function(){
15031         accessList.sort(sortDialogs);
15032         var seed = Roo.DialogManager.zseed;
15033         for(var i = 0, len = accessList.length; i < len; i++){
15034             var dlg = accessList[i];
15035             if(dlg){
15036                 dlg.setZIndex(seed + (i*10));
15037             }
15038         }
15039     };
15040
15041     return {
15042         /**
15043          * The starting z-index for BasicDialogs (defaults to 9000)
15044          * @type Number The z-index value
15045          */
15046         zseed : 9000,
15047
15048         // private
15049         register : function(dlg){
15050             list[dlg.id] = dlg;
15051             accessList.push(dlg);
15052         },
15053
15054         // private
15055         unregister : function(dlg){
15056             delete list[dlg.id];
15057             var i=0;
15058             var len=0;
15059             if(!accessList.indexOf){
15060                 for(  i = 0, len = accessList.length; i < len; i++){
15061                     if(accessList[i] == dlg){
15062                         accessList.splice(i, 1);
15063                         return;
15064                     }
15065                 }
15066             }else{
15067                  i = accessList.indexOf(dlg);
15068                 if(i != -1){
15069                     accessList.splice(i, 1);
15070                 }
15071             }
15072         },
15073
15074         /**
15075          * Gets a registered dialog by id
15076          * @param {String/Object} id The id of the dialog or a dialog
15077          * @return {Roo.BasicDialog} this
15078          */
15079         get : function(id){
15080             return typeof id == "object" ? id : list[id];
15081         },
15082
15083         /**
15084          * Brings the specified dialog to the front
15085          * @param {String/Object} dlg The id of the dialog or a dialog
15086          * @return {Roo.BasicDialog} this
15087          */
15088         bringToFront : function(dlg){
15089             dlg = this.get(dlg);
15090             if(dlg != front){
15091                 front = dlg;
15092                 dlg._lastAccess = new Date().getTime();
15093                 orderDialogs();
15094             }
15095             return dlg;
15096         },
15097
15098         /**
15099          * Sends the specified dialog to the back
15100          * @param {String/Object} dlg The id of the dialog or a dialog
15101          * @return {Roo.BasicDialog} this
15102          */
15103         sendToBack : function(dlg){
15104             dlg = this.get(dlg);
15105             dlg._lastAccess = -(new Date().getTime());
15106             orderDialogs();
15107             return dlg;
15108         },
15109
15110         /**
15111          * Hides all dialogs
15112          */
15113         hideAll : function(){
15114             for(var id in list){
15115                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15116                     list[id].hide();
15117                 }
15118             }
15119         }
15120     };
15121 }();
15122
15123 /**
15124  * @class Roo.LayoutDialog
15125  * @extends Roo.BasicDialog
15126  * Dialog which provides adjustments for working with a layout in a Dialog.
15127  * Add your necessary layout config options to the dialog's config.<br>
15128  * Example usage (including a nested layout):
15129  * <pre><code>
15130 if(!dialog){
15131     dialog = new Roo.LayoutDialog("download-dlg", {
15132         modal: true,
15133         width:600,
15134         height:450,
15135         shadow:true,
15136         minWidth:500,
15137         minHeight:350,
15138         autoTabs:true,
15139         proxyDrag:true,
15140         // layout config merges with the dialog config
15141         center:{
15142             tabPosition: "top",
15143             alwaysShowTabs: true
15144         }
15145     });
15146     dialog.addKeyListener(27, dialog.hide, dialog);
15147     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15148     dialog.addButton("Build It!", this.getDownload, this);
15149
15150     // we can even add nested layouts
15151     var innerLayout = new Roo.BorderLayout("dl-inner", {
15152         east: {
15153             initialSize: 200,
15154             autoScroll:true,
15155             split:true
15156         },
15157         center: {
15158             autoScroll:true
15159         }
15160     });
15161     innerLayout.beginUpdate();
15162     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15163     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15164     innerLayout.endUpdate(true);
15165
15166     var layout = dialog.getLayout();
15167     layout.beginUpdate();
15168     layout.add("center", new Roo.ContentPanel("standard-panel",
15169                         {title: "Download the Source", fitToFrame:true}));
15170     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15171                {title: "Build your own roo.js"}));
15172     layout.getRegion("center").showPanel(sp);
15173     layout.endUpdate();
15174 }
15175 </code></pre>
15176     * @constructor
15177     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15178     * @param {Object} config configuration options
15179   */
15180 Roo.LayoutDialog = function(el, cfg){
15181     
15182     var config=  cfg;
15183     if (typeof(cfg) == 'undefined') {
15184         config = Roo.apply({}, el);
15185         // not sure why we use documentElement here.. - it should always be body.
15186         // IE7 borks horribly if we use documentElement.
15187         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15188         //config.autoCreate = true;
15189     }
15190     
15191     
15192     config.autoTabs = false;
15193     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15194     this.body.setStyle({overflow:"hidden", position:"relative"});
15195     this.layout = new Roo.BorderLayout(this.body.dom, config);
15196     this.layout.monitorWindowResize = false;
15197     this.el.addClass("x-dlg-auto-layout");
15198     // fix case when center region overwrites center function
15199     this.center = Roo.BasicDialog.prototype.center;
15200     this.on("show", this.layout.layout, this.layout, true);
15201     if (config.items) {
15202         var xitems = config.items;
15203         delete config.items;
15204         Roo.each(xitems, this.addxtype, this);
15205     }
15206     
15207     
15208 };
15209 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15210     /**
15211      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15212      * @deprecated
15213      */
15214     endUpdate : function(){
15215         this.layout.endUpdate();
15216     },
15217
15218     /**
15219      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15220      *  @deprecated
15221      */
15222     beginUpdate : function(){
15223         this.layout.beginUpdate();
15224     },
15225
15226     /**
15227      * Get the BorderLayout for this dialog
15228      * @return {Roo.BorderLayout}
15229      */
15230     getLayout : function(){
15231         return this.layout;
15232     },
15233
15234     showEl : function(){
15235         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15236         if(Roo.isIE7){
15237             this.layout.layout();
15238         }
15239     },
15240
15241     // private
15242     // Use the syncHeightBeforeShow config option to control this automatically
15243     syncBodyHeight : function(){
15244         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15245         if(this.layout){this.layout.layout();}
15246     },
15247     
15248       /**
15249      * Add an xtype element (actually adds to the layout.)
15250      * @return {Object} xdata xtype object data.
15251      */
15252     
15253     addxtype : function(c) {
15254         return this.layout.addxtype(c);
15255     }
15256 });/*
15257  * Based on:
15258  * Ext JS Library 1.1.1
15259  * Copyright(c) 2006-2007, Ext JS, LLC.
15260  *
15261  * Originally Released Under LGPL - original licence link has changed is not relivant.
15262  *
15263  * Fork - LGPL
15264  * <script type="text/javascript">
15265  */
15266  
15267 /**
15268  * @class Roo.MessageBox
15269  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15270  * Example usage:
15271  *<pre><code>
15272 // Basic alert:
15273 Roo.Msg.alert('Status', 'Changes saved successfully.');
15274
15275 // Prompt for user data:
15276 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15277     if (btn == 'ok'){
15278         // process text value...
15279     }
15280 });
15281
15282 // Show a dialog using config options:
15283 Roo.Msg.show({
15284    title:'Save Changes?',
15285    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15286    buttons: Roo.Msg.YESNOCANCEL,
15287    fn: processResult,
15288    animEl: 'elId'
15289 });
15290 </code></pre>
15291  * @singleton
15292  */
15293 Roo.MessageBox = function(){
15294     var dlg, opt, mask, waitTimer;
15295     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15296     var buttons, activeTextEl, bwidth;
15297
15298     // private
15299     var handleButton = function(button){
15300         dlg.hide();
15301         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15302     };
15303
15304     // private
15305     var handleHide = function(){
15306         if(opt && opt.cls){
15307             dlg.el.removeClass(opt.cls);
15308         }
15309         if(waitTimer){
15310             Roo.TaskMgr.stop(waitTimer);
15311             waitTimer = null;
15312         }
15313     };
15314
15315     // private
15316     var updateButtons = function(b){
15317         var width = 0;
15318         if(!b){
15319             buttons["ok"].hide();
15320             buttons["cancel"].hide();
15321             buttons["yes"].hide();
15322             buttons["no"].hide();
15323             dlg.footer.dom.style.display = 'none';
15324             return width;
15325         }
15326         dlg.footer.dom.style.display = '';
15327         for(var k in buttons){
15328             if(typeof buttons[k] != "function"){
15329                 if(b[k]){
15330                     buttons[k].show();
15331                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15332                     width += buttons[k].el.getWidth()+15;
15333                 }else{
15334                     buttons[k].hide();
15335                 }
15336             }
15337         }
15338         return width;
15339     };
15340
15341     // private
15342     var handleEsc = function(d, k, e){
15343         if(opt && opt.closable !== false){
15344             dlg.hide();
15345         }
15346         if(e){
15347             e.stopEvent();
15348         }
15349     };
15350
15351     return {
15352         /**
15353          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15354          * @return {Roo.BasicDialog} The BasicDialog element
15355          */
15356         getDialog : function(){
15357            if(!dlg){
15358                 dlg = new Roo.BasicDialog("x-msg-box", {
15359                     autoCreate : true,
15360                     shadow: true,
15361                     draggable: true,
15362                     resizable:false,
15363                     constraintoviewport:false,
15364                     fixedcenter:true,
15365                     collapsible : false,
15366                     shim:true,
15367                     modal: true,
15368                     width:400, height:100,
15369                     buttonAlign:"center",
15370                     closeClick : function(){
15371                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15372                             handleButton("no");
15373                         }else{
15374                             handleButton("cancel");
15375                         }
15376                     }
15377                 });
15378                 dlg.on("hide", handleHide);
15379                 mask = dlg.mask;
15380                 dlg.addKeyListener(27, handleEsc);
15381                 buttons = {};
15382                 var bt = this.buttonText;
15383                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15384                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15385                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15386                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15387                 bodyEl = dlg.body.createChild({
15388
15389                     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>'
15390                 });
15391                 msgEl = bodyEl.dom.firstChild;
15392                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15393                 textboxEl.enableDisplayMode();
15394                 textboxEl.addKeyListener([10,13], function(){
15395                     if(dlg.isVisible() && opt && opt.buttons){
15396                         if(opt.buttons.ok){
15397                             handleButton("ok");
15398                         }else if(opt.buttons.yes){
15399                             handleButton("yes");
15400                         }
15401                     }
15402                 });
15403                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15404                 textareaEl.enableDisplayMode();
15405                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15406                 progressEl.enableDisplayMode();
15407                 var pf = progressEl.dom.firstChild;
15408                 if (pf) {
15409                     pp = Roo.get(pf.firstChild);
15410                     pp.setHeight(pf.offsetHeight);
15411                 }
15412                 
15413             }
15414             return dlg;
15415         },
15416
15417         /**
15418          * Updates the message box body text
15419          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15420          * the XHTML-compliant non-breaking space character '&amp;#160;')
15421          * @return {Roo.MessageBox} This message box
15422          */
15423         updateText : function(text){
15424             if(!dlg.isVisible() && !opt.width){
15425                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15426             }
15427             msgEl.innerHTML = text || '&#160;';
15428             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15429                         Math.max(opt.minWidth || this.minWidth, bwidth));
15430             if(opt.prompt){
15431                 activeTextEl.setWidth(w);
15432             }
15433             if(dlg.isVisible()){
15434                 dlg.fixedcenter = false;
15435             }
15436             dlg.setContentSize(w, bodyEl.getHeight());
15437             if(dlg.isVisible()){
15438                 dlg.fixedcenter = true;
15439             }
15440             return this;
15441         },
15442
15443         /**
15444          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15445          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15446          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15447          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15448          * @return {Roo.MessageBox} This message box
15449          */
15450         updateProgress : function(value, text){
15451             if(text){
15452                 this.updateText(text);
15453             }
15454             if (pp) { // weird bug on my firefox - for some reason this is not defined
15455                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15456             }
15457             return this;
15458         },        
15459
15460         /**
15461          * Returns true if the message box is currently displayed
15462          * @return {Boolean} True if the message box is visible, else false
15463          */
15464         isVisible : function(){
15465             return dlg && dlg.isVisible();  
15466         },
15467
15468         /**
15469          * Hides the message box if it is displayed
15470          */
15471         hide : function(){
15472             if(this.isVisible()){
15473                 dlg.hide();
15474             }  
15475         },
15476
15477         /**
15478          * Displays a new message box, or reinitializes an existing message box, based on the config options
15479          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15480          * The following config object properties are supported:
15481          * <pre>
15482 Property    Type             Description
15483 ----------  ---------------  ------------------------------------------------------------------------------------
15484 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15485                                    closes (defaults to undefined)
15486 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15487                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15488 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15489                                    progress and wait dialogs will ignore this property and always hide the
15490                                    close button as they can only be closed programmatically.
15491 cls               String           A custom CSS class to apply to the message box element
15492 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15493                                    displayed (defaults to 75)
15494 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15495                                    function will be btn (the name of the button that was clicked, if applicable,
15496                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15497                                    Progress and wait dialogs will ignore this option since they do not respond to
15498                                    user actions and can only be closed programmatically, so any required function
15499                                    should be called by the same code after it closes the dialog.
15500 icon              String           A CSS class that provides a background image to be used as an icon for
15501                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15502 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15503 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15504 modal             Boolean          False to allow user interaction with the page while the message box is
15505                                    displayed (defaults to true)
15506 msg               String           A string that will replace the existing message box body text (defaults
15507                                    to the XHTML-compliant non-breaking space character '&#160;')
15508 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15509 progress          Boolean          True to display a progress bar (defaults to false)
15510 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15511 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15512 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15513 title             String           The title text
15514 value             String           The string value to set into the active textbox element if displayed
15515 wait              Boolean          True to display a progress bar (defaults to false)
15516 width             Number           The width of the dialog in pixels
15517 </pre>
15518          *
15519          * Example usage:
15520          * <pre><code>
15521 Roo.Msg.show({
15522    title: 'Address',
15523    msg: 'Please enter your address:',
15524    width: 300,
15525    buttons: Roo.MessageBox.OKCANCEL,
15526    multiline: true,
15527    fn: saveAddress,
15528    animEl: 'addAddressBtn'
15529 });
15530 </code></pre>
15531          * @param {Object} config Configuration options
15532          * @return {Roo.MessageBox} This message box
15533          */
15534         show : function(options){
15535             if(this.isVisible()){
15536                 this.hide();
15537             }
15538             var d = this.getDialog();
15539             opt = options;
15540             d.setTitle(opt.title || "&#160;");
15541             d.close.setDisplayed(opt.closable !== false);
15542             activeTextEl = textboxEl;
15543             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15544             if(opt.prompt){
15545                 if(opt.multiline){
15546                     textboxEl.hide();
15547                     textareaEl.show();
15548                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15549                         opt.multiline : this.defaultTextHeight);
15550                     activeTextEl = textareaEl;
15551                 }else{
15552                     textboxEl.show();
15553                     textareaEl.hide();
15554                 }
15555             }else{
15556                 textboxEl.hide();
15557                 textareaEl.hide();
15558             }
15559             progressEl.setDisplayed(opt.progress === true);
15560             this.updateProgress(0);
15561             activeTextEl.dom.value = opt.value || "";
15562             if(opt.prompt){
15563                 dlg.setDefaultButton(activeTextEl);
15564             }else{
15565                 var bs = opt.buttons;
15566                 var db = null;
15567                 if(bs && bs.ok){
15568                     db = buttons["ok"];
15569                 }else if(bs && bs.yes){
15570                     db = buttons["yes"];
15571                 }
15572                 dlg.setDefaultButton(db);
15573             }
15574             bwidth = updateButtons(opt.buttons);
15575             this.updateText(opt.msg);
15576             if(opt.cls){
15577                 d.el.addClass(opt.cls);
15578             }
15579             d.proxyDrag = opt.proxyDrag === true;
15580             d.modal = opt.modal !== false;
15581             d.mask = opt.modal !== false ? mask : false;
15582             if(!d.isVisible()){
15583                 // force it to the end of the z-index stack so it gets a cursor in FF
15584                 document.body.appendChild(dlg.el.dom);
15585                 d.animateTarget = null;
15586                 d.show(options.animEl);
15587             }
15588             return this;
15589         },
15590
15591         /**
15592          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15593          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15594          * and closing the message box when the process is complete.
15595          * @param {String} title The title bar text
15596          * @param {String} msg The message box body text
15597          * @return {Roo.MessageBox} This message box
15598          */
15599         progress : function(title, msg){
15600             this.show({
15601                 title : title,
15602                 msg : msg,
15603                 buttons: false,
15604                 progress:true,
15605                 closable:false,
15606                 minWidth: this.minProgressWidth,
15607                 modal : true
15608             });
15609             return this;
15610         },
15611
15612         /**
15613          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15614          * If a callback function is passed it will be called after the user clicks the button, and the
15615          * id of the button that was clicked will be passed as the only parameter to the callback
15616          * (could also be the top-right close button).
15617          * @param {String} title The title bar text
15618          * @param {String} msg The message box body text
15619          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15620          * @param {Object} scope (optional) The scope of the callback function
15621          * @return {Roo.MessageBox} This message box
15622          */
15623         alert : function(title, msg, fn, scope){
15624             this.show({
15625                 title : title,
15626                 msg : msg,
15627                 buttons: this.OK,
15628                 fn: fn,
15629                 scope : scope,
15630                 modal : true
15631             });
15632             return this;
15633         },
15634
15635         /**
15636          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15637          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15638          * You are responsible for closing the message box when the process is complete.
15639          * @param {String} msg The message box body text
15640          * @param {String} title (optional) The title bar text
15641          * @return {Roo.MessageBox} This message box
15642          */
15643         wait : function(msg, title){
15644             this.show({
15645                 title : title,
15646                 msg : msg,
15647                 buttons: false,
15648                 closable:false,
15649                 progress:true,
15650                 modal:true,
15651                 width:300,
15652                 wait:true
15653             });
15654             waitTimer = Roo.TaskMgr.start({
15655                 run: function(i){
15656                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15657                 },
15658                 interval: 1000
15659             });
15660             return this;
15661         },
15662
15663         /**
15664          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15665          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15666          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15667          * @param {String} title The title bar text
15668          * @param {String} msg The message box body text
15669          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15670          * @param {Object} scope (optional) The scope of the callback function
15671          * @return {Roo.MessageBox} This message box
15672          */
15673         confirm : function(title, msg, fn, scope){
15674             this.show({
15675                 title : title,
15676                 msg : msg,
15677                 buttons: this.YESNO,
15678                 fn: fn,
15679                 scope : scope,
15680                 modal : true
15681             });
15682             return this;
15683         },
15684
15685         /**
15686          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15687          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15688          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15689          * (could also be the top-right close button) and the text that was entered will be passed as the two
15690          * parameters to the callback.
15691          * @param {String} title The title bar text
15692          * @param {String} msg The message box body text
15693          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15694          * @param {Object} scope (optional) The scope of the callback function
15695          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15696          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15697          * @return {Roo.MessageBox} This message box
15698          */
15699         prompt : function(title, msg, fn, scope, multiline){
15700             this.show({
15701                 title : title,
15702                 msg : msg,
15703                 buttons: this.OKCANCEL,
15704                 fn: fn,
15705                 minWidth:250,
15706                 scope : scope,
15707                 prompt:true,
15708                 multiline: multiline,
15709                 modal : true
15710             });
15711             return this;
15712         },
15713
15714         /**
15715          * Button config that displays a single OK button
15716          * @type Object
15717          */
15718         OK : {ok:true},
15719         /**
15720          * Button config that displays Yes and No buttons
15721          * @type Object
15722          */
15723         YESNO : {yes:true, no:true},
15724         /**
15725          * Button config that displays OK and Cancel buttons
15726          * @type Object
15727          */
15728         OKCANCEL : {ok:true, cancel:true},
15729         /**
15730          * Button config that displays Yes, No and Cancel buttons
15731          * @type Object
15732          */
15733         YESNOCANCEL : {yes:true, no:true, cancel:true},
15734
15735         /**
15736          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15737          * @type Number
15738          */
15739         defaultTextHeight : 75,
15740         /**
15741          * The maximum width in pixels of the message box (defaults to 600)
15742          * @type Number
15743          */
15744         maxWidth : 600,
15745         /**
15746          * The minimum width in pixels of the message box (defaults to 100)
15747          * @type Number
15748          */
15749         minWidth : 100,
15750         /**
15751          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15752          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15753          * @type Number
15754          */
15755         minProgressWidth : 250,
15756         /**
15757          * An object containing the default button text strings that can be overriden for localized language support.
15758          * Supported properties are: ok, cancel, yes and no.
15759          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15760          * @type Object
15761          */
15762         buttonText : {
15763             ok : "OK",
15764             cancel : "Cancel",
15765             yes : "Yes",
15766             no : "No"
15767         }
15768     };
15769 }();
15770
15771 /**
15772  * Shorthand for {@link Roo.MessageBox}
15773  */
15774 Roo.Msg = Roo.MessageBox;/*
15775  * Based on:
15776  * Ext JS Library 1.1.1
15777  * Copyright(c) 2006-2007, Ext JS, LLC.
15778  *
15779  * Originally Released Under LGPL - original licence link has changed is not relivant.
15780  *
15781  * Fork - LGPL
15782  * <script type="text/javascript">
15783  */
15784 /**
15785  * @class Roo.QuickTips
15786  * Provides attractive and customizable tooltips for any element.
15787  * @singleton
15788  */
15789 Roo.QuickTips = function(){
15790     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15791     var ce, bd, xy, dd;
15792     var visible = false, disabled = true, inited = false;
15793     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15794     
15795     var onOver = function(e){
15796         if(disabled){
15797             return;
15798         }
15799         var t = e.getTarget();
15800         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15801             return;
15802         }
15803         if(ce && t == ce.el){
15804             clearTimeout(hideProc);
15805             return;
15806         }
15807         if(t && tagEls[t.id]){
15808             tagEls[t.id].el = t;
15809             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15810             return;
15811         }
15812         var ttp, et = Roo.fly(t);
15813         var ns = cfg.namespace;
15814         if(tm.interceptTitles && t.title){
15815             ttp = t.title;
15816             t.qtip = ttp;
15817             t.removeAttribute("title");
15818             e.preventDefault();
15819         }else{
15820             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15821         }
15822         if(ttp){
15823             showProc = show.defer(tm.showDelay, tm, [{
15824                 el: t, 
15825                 text: ttp, 
15826                 width: et.getAttributeNS(ns, cfg.width),
15827                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15828                 title: et.getAttributeNS(ns, cfg.title),
15829                     cls: et.getAttributeNS(ns, cfg.cls)
15830             }]);
15831         }
15832     };
15833     
15834     var onOut = function(e){
15835         clearTimeout(showProc);
15836         var t = e.getTarget();
15837         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15838             hideProc = setTimeout(hide, tm.hideDelay);
15839         }
15840     };
15841     
15842     var onMove = function(e){
15843         if(disabled){
15844             return;
15845         }
15846         xy = e.getXY();
15847         xy[1] += 18;
15848         if(tm.trackMouse && ce){
15849             el.setXY(xy);
15850         }
15851     };
15852     
15853     var onDown = function(e){
15854         clearTimeout(showProc);
15855         clearTimeout(hideProc);
15856         if(!e.within(el)){
15857             if(tm.hideOnClick){
15858                 hide();
15859                 tm.disable();
15860                 tm.enable.defer(100, tm);
15861             }
15862         }
15863     };
15864     
15865     var getPad = function(){
15866         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15867     };
15868
15869     var show = function(o){
15870         if(disabled){
15871             return;
15872         }
15873         clearTimeout(dismissProc);
15874         ce = o;
15875         if(removeCls){ // in case manually hidden
15876             el.removeClass(removeCls);
15877             removeCls = null;
15878         }
15879         if(ce.cls){
15880             el.addClass(ce.cls);
15881             removeCls = ce.cls;
15882         }
15883         if(ce.title){
15884             tipTitle.update(ce.title);
15885             tipTitle.show();
15886         }else{
15887             tipTitle.update('');
15888             tipTitle.hide();
15889         }
15890         el.dom.style.width  = tm.maxWidth+'px';
15891         //tipBody.dom.style.width = '';
15892         tipBodyText.update(o.text);
15893         var p = getPad(), w = ce.width;
15894         if(!w){
15895             var td = tipBodyText.dom;
15896             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15897             if(aw > tm.maxWidth){
15898                 w = tm.maxWidth;
15899             }else if(aw < tm.minWidth){
15900                 w = tm.minWidth;
15901             }else{
15902                 w = aw;
15903             }
15904         }
15905         //tipBody.setWidth(w);
15906         el.setWidth(parseInt(w, 10) + p);
15907         if(ce.autoHide === false){
15908             close.setDisplayed(true);
15909             if(dd){
15910                 dd.unlock();
15911             }
15912         }else{
15913             close.setDisplayed(false);
15914             if(dd){
15915                 dd.lock();
15916             }
15917         }
15918         if(xy){
15919             el.avoidY = xy[1]-18;
15920             el.setXY(xy);
15921         }
15922         if(tm.animate){
15923             el.setOpacity(.1);
15924             el.setStyle("visibility", "visible");
15925             el.fadeIn({callback: afterShow});
15926         }else{
15927             afterShow();
15928         }
15929     };
15930     
15931     var afterShow = function(){
15932         if(ce){
15933             el.show();
15934             esc.enable();
15935             if(tm.autoDismiss && ce.autoHide !== false){
15936                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
15937             }
15938         }
15939     };
15940     
15941     var hide = function(noanim){
15942         clearTimeout(dismissProc);
15943         clearTimeout(hideProc);
15944         ce = null;
15945         if(el.isVisible()){
15946             esc.disable();
15947             if(noanim !== true && tm.animate){
15948                 el.fadeOut({callback: afterHide});
15949             }else{
15950                 afterHide();
15951             } 
15952         }
15953     };
15954     
15955     var afterHide = function(){
15956         el.hide();
15957         if(removeCls){
15958             el.removeClass(removeCls);
15959             removeCls = null;
15960         }
15961     };
15962     
15963     return {
15964         /**
15965         * @cfg {Number} minWidth
15966         * The minimum width of the quick tip (defaults to 40)
15967         */
15968        minWidth : 40,
15969         /**
15970         * @cfg {Number} maxWidth
15971         * The maximum width of the quick tip (defaults to 300)
15972         */
15973        maxWidth : 300,
15974         /**
15975         * @cfg {Boolean} interceptTitles
15976         * True to automatically use the element's DOM title value if available (defaults to false)
15977         */
15978        interceptTitles : false,
15979         /**
15980         * @cfg {Boolean} trackMouse
15981         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
15982         */
15983        trackMouse : false,
15984         /**
15985         * @cfg {Boolean} hideOnClick
15986         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
15987         */
15988        hideOnClick : true,
15989         /**
15990         * @cfg {Number} showDelay
15991         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
15992         */
15993        showDelay : 500,
15994         /**
15995         * @cfg {Number} hideDelay
15996         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
15997         */
15998        hideDelay : 200,
15999         /**
16000         * @cfg {Boolean} autoHide
16001         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16002         * Used in conjunction with hideDelay.
16003         */
16004        autoHide : true,
16005         /**
16006         * @cfg {Boolean}
16007         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16008         * (defaults to true).  Used in conjunction with autoDismissDelay.
16009         */
16010        autoDismiss : true,
16011         /**
16012         * @cfg {Number}
16013         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16014         */
16015        autoDismissDelay : 5000,
16016        /**
16017         * @cfg {Boolean} animate
16018         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16019         */
16020        animate : false,
16021
16022        /**
16023         * @cfg {String} title
16024         * Title text to display (defaults to '').  This can be any valid HTML markup.
16025         */
16026         title: '',
16027        /**
16028         * @cfg {String} text
16029         * Body text to display (defaults to '').  This can be any valid HTML markup.
16030         */
16031         text : '',
16032        /**
16033         * @cfg {String} cls
16034         * A CSS class to apply to the base quick tip element (defaults to '').
16035         */
16036         cls : '',
16037        /**
16038         * @cfg {Number} width
16039         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16040         * minWidth or maxWidth.
16041         */
16042         width : null,
16043
16044     /**
16045      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16046      * or display QuickTips in a page.
16047      */
16048        init : function(){
16049           tm = Roo.QuickTips;
16050           cfg = tm.tagConfig;
16051           if(!inited){
16052               if(!Roo.isReady){ // allow calling of init() before onReady
16053                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16054                   return;
16055               }
16056               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16057               el.fxDefaults = {stopFx: true};
16058               // maximum custom styling
16059               //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>');
16060               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>');              
16061               tipTitle = el.child('h3');
16062               tipTitle.enableDisplayMode("block");
16063               tipBody = el.child('div.x-tip-bd');
16064               tipBodyText = el.child('div.x-tip-bd-inner');
16065               //bdLeft = el.child('div.x-tip-bd-left');
16066               //bdRight = el.child('div.x-tip-bd-right');
16067               close = el.child('div.x-tip-close');
16068               close.enableDisplayMode("block");
16069               close.on("click", hide);
16070               var d = Roo.get(document);
16071               d.on("mousedown", onDown);
16072               d.on("mouseover", onOver);
16073               d.on("mouseout", onOut);
16074               d.on("mousemove", onMove);
16075               esc = d.addKeyListener(27, hide);
16076               esc.disable();
16077               if(Roo.dd.DD){
16078                   dd = el.initDD("default", null, {
16079                       onDrag : function(){
16080                           el.sync();  
16081                       }
16082                   });
16083                   dd.setHandleElId(tipTitle.id);
16084                   dd.lock();
16085               }
16086               inited = true;
16087           }
16088           this.enable(); 
16089        },
16090
16091     /**
16092      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16093      * are supported:
16094      * <pre>
16095 Property    Type                   Description
16096 ----------  ---------------------  ------------------------------------------------------------------------
16097 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16098      * </ul>
16099      * @param {Object} config The config object
16100      */
16101        register : function(config){
16102            var cs = config instanceof Array ? config : arguments;
16103            for(var i = 0, len = cs.length; i < len; i++) {
16104                var c = cs[i];
16105                var target = c.target;
16106                if(target){
16107                    if(target instanceof Array){
16108                        for(var j = 0, jlen = target.length; j < jlen; j++){
16109                            tagEls[target[j]] = c;
16110                        }
16111                    }else{
16112                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16113                    }
16114                }
16115            }
16116        },
16117
16118     /**
16119      * Removes this quick tip from its element and destroys it.
16120      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16121      */
16122        unregister : function(el){
16123            delete tagEls[Roo.id(el)];
16124        },
16125
16126     /**
16127      * Enable this quick tip.
16128      */
16129        enable : function(){
16130            if(inited && disabled){
16131                locks.pop();
16132                if(locks.length < 1){
16133                    disabled = false;
16134                }
16135            }
16136        },
16137
16138     /**
16139      * Disable this quick tip.
16140      */
16141        disable : function(){
16142           disabled = true;
16143           clearTimeout(showProc);
16144           clearTimeout(hideProc);
16145           clearTimeout(dismissProc);
16146           if(ce){
16147               hide(true);
16148           }
16149           locks.push(1);
16150        },
16151
16152     /**
16153      * Returns true if the quick tip is enabled, else false.
16154      */
16155        isEnabled : function(){
16156             return !disabled;
16157        },
16158
16159         // private
16160        tagConfig : {
16161            namespace : "ext",
16162            attribute : "qtip",
16163            width : "width",
16164            target : "target",
16165            title : "qtitle",
16166            hide : "hide",
16167            cls : "qclass"
16168        }
16169    };
16170 }();
16171
16172 // backwards compat
16173 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16174  * Based on:
16175  * Ext JS Library 1.1.1
16176  * Copyright(c) 2006-2007, Ext JS, LLC.
16177  *
16178  * Originally Released Under LGPL - original licence link has changed is not relivant.
16179  *
16180  * Fork - LGPL
16181  * <script type="text/javascript">
16182  */
16183  
16184
16185 /**
16186  * @class Roo.tree.TreePanel
16187  * @extends Roo.data.Tree
16188
16189  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16190  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16191  * @cfg {Boolean} enableDD true to enable drag and drop
16192  * @cfg {Boolean} enableDrag true to enable just drag
16193  * @cfg {Boolean} enableDrop true to enable just drop
16194  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16195  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16196  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16197  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16198  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16199  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16200  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16201  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16202  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16203  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16204  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16205  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16206  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16207  * @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>
16208  * @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>
16209  * 
16210  * @constructor
16211  * @param {String/HTMLElement/Element} el The container element
16212  * @param {Object} config
16213  */
16214 Roo.tree.TreePanel = function(el, config){
16215     var root = false;
16216     var loader = false;
16217     if (config.root) {
16218         root = config.root;
16219         delete config.root;
16220     }
16221     if (config.loader) {
16222         loader = config.loader;
16223         delete config.loader;
16224     }
16225     
16226     Roo.apply(this, config);
16227     Roo.tree.TreePanel.superclass.constructor.call(this);
16228     this.el = Roo.get(el);
16229     this.el.addClass('x-tree');
16230     //console.log(root);
16231     if (root) {
16232         this.setRootNode( Roo.factory(root, Roo.tree));
16233     }
16234     if (loader) {
16235         this.loader = Roo.factory(loader, Roo.tree);
16236     }
16237    /**
16238     * Read-only. The id of the container element becomes this TreePanel's id.
16239     */
16240    this.id = this.el.id;
16241    this.addEvents({
16242         /**
16243         * @event beforeload
16244         * Fires before a node is loaded, return false to cancel
16245         * @param {Node} node The node being loaded
16246         */
16247         "beforeload" : true,
16248         /**
16249         * @event load
16250         * Fires when a node is loaded
16251         * @param {Node} node The node that was loaded
16252         */
16253         "load" : true,
16254         /**
16255         * @event textchange
16256         * Fires when the text for a node is changed
16257         * @param {Node} node The node
16258         * @param {String} text The new text
16259         * @param {String} oldText The old text
16260         */
16261         "textchange" : true,
16262         /**
16263         * @event beforeexpand
16264         * Fires before a node is expanded, return false to cancel.
16265         * @param {Node} node The node
16266         * @param {Boolean} deep
16267         * @param {Boolean} anim
16268         */
16269         "beforeexpand" : true,
16270         /**
16271         * @event beforecollapse
16272         * Fires before a node is collapsed, return false to cancel.
16273         * @param {Node} node The node
16274         * @param {Boolean} deep
16275         * @param {Boolean} anim
16276         */
16277         "beforecollapse" : true,
16278         /**
16279         * @event expand
16280         * Fires when a node is expanded
16281         * @param {Node} node The node
16282         */
16283         "expand" : true,
16284         /**
16285         * @event disabledchange
16286         * Fires when the disabled status of a node changes
16287         * @param {Node} node The node
16288         * @param {Boolean} disabled
16289         */
16290         "disabledchange" : true,
16291         /**
16292         * @event collapse
16293         * Fires when a node is collapsed
16294         * @param {Node} node The node
16295         */
16296         "collapse" : true,
16297         /**
16298         * @event beforeclick
16299         * Fires before click processing on a node. Return false to cancel the default action.
16300         * @param {Node} node The node
16301         * @param {Roo.EventObject} e The event object
16302         */
16303         "beforeclick":true,
16304         /**
16305         * @event checkchange
16306         * Fires when a node with a checkbox's checked property changes
16307         * @param {Node} this This node
16308         * @param {Boolean} checked
16309         */
16310         "checkchange":true,
16311         /**
16312         * @event click
16313         * Fires when a node is clicked
16314         * @param {Node} node The node
16315         * @param {Roo.EventObject} e The event object
16316         */
16317         "click":true,
16318         /**
16319         * @event dblclick
16320         * Fires when a node is double clicked
16321         * @param {Node} node The node
16322         * @param {Roo.EventObject} e The event object
16323         */
16324         "dblclick":true,
16325         /**
16326         * @event contextmenu
16327         * Fires when a node is right clicked
16328         * @param {Node} node The node
16329         * @param {Roo.EventObject} e The event object
16330         */
16331         "contextmenu":true,
16332         /**
16333         * @event beforechildrenrendered
16334         * Fires right before the child nodes for a node are rendered
16335         * @param {Node} node The node
16336         */
16337         "beforechildrenrendered":true,
16338        /**
16339              * @event startdrag
16340              * Fires when a node starts being dragged
16341              * @param {Roo.tree.TreePanel} this
16342              * @param {Roo.tree.TreeNode} node
16343              * @param {event} e The raw browser event
16344              */ 
16345             "startdrag" : true,
16346             /**
16347              * @event enddrag
16348              * Fires when a drag operation is complete
16349              * @param {Roo.tree.TreePanel} this
16350              * @param {Roo.tree.TreeNode} node
16351              * @param {event} e The raw browser event
16352              */
16353             "enddrag" : true,
16354             /**
16355              * @event dragdrop
16356              * Fires when a dragged node is dropped on a valid DD target
16357              * @param {Roo.tree.TreePanel} this
16358              * @param {Roo.tree.TreeNode} node
16359              * @param {DD} dd The dd it was dropped on
16360              * @param {event} e The raw browser event
16361              */
16362             "dragdrop" : true,
16363             /**
16364              * @event beforenodedrop
16365              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16366              * passed to handlers has the following properties:<br />
16367              * <ul style="padding:5px;padding-left:16px;">
16368              * <li>tree - The TreePanel</li>
16369              * <li>target - The node being targeted for the drop</li>
16370              * <li>data - The drag data from the drag source</li>
16371              * <li>point - The point of the drop - append, above or below</li>
16372              * <li>source - The drag source</li>
16373              * <li>rawEvent - Raw mouse event</li>
16374              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16375              * to be inserted by setting them on this object.</li>
16376              * <li>cancel - Set this to true to cancel the drop.</li>
16377              * </ul>
16378              * @param {Object} dropEvent
16379              */
16380             "beforenodedrop" : true,
16381             /**
16382              * @event nodedrop
16383              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16384              * passed to handlers has the following properties:<br />
16385              * <ul style="padding:5px;padding-left:16px;">
16386              * <li>tree - The TreePanel</li>
16387              * <li>target - The node being targeted for the drop</li>
16388              * <li>data - The drag data from the drag source</li>
16389              * <li>point - The point of the drop - append, above or below</li>
16390              * <li>source - The drag source</li>
16391              * <li>rawEvent - Raw mouse event</li>
16392              * <li>dropNode - Dropped node(s).</li>
16393              * </ul>
16394              * @param {Object} dropEvent
16395              */
16396             "nodedrop" : true,
16397              /**
16398              * @event nodedragover
16399              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16400              * passed to handlers has the following properties:<br />
16401              * <ul style="padding:5px;padding-left:16px;">
16402              * <li>tree - The TreePanel</li>
16403              * <li>target - The node being targeted for the drop</li>
16404              * <li>data - The drag data from the drag source</li>
16405              * <li>point - The point of the drop - append, above or below</li>
16406              * <li>source - The drag source</li>
16407              * <li>rawEvent - Raw mouse event</li>
16408              * <li>dropNode - Drop node(s) provided by the source.</li>
16409              * <li>cancel - Set this to true to signal drop not allowed.</li>
16410              * </ul>
16411              * @param {Object} dragOverEvent
16412              */
16413             "nodedragover" : true
16414         
16415    });
16416    if(this.singleExpand){
16417        this.on("beforeexpand", this.restrictExpand, this);
16418    }
16419 };
16420 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16421     rootVisible : true,
16422     animate: Roo.enableFx,
16423     lines : true,
16424     enableDD : false,
16425     hlDrop : Roo.enableFx,
16426   
16427     renderer: false,
16428     
16429     rendererTip: false,
16430     // private
16431     restrictExpand : function(node){
16432         var p = node.parentNode;
16433         if(p){
16434             if(p.expandedChild && p.expandedChild.parentNode == p){
16435                 p.expandedChild.collapse();
16436             }
16437             p.expandedChild = node;
16438         }
16439     },
16440
16441     // private override
16442     setRootNode : function(node){
16443         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16444         if(!this.rootVisible){
16445             node.ui = new Roo.tree.RootTreeNodeUI(node);
16446         }
16447         return node;
16448     },
16449
16450     /**
16451      * Returns the container element for this TreePanel
16452      */
16453     getEl : function(){
16454         return this.el;
16455     },
16456
16457     /**
16458      * Returns the default TreeLoader for this TreePanel
16459      */
16460     getLoader : function(){
16461         return this.loader;
16462     },
16463
16464     /**
16465      * Expand all nodes
16466      */
16467     expandAll : function(){
16468         this.root.expand(true);
16469     },
16470
16471     /**
16472      * Collapse all nodes
16473      */
16474     collapseAll : function(){
16475         this.root.collapse(true);
16476     },
16477
16478     /**
16479      * Returns the selection model used by this TreePanel
16480      */
16481     getSelectionModel : function(){
16482         if(!this.selModel){
16483             this.selModel = new Roo.tree.DefaultSelectionModel();
16484         }
16485         return this.selModel;
16486     },
16487
16488     /**
16489      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16490      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16491      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16492      * @return {Array}
16493      */
16494     getChecked : function(a, startNode){
16495         startNode = startNode || this.root;
16496         var r = [];
16497         var f = function(){
16498             if(this.attributes.checked){
16499                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16500             }
16501         }
16502         startNode.cascade(f);
16503         return r;
16504     },
16505
16506     /**
16507      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16508      * @param {String} path
16509      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16510      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16511      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16512      */
16513     expandPath : function(path, attr, callback){
16514         attr = attr || "id";
16515         var keys = path.split(this.pathSeparator);
16516         var curNode = this.root;
16517         if(curNode.attributes[attr] != keys[1]){ // invalid root
16518             if(callback){
16519                 callback(false, null);
16520             }
16521             return;
16522         }
16523         var index = 1;
16524         var f = function(){
16525             if(++index == keys.length){
16526                 if(callback){
16527                     callback(true, curNode);
16528                 }
16529                 return;
16530             }
16531             var c = curNode.findChild(attr, keys[index]);
16532             if(!c){
16533                 if(callback){
16534                     callback(false, curNode);
16535                 }
16536                 return;
16537             }
16538             curNode = c;
16539             c.expand(false, false, f);
16540         };
16541         curNode.expand(false, false, f);
16542     },
16543
16544     /**
16545      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16546      * @param {String} path
16547      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16548      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16549      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16550      */
16551     selectPath : function(path, attr, callback){
16552         attr = attr || "id";
16553         var keys = path.split(this.pathSeparator);
16554         var v = keys.pop();
16555         if(keys.length > 0){
16556             var f = function(success, node){
16557                 if(success && node){
16558                     var n = node.findChild(attr, v);
16559                     if(n){
16560                         n.select();
16561                         if(callback){
16562                             callback(true, n);
16563                         }
16564                     }else if(callback){
16565                         callback(false, n);
16566                     }
16567                 }else{
16568                     if(callback){
16569                         callback(false, n);
16570                     }
16571                 }
16572             };
16573             this.expandPath(keys.join(this.pathSeparator), attr, f);
16574         }else{
16575             this.root.select();
16576             if(callback){
16577                 callback(true, this.root);
16578             }
16579         }
16580     },
16581
16582     getTreeEl : function(){
16583         return this.el;
16584     },
16585
16586     /**
16587      * Trigger rendering of this TreePanel
16588      */
16589     render : function(){
16590         if (this.innerCt) {
16591             return this; // stop it rendering more than once!!
16592         }
16593         
16594         this.innerCt = this.el.createChild({tag:"ul",
16595                cls:"x-tree-root-ct " +
16596                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16597
16598         if(this.containerScroll){
16599             Roo.dd.ScrollManager.register(this.el);
16600         }
16601         if((this.enableDD || this.enableDrop) && !this.dropZone){
16602            /**
16603             * The dropZone used by this tree if drop is enabled
16604             * @type Roo.tree.TreeDropZone
16605             */
16606              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16607                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16608            });
16609         }
16610         if((this.enableDD || this.enableDrag) && !this.dragZone){
16611            /**
16612             * The dragZone used by this tree if drag is enabled
16613             * @type Roo.tree.TreeDragZone
16614             */
16615             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16616                ddGroup: this.ddGroup || "TreeDD",
16617                scroll: this.ddScroll
16618            });
16619         }
16620         this.getSelectionModel().init(this);
16621         if (!this.root) {
16622             console.log("ROOT not set in tree");
16623             return;
16624         }
16625         this.root.render();
16626         if(!this.rootVisible){
16627             this.root.renderChildren();
16628         }
16629         return this;
16630     }
16631 });/*
16632  * Based on:
16633  * Ext JS Library 1.1.1
16634  * Copyright(c) 2006-2007, Ext JS, LLC.
16635  *
16636  * Originally Released Under LGPL - original licence link has changed is not relivant.
16637  *
16638  * Fork - LGPL
16639  * <script type="text/javascript">
16640  */
16641  
16642
16643 /**
16644  * @class Roo.tree.DefaultSelectionModel
16645  * @extends Roo.util.Observable
16646  * The default single selection for a TreePanel.
16647  */
16648 Roo.tree.DefaultSelectionModel = function(){
16649    this.selNode = null;
16650    
16651    this.addEvents({
16652        /**
16653         * @event selectionchange
16654         * Fires when the selected node changes
16655         * @param {DefaultSelectionModel} this
16656         * @param {TreeNode} node the new selection
16657         */
16658        "selectionchange" : true,
16659
16660        /**
16661         * @event beforeselect
16662         * Fires before the selected node changes, return false to cancel the change
16663         * @param {DefaultSelectionModel} this
16664         * @param {TreeNode} node the new selection
16665         * @param {TreeNode} node the old selection
16666         */
16667        "beforeselect" : true
16668    });
16669 };
16670
16671 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16672     init : function(tree){
16673         this.tree = tree;
16674         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16675         tree.on("click", this.onNodeClick, this);
16676     },
16677     
16678     onNodeClick : function(node, e){
16679         if (e.ctrlKey && this.selNode == node)  {
16680             this.unselect(node);
16681             return;
16682         }
16683         this.select(node);
16684     },
16685     
16686     /**
16687      * Select a node.
16688      * @param {TreeNode} node The node to select
16689      * @return {TreeNode} The selected node
16690      */
16691     select : function(node){
16692         var last = this.selNode;
16693         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16694             if(last){
16695                 last.ui.onSelectedChange(false);
16696             }
16697             this.selNode = node;
16698             node.ui.onSelectedChange(true);
16699             this.fireEvent("selectionchange", this, node, last);
16700         }
16701         return node;
16702     },
16703     
16704     /**
16705      * Deselect a node.
16706      * @param {TreeNode} node The node to unselect
16707      */
16708     unselect : function(node){
16709         if(this.selNode == node){
16710             this.clearSelections();
16711         }    
16712     },
16713     
16714     /**
16715      * Clear all selections
16716      */
16717     clearSelections : function(){
16718         var n = this.selNode;
16719         if(n){
16720             n.ui.onSelectedChange(false);
16721             this.selNode = null;
16722             this.fireEvent("selectionchange", this, null);
16723         }
16724         return n;
16725     },
16726     
16727     /**
16728      * Get the selected node
16729      * @return {TreeNode} The selected node
16730      */
16731     getSelectedNode : function(){
16732         return this.selNode;    
16733     },
16734     
16735     /**
16736      * Returns true if the node is selected
16737      * @param {TreeNode} node The node to check
16738      * @return {Boolean}
16739      */
16740     isSelected : function(node){
16741         return this.selNode == node;  
16742     },
16743
16744     /**
16745      * Selects the node above the selected node in the tree, intelligently walking the nodes
16746      * @return TreeNode The new selection
16747      */
16748     selectPrevious : function(){
16749         var s = this.selNode || this.lastSelNode;
16750         if(!s){
16751             return null;
16752         }
16753         var ps = s.previousSibling;
16754         if(ps){
16755             if(!ps.isExpanded() || ps.childNodes.length < 1){
16756                 return this.select(ps);
16757             } else{
16758                 var lc = ps.lastChild;
16759                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16760                     lc = lc.lastChild;
16761                 }
16762                 return this.select(lc);
16763             }
16764         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16765             return this.select(s.parentNode);
16766         }
16767         return null;
16768     },
16769
16770     /**
16771      * Selects the node above the selected node in the tree, intelligently walking the nodes
16772      * @return TreeNode The new selection
16773      */
16774     selectNext : function(){
16775         var s = this.selNode || this.lastSelNode;
16776         if(!s){
16777             return null;
16778         }
16779         if(s.firstChild && s.isExpanded()){
16780              return this.select(s.firstChild);
16781          }else if(s.nextSibling){
16782              return this.select(s.nextSibling);
16783          }else if(s.parentNode){
16784             var newS = null;
16785             s.parentNode.bubble(function(){
16786                 if(this.nextSibling){
16787                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16788                     return false;
16789                 }
16790             });
16791             return newS;
16792          }
16793         return null;
16794     },
16795
16796     onKeyDown : function(e){
16797         var s = this.selNode || this.lastSelNode;
16798         // undesirable, but required
16799         var sm = this;
16800         if(!s){
16801             return;
16802         }
16803         var k = e.getKey();
16804         switch(k){
16805              case e.DOWN:
16806                  e.stopEvent();
16807                  this.selectNext();
16808              break;
16809              case e.UP:
16810                  e.stopEvent();
16811                  this.selectPrevious();
16812              break;
16813              case e.RIGHT:
16814                  e.preventDefault();
16815                  if(s.hasChildNodes()){
16816                      if(!s.isExpanded()){
16817                          s.expand();
16818                      }else if(s.firstChild){
16819                          this.select(s.firstChild, e);
16820                      }
16821                  }
16822              break;
16823              case e.LEFT:
16824                  e.preventDefault();
16825                  if(s.hasChildNodes() && s.isExpanded()){
16826                      s.collapse();
16827                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16828                      this.select(s.parentNode, e);
16829                  }
16830              break;
16831         };
16832     }
16833 });
16834
16835 /**
16836  * @class Roo.tree.MultiSelectionModel
16837  * @extends Roo.util.Observable
16838  * Multi selection for a TreePanel.
16839  */
16840 Roo.tree.MultiSelectionModel = function(){
16841    this.selNodes = [];
16842    this.selMap = {};
16843    this.addEvents({
16844        /**
16845         * @event selectionchange
16846         * Fires when the selected nodes change
16847         * @param {MultiSelectionModel} this
16848         * @param {Array} nodes Array of the selected nodes
16849         */
16850        "selectionchange" : true
16851    });
16852 };
16853
16854 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16855     init : function(tree){
16856         this.tree = tree;
16857         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16858         tree.on("click", this.onNodeClick, this);
16859     },
16860     
16861     onNodeClick : function(node, e){
16862         this.select(node, e, e.ctrlKey);
16863     },
16864     
16865     /**
16866      * Select a node.
16867      * @param {TreeNode} node The node to select
16868      * @param {EventObject} e (optional) An event associated with the selection
16869      * @param {Boolean} keepExisting True to retain existing selections
16870      * @return {TreeNode} The selected node
16871      */
16872     select : function(node, e, keepExisting){
16873         if(keepExisting !== true){
16874             this.clearSelections(true);
16875         }
16876         if(this.isSelected(node)){
16877             this.lastSelNode = node;
16878             return node;
16879         }
16880         this.selNodes.push(node);
16881         this.selMap[node.id] = node;
16882         this.lastSelNode = node;
16883         node.ui.onSelectedChange(true);
16884         this.fireEvent("selectionchange", this, this.selNodes);
16885         return node;
16886     },
16887     
16888     /**
16889      * Deselect a node.
16890      * @param {TreeNode} node The node to unselect
16891      */
16892     unselect : function(node){
16893         if(this.selMap[node.id]){
16894             node.ui.onSelectedChange(false);
16895             var sn = this.selNodes;
16896             var index = -1;
16897             if(sn.indexOf){
16898                 index = sn.indexOf(node);
16899             }else{
16900                 for(var i = 0, len = sn.length; i < len; i++){
16901                     if(sn[i] == node){
16902                         index = i;
16903                         break;
16904                     }
16905                 }
16906             }
16907             if(index != -1){
16908                 this.selNodes.splice(index, 1);
16909             }
16910             delete this.selMap[node.id];
16911             this.fireEvent("selectionchange", this, this.selNodes);
16912         }
16913     },
16914     
16915     /**
16916      * Clear all selections
16917      */
16918     clearSelections : function(suppressEvent){
16919         var sn = this.selNodes;
16920         if(sn.length > 0){
16921             for(var i = 0, len = sn.length; i < len; i++){
16922                 sn[i].ui.onSelectedChange(false);
16923             }
16924             this.selNodes = [];
16925             this.selMap = {};
16926             if(suppressEvent !== true){
16927                 this.fireEvent("selectionchange", this, this.selNodes);
16928             }
16929         }
16930     },
16931     
16932     /**
16933      * Returns true if the node is selected
16934      * @param {TreeNode} node The node to check
16935      * @return {Boolean}
16936      */
16937     isSelected : function(node){
16938         return this.selMap[node.id] ? true : false;  
16939     },
16940     
16941     /**
16942      * Returns an array of the selected nodes
16943      * @return {Array}
16944      */
16945     getSelectedNodes : function(){
16946         return this.selNodes;    
16947     },
16948
16949     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
16950
16951     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
16952
16953     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
16954 });/*
16955  * Based on:
16956  * Ext JS Library 1.1.1
16957  * Copyright(c) 2006-2007, Ext JS, LLC.
16958  *
16959  * Originally Released Under LGPL - original licence link has changed is not relivant.
16960  *
16961  * Fork - LGPL
16962  * <script type="text/javascript">
16963  */
16964  
16965 /**
16966  * @class Roo.tree.TreeNode
16967  * @extends Roo.data.Node
16968  * @cfg {String} text The text for this node
16969  * @cfg {Boolean} expanded true to start the node expanded
16970  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
16971  * @cfg {Boolean} allowDrop false if this node cannot be drop on
16972  * @cfg {Boolean} disabled true to start the node disabled
16973  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
16974  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
16975  * @cfg {String} cls A css class to be added to the node
16976  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
16977  * @cfg {String} href URL of the link used for the node (defaults to #)
16978  * @cfg {String} hrefTarget target frame for the link
16979  * @cfg {String} qtip An Ext QuickTip for the node
16980  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
16981  * @cfg {Boolean} singleClickExpand True for single click expand on this node
16982  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
16983  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
16984  * (defaults to undefined with no checkbox rendered)
16985  * @constructor
16986  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
16987  */
16988 Roo.tree.TreeNode = function(attributes){
16989     attributes = attributes || {};
16990     if(typeof attributes == "string"){
16991         attributes = {text: attributes};
16992     }
16993     this.childrenRendered = false;
16994     this.rendered = false;
16995     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
16996     this.expanded = attributes.expanded === true;
16997     this.isTarget = attributes.isTarget !== false;
16998     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
16999     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17000
17001     /**
17002      * Read-only. The text for this node. To change it use setText().
17003      * @type String
17004      */
17005     this.text = attributes.text;
17006     /**
17007      * True if this node is disabled.
17008      * @type Boolean
17009      */
17010     this.disabled = attributes.disabled === true;
17011
17012     this.addEvents({
17013         /**
17014         * @event textchange
17015         * Fires when the text for this node is changed
17016         * @param {Node} this This node
17017         * @param {String} text The new text
17018         * @param {String} oldText The old text
17019         */
17020         "textchange" : true,
17021         /**
17022         * @event beforeexpand
17023         * Fires before this node is expanded, return false to cancel.
17024         * @param {Node} this This node
17025         * @param {Boolean} deep
17026         * @param {Boolean} anim
17027         */
17028         "beforeexpand" : true,
17029         /**
17030         * @event beforecollapse
17031         * Fires before this node is collapsed, return false to cancel.
17032         * @param {Node} this This node
17033         * @param {Boolean} deep
17034         * @param {Boolean} anim
17035         */
17036         "beforecollapse" : true,
17037         /**
17038         * @event expand
17039         * Fires when this node is expanded
17040         * @param {Node} this This node
17041         */
17042         "expand" : true,
17043         /**
17044         * @event disabledchange
17045         * Fires when the disabled status of this node changes
17046         * @param {Node} this This node
17047         * @param {Boolean} disabled
17048         */
17049         "disabledchange" : true,
17050         /**
17051         * @event collapse
17052         * Fires when this node is collapsed
17053         * @param {Node} this This node
17054         */
17055         "collapse" : true,
17056         /**
17057         * @event beforeclick
17058         * Fires before click processing. Return false to cancel the default action.
17059         * @param {Node} this This node
17060         * @param {Roo.EventObject} e The event object
17061         */
17062         "beforeclick":true,
17063         /**
17064         * @event checkchange
17065         * Fires when a node with a checkbox's checked property changes
17066         * @param {Node} this This node
17067         * @param {Boolean} checked
17068         */
17069         "checkchange":true,
17070         /**
17071         * @event click
17072         * Fires when this node is clicked
17073         * @param {Node} this This node
17074         * @param {Roo.EventObject} e The event object
17075         */
17076         "click":true,
17077         /**
17078         * @event dblclick
17079         * Fires when this node is double clicked
17080         * @param {Node} this This node
17081         * @param {Roo.EventObject} e The event object
17082         */
17083         "dblclick":true,
17084         /**
17085         * @event contextmenu
17086         * Fires when this node is right clicked
17087         * @param {Node} this This node
17088         * @param {Roo.EventObject} e The event object
17089         */
17090         "contextmenu":true,
17091         /**
17092         * @event beforechildrenrendered
17093         * Fires right before the child nodes for this node are rendered
17094         * @param {Node} this This node
17095         */
17096         "beforechildrenrendered":true
17097     });
17098
17099     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17100
17101     /**
17102      * Read-only. The UI for this node
17103      * @type TreeNodeUI
17104      */
17105     this.ui = new uiClass(this);
17106 };
17107 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17108     preventHScroll: true,
17109     /**
17110      * Returns true if this node is expanded
17111      * @return {Boolean}
17112      */
17113     isExpanded : function(){
17114         return this.expanded;
17115     },
17116
17117     /**
17118      * Returns the UI object for this node
17119      * @return {TreeNodeUI}
17120      */
17121     getUI : function(){
17122         return this.ui;
17123     },
17124
17125     // private override
17126     setFirstChild : function(node){
17127         var of = this.firstChild;
17128         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17129         if(this.childrenRendered && of && node != of){
17130             of.renderIndent(true, true);
17131         }
17132         if(this.rendered){
17133             this.renderIndent(true, true);
17134         }
17135     },
17136
17137     // private override
17138     setLastChild : function(node){
17139         var ol = this.lastChild;
17140         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17141         if(this.childrenRendered && ol && node != ol){
17142             ol.renderIndent(true, true);
17143         }
17144         if(this.rendered){
17145             this.renderIndent(true, true);
17146         }
17147     },
17148
17149     // these methods are overridden to provide lazy rendering support
17150     // private override
17151     appendChild : function(){
17152         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17153         if(node && this.childrenRendered){
17154             node.render();
17155         }
17156         this.ui.updateExpandIcon();
17157         return node;
17158     },
17159
17160     // private override
17161     removeChild : function(node){
17162         this.ownerTree.getSelectionModel().unselect(node);
17163         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17164         // if it's been rendered remove dom node
17165         if(this.childrenRendered){
17166             node.ui.remove();
17167         }
17168         if(this.childNodes.length < 1){
17169             this.collapse(false, false);
17170         }else{
17171             this.ui.updateExpandIcon();
17172         }
17173         if(!this.firstChild) {
17174             this.childrenRendered = false;
17175         }
17176         return node;
17177     },
17178
17179     // private override
17180     insertBefore : function(node, refNode){
17181         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17182         if(newNode && refNode && this.childrenRendered){
17183             node.render();
17184         }
17185         this.ui.updateExpandIcon();
17186         return newNode;
17187     },
17188
17189     /**
17190      * Sets the text for this node
17191      * @param {String} text
17192      */
17193     setText : function(text){
17194         var oldText = this.text;
17195         this.text = text;
17196         this.attributes.text = text;
17197         if(this.rendered){ // event without subscribing
17198             this.ui.onTextChange(this, text, oldText);
17199         }
17200         this.fireEvent("textchange", this, text, oldText);
17201     },
17202
17203     /**
17204      * Triggers selection of this node
17205      */
17206     select : function(){
17207         this.getOwnerTree().getSelectionModel().select(this);
17208     },
17209
17210     /**
17211      * Triggers deselection of this node
17212      */
17213     unselect : function(){
17214         this.getOwnerTree().getSelectionModel().unselect(this);
17215     },
17216
17217     /**
17218      * Returns true if this node is selected
17219      * @return {Boolean}
17220      */
17221     isSelected : function(){
17222         return this.getOwnerTree().getSelectionModel().isSelected(this);
17223     },
17224
17225     /**
17226      * Expand this node.
17227      * @param {Boolean} deep (optional) True to expand all children as well
17228      * @param {Boolean} anim (optional) false to cancel the default animation
17229      * @param {Function} callback (optional) A callback to be called when
17230      * expanding this node completes (does not wait for deep expand to complete).
17231      * Called with 1 parameter, this node.
17232      */
17233     expand : function(deep, anim, callback){
17234         if(!this.expanded){
17235             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17236                 return;
17237             }
17238             if(!this.childrenRendered){
17239                 this.renderChildren();
17240             }
17241             this.expanded = true;
17242             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17243                 this.ui.animExpand(function(){
17244                     this.fireEvent("expand", this);
17245                     if(typeof callback == "function"){
17246                         callback(this);
17247                     }
17248                     if(deep === true){
17249                         this.expandChildNodes(true);
17250                     }
17251                 }.createDelegate(this));
17252                 return;
17253             }else{
17254                 this.ui.expand();
17255                 this.fireEvent("expand", this);
17256                 if(typeof callback == "function"){
17257                     callback(this);
17258                 }
17259             }
17260         }else{
17261            if(typeof callback == "function"){
17262                callback(this);
17263            }
17264         }
17265         if(deep === true){
17266             this.expandChildNodes(true);
17267         }
17268     },
17269
17270     isHiddenRoot : function(){
17271         return this.isRoot && !this.getOwnerTree().rootVisible;
17272     },
17273
17274     /**
17275      * Collapse this node.
17276      * @param {Boolean} deep (optional) True to collapse all children as well
17277      * @param {Boolean} anim (optional) false to cancel the default animation
17278      */
17279     collapse : function(deep, anim){
17280         if(this.expanded && !this.isHiddenRoot()){
17281             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17282                 return;
17283             }
17284             this.expanded = false;
17285             if((this.getOwnerTree().animate && anim !== false) || anim){
17286                 this.ui.animCollapse(function(){
17287                     this.fireEvent("collapse", this);
17288                     if(deep === true){
17289                         this.collapseChildNodes(true);
17290                     }
17291                 }.createDelegate(this));
17292                 return;
17293             }else{
17294                 this.ui.collapse();
17295                 this.fireEvent("collapse", this);
17296             }
17297         }
17298         if(deep === true){
17299             var cs = this.childNodes;
17300             for(var i = 0, len = cs.length; i < len; i++) {
17301                 cs[i].collapse(true, false);
17302             }
17303         }
17304     },
17305
17306     // private
17307     delayedExpand : function(delay){
17308         if(!this.expandProcId){
17309             this.expandProcId = this.expand.defer(delay, this);
17310         }
17311     },
17312
17313     // private
17314     cancelExpand : function(){
17315         if(this.expandProcId){
17316             clearTimeout(this.expandProcId);
17317         }
17318         this.expandProcId = false;
17319     },
17320
17321     /**
17322      * Toggles expanded/collapsed state of the node
17323      */
17324     toggle : function(){
17325         if(this.expanded){
17326             this.collapse();
17327         }else{
17328             this.expand();
17329         }
17330     },
17331
17332     /**
17333      * Ensures all parent nodes are expanded
17334      */
17335     ensureVisible : function(callback){
17336         var tree = this.getOwnerTree();
17337         tree.expandPath(this.parentNode.getPath(), false, function(){
17338             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17339             Roo.callback(callback);
17340         }.createDelegate(this));
17341     },
17342
17343     /**
17344      * Expand all child nodes
17345      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17346      */
17347     expandChildNodes : function(deep){
17348         var cs = this.childNodes;
17349         for(var i = 0, len = cs.length; i < len; i++) {
17350                 cs[i].expand(deep);
17351         }
17352     },
17353
17354     /**
17355      * Collapse all child nodes
17356      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17357      */
17358     collapseChildNodes : function(deep){
17359         var cs = this.childNodes;
17360         for(var i = 0, len = cs.length; i < len; i++) {
17361                 cs[i].collapse(deep);
17362         }
17363     },
17364
17365     /**
17366      * Disables this node
17367      */
17368     disable : function(){
17369         this.disabled = true;
17370         this.unselect();
17371         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17372             this.ui.onDisableChange(this, true);
17373         }
17374         this.fireEvent("disabledchange", this, true);
17375     },
17376
17377     /**
17378      * Enables this node
17379      */
17380     enable : function(){
17381         this.disabled = false;
17382         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17383             this.ui.onDisableChange(this, false);
17384         }
17385         this.fireEvent("disabledchange", this, false);
17386     },
17387
17388     // private
17389     renderChildren : function(suppressEvent){
17390         if(suppressEvent !== false){
17391             this.fireEvent("beforechildrenrendered", this);
17392         }
17393         var cs = this.childNodes;
17394         for(var i = 0, len = cs.length; i < len; i++){
17395             cs[i].render(true);
17396         }
17397         this.childrenRendered = true;
17398     },
17399
17400     // private
17401     sort : function(fn, scope){
17402         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17403         if(this.childrenRendered){
17404             var cs = this.childNodes;
17405             for(var i = 0, len = cs.length; i < len; i++){
17406                 cs[i].render(true);
17407             }
17408         }
17409     },
17410
17411     // private
17412     render : function(bulkRender){
17413         this.ui.render(bulkRender);
17414         if(!this.rendered){
17415             this.rendered = true;
17416             if(this.expanded){
17417                 this.expanded = false;
17418                 this.expand(false, false);
17419             }
17420         }
17421     },
17422
17423     // private
17424     renderIndent : function(deep, refresh){
17425         if(refresh){
17426             this.ui.childIndent = null;
17427         }
17428         this.ui.renderIndent();
17429         if(deep === true && this.childrenRendered){
17430             var cs = this.childNodes;
17431             for(var i = 0, len = cs.length; i < len; i++){
17432                 cs[i].renderIndent(true, refresh);
17433             }
17434         }
17435     }
17436 });/*
17437  * Based on:
17438  * Ext JS Library 1.1.1
17439  * Copyright(c) 2006-2007, Ext JS, LLC.
17440  *
17441  * Originally Released Under LGPL - original licence link has changed is not relivant.
17442  *
17443  * Fork - LGPL
17444  * <script type="text/javascript">
17445  */
17446  
17447 /**
17448  * @class Roo.tree.AsyncTreeNode
17449  * @extends Roo.tree.TreeNode
17450  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17451  * @constructor
17452  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17453  */
17454  Roo.tree.AsyncTreeNode = function(config){
17455     this.loaded = false;
17456     this.loading = false;
17457     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17458     /**
17459     * @event beforeload
17460     * Fires before this node is loaded, return false to cancel
17461     * @param {Node} this This node
17462     */
17463     this.addEvents({'beforeload':true, 'load': true});
17464     /**
17465     * @event load
17466     * Fires when this node is loaded
17467     * @param {Node} this This node
17468     */
17469     /**
17470      * The loader used by this node (defaults to using the tree's defined loader)
17471      * @type TreeLoader
17472      * @property loader
17473      */
17474 };
17475 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17476     expand : function(deep, anim, callback){
17477         if(this.loading){ // if an async load is already running, waiting til it's done
17478             var timer;
17479             var f = function(){
17480                 if(!this.loading){ // done loading
17481                     clearInterval(timer);
17482                     this.expand(deep, anim, callback);
17483                 }
17484             }.createDelegate(this);
17485             timer = setInterval(f, 200);
17486             return;
17487         }
17488         if(!this.loaded){
17489             if(this.fireEvent("beforeload", this) === false){
17490                 return;
17491             }
17492             this.loading = true;
17493             this.ui.beforeLoad(this);
17494             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17495             if(loader){
17496                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17497                 return;
17498             }
17499         }
17500         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17501     },
17502     
17503     /**
17504      * Returns true if this node is currently loading
17505      * @return {Boolean}
17506      */
17507     isLoading : function(){
17508         return this.loading;  
17509     },
17510     
17511     loadComplete : function(deep, anim, callback){
17512         this.loading = false;
17513         this.loaded = true;
17514         this.ui.afterLoad(this);
17515         this.fireEvent("load", this);
17516         this.expand(deep, anim, callback);
17517     },
17518     
17519     /**
17520      * Returns true if this node has been loaded
17521      * @return {Boolean}
17522      */
17523     isLoaded : function(){
17524         return this.loaded;
17525     },
17526     
17527     hasChildNodes : function(){
17528         if(!this.isLeaf() && !this.loaded){
17529             return true;
17530         }else{
17531             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17532         }
17533     },
17534
17535     /**
17536      * Trigger a reload for this node
17537      * @param {Function} callback
17538      */
17539     reload : function(callback){
17540         this.collapse(false, false);
17541         while(this.firstChild){
17542             this.removeChild(this.firstChild);
17543         }
17544         this.childrenRendered = false;
17545         this.loaded = false;
17546         if(this.isHiddenRoot()){
17547             this.expanded = false;
17548         }
17549         this.expand(false, false, callback);
17550     }
17551 });/*
17552  * Based on:
17553  * Ext JS Library 1.1.1
17554  * Copyright(c) 2006-2007, Ext JS, LLC.
17555  *
17556  * Originally Released Under LGPL - original licence link has changed is not relivant.
17557  *
17558  * Fork - LGPL
17559  * <script type="text/javascript">
17560  */
17561  
17562 /**
17563  * @class Roo.tree.TreeNodeUI
17564  * @constructor
17565  * @param {Object} node The node to render
17566  * The TreeNode UI implementation is separate from the
17567  * tree implementation. Unless you are customizing the tree UI,
17568  * you should never have to use this directly.
17569  */
17570 Roo.tree.TreeNodeUI = function(node){
17571     this.node = node;
17572     this.rendered = false;
17573     this.animating = false;
17574     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17575 };
17576
17577 Roo.tree.TreeNodeUI.prototype = {
17578     removeChild : function(node){
17579         if(this.rendered){
17580             this.ctNode.removeChild(node.ui.getEl());
17581         }
17582     },
17583
17584     beforeLoad : function(){
17585          this.addClass("x-tree-node-loading");
17586     },
17587
17588     afterLoad : function(){
17589          this.removeClass("x-tree-node-loading");
17590     },
17591
17592     onTextChange : function(node, text, oldText){
17593         if(this.rendered){
17594             this.textNode.innerHTML = text;
17595         }
17596     },
17597
17598     onDisableChange : function(node, state){
17599         this.disabled = state;
17600         if(state){
17601             this.addClass("x-tree-node-disabled");
17602         }else{
17603             this.removeClass("x-tree-node-disabled");
17604         }
17605     },
17606
17607     onSelectedChange : function(state){
17608         if(state){
17609             this.focus();
17610             this.addClass("x-tree-selected");
17611         }else{
17612             //this.blur();
17613             this.removeClass("x-tree-selected");
17614         }
17615     },
17616
17617     onMove : function(tree, node, oldParent, newParent, index, refNode){
17618         this.childIndent = null;
17619         if(this.rendered){
17620             var targetNode = newParent.ui.getContainer();
17621             if(!targetNode){//target not rendered
17622                 this.holder = document.createElement("div");
17623                 this.holder.appendChild(this.wrap);
17624                 return;
17625             }
17626             var insertBefore = refNode ? refNode.ui.getEl() : null;
17627             if(insertBefore){
17628                 targetNode.insertBefore(this.wrap, insertBefore);
17629             }else{
17630                 targetNode.appendChild(this.wrap);
17631             }
17632             this.node.renderIndent(true);
17633         }
17634     },
17635
17636     addClass : function(cls){
17637         if(this.elNode){
17638             Roo.fly(this.elNode).addClass(cls);
17639         }
17640     },
17641
17642     removeClass : function(cls){
17643         if(this.elNode){
17644             Roo.fly(this.elNode).removeClass(cls);
17645         }
17646     },
17647
17648     remove : function(){
17649         if(this.rendered){
17650             this.holder = document.createElement("div");
17651             this.holder.appendChild(this.wrap);
17652         }
17653     },
17654
17655     fireEvent : function(){
17656         return this.node.fireEvent.apply(this.node, arguments);
17657     },
17658
17659     initEvents : function(){
17660         this.node.on("move", this.onMove, this);
17661         var E = Roo.EventManager;
17662         var a = this.anchor;
17663
17664         var el = Roo.fly(a, '_treeui');
17665
17666         if(Roo.isOpera){ // opera render bug ignores the CSS
17667             el.setStyle("text-decoration", "none");
17668         }
17669
17670         el.on("click", this.onClick, this);
17671         el.on("dblclick", this.onDblClick, this);
17672
17673         if(this.checkbox){
17674             Roo.EventManager.on(this.checkbox,
17675                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17676         }
17677
17678         el.on("contextmenu", this.onContextMenu, this);
17679
17680         var icon = Roo.fly(this.iconNode);
17681         icon.on("click", this.onClick, this);
17682         icon.on("dblclick", this.onDblClick, this);
17683         icon.on("contextmenu", this.onContextMenu, this);
17684         E.on(this.ecNode, "click", this.ecClick, this, true);
17685
17686         if(this.node.disabled){
17687             this.addClass("x-tree-node-disabled");
17688         }
17689         if(this.node.hidden){
17690             this.addClass("x-tree-node-disabled");
17691         }
17692         var ot = this.node.getOwnerTree();
17693         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17694         if(dd && (!this.node.isRoot || ot.rootVisible)){
17695             Roo.dd.Registry.register(this.elNode, {
17696                 node: this.node,
17697                 handles: this.getDDHandles(),
17698                 isHandle: false
17699             });
17700         }
17701     },
17702
17703     getDDHandles : function(){
17704         return [this.iconNode, this.textNode];
17705     },
17706
17707     hide : function(){
17708         if(this.rendered){
17709             this.wrap.style.display = "none";
17710         }
17711     },
17712
17713     show : function(){
17714         if(this.rendered){
17715             this.wrap.style.display = "";
17716         }
17717     },
17718
17719     onContextMenu : function(e){
17720         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17721             e.preventDefault();
17722             this.focus();
17723             this.fireEvent("contextmenu", this.node, e);
17724         }
17725     },
17726
17727     onClick : function(e){
17728         if(this.dropping){
17729             e.stopEvent();
17730             return;
17731         }
17732         if(this.fireEvent("beforeclick", this.node, e) !== false){
17733             if(!this.disabled && this.node.attributes.href){
17734                 this.fireEvent("click", this.node, e);
17735                 return;
17736             }
17737             e.preventDefault();
17738             if(this.disabled){
17739                 return;
17740             }
17741
17742             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17743                 this.node.toggle();
17744             }
17745
17746             this.fireEvent("click", this.node, e);
17747         }else{
17748             e.stopEvent();
17749         }
17750     },
17751
17752     onDblClick : function(e){
17753         e.preventDefault();
17754         if(this.disabled){
17755             return;
17756         }
17757         if(this.checkbox){
17758             this.toggleCheck();
17759         }
17760         if(!this.animating && this.node.hasChildNodes()){
17761             this.node.toggle();
17762         }
17763         this.fireEvent("dblclick", this.node, e);
17764     },
17765
17766     onCheckChange : function(){
17767         var checked = this.checkbox.checked;
17768         this.node.attributes.checked = checked;
17769         this.fireEvent('checkchange', this.node, checked);
17770     },
17771
17772     ecClick : function(e){
17773         if(!this.animating && this.node.hasChildNodes()){
17774             this.node.toggle();
17775         }
17776     },
17777
17778     startDrop : function(){
17779         this.dropping = true;
17780     },
17781
17782     // delayed drop so the click event doesn't get fired on a drop
17783     endDrop : function(){
17784        setTimeout(function(){
17785            this.dropping = false;
17786        }.createDelegate(this), 50);
17787     },
17788
17789     expand : function(){
17790         this.updateExpandIcon();
17791         this.ctNode.style.display = "";
17792     },
17793
17794     focus : function(){
17795         if(!this.node.preventHScroll){
17796             try{this.anchor.focus();
17797             }catch(e){}
17798         }else if(!Roo.isIE){
17799             try{
17800                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17801                 var l = noscroll.scrollLeft;
17802                 this.anchor.focus();
17803                 noscroll.scrollLeft = l;
17804             }catch(e){}
17805         }
17806     },
17807
17808     toggleCheck : function(value){
17809         var cb = this.checkbox;
17810         if(cb){
17811             cb.checked = (value === undefined ? !cb.checked : value);
17812         }
17813     },
17814
17815     blur : function(){
17816         try{
17817             this.anchor.blur();
17818         }catch(e){}
17819     },
17820
17821     animExpand : function(callback){
17822         var ct = Roo.get(this.ctNode);
17823         ct.stopFx();
17824         if(!this.node.hasChildNodes()){
17825             this.updateExpandIcon();
17826             this.ctNode.style.display = "";
17827             Roo.callback(callback);
17828             return;
17829         }
17830         this.animating = true;
17831         this.updateExpandIcon();
17832
17833         ct.slideIn('t', {
17834            callback : function(){
17835                this.animating = false;
17836                Roo.callback(callback);
17837             },
17838             scope: this,
17839             duration: this.node.ownerTree.duration || .25
17840         });
17841     },
17842
17843     highlight : function(){
17844         var tree = this.node.getOwnerTree();
17845         Roo.fly(this.wrap).highlight(
17846             tree.hlColor || "C3DAF9",
17847             {endColor: tree.hlBaseColor}
17848         );
17849     },
17850
17851     collapse : function(){
17852         this.updateExpandIcon();
17853         this.ctNode.style.display = "none";
17854     },
17855
17856     animCollapse : function(callback){
17857         var ct = Roo.get(this.ctNode);
17858         ct.enableDisplayMode('block');
17859         ct.stopFx();
17860
17861         this.animating = true;
17862         this.updateExpandIcon();
17863
17864         ct.slideOut('t', {
17865             callback : function(){
17866                this.animating = false;
17867                Roo.callback(callback);
17868             },
17869             scope: this,
17870             duration: this.node.ownerTree.duration || .25
17871         });
17872     },
17873
17874     getContainer : function(){
17875         return this.ctNode;
17876     },
17877
17878     getEl : function(){
17879         return this.wrap;
17880     },
17881
17882     appendDDGhost : function(ghostNode){
17883         ghostNode.appendChild(this.elNode.cloneNode(true));
17884     },
17885
17886     getDDRepairXY : function(){
17887         return Roo.lib.Dom.getXY(this.iconNode);
17888     },
17889
17890     onRender : function(){
17891         this.render();
17892     },
17893
17894     render : function(bulkRender){
17895         var n = this.node, a = n.attributes;
17896         var targetNode = n.parentNode ?
17897               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17898
17899         if(!this.rendered){
17900             this.rendered = true;
17901
17902             this.renderElements(n, a, targetNode, bulkRender);
17903
17904             if(a.qtip){
17905                if(this.textNode.setAttributeNS){
17906                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17907                    if(a.qtipTitle){
17908                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17909                    }
17910                }else{
17911                    this.textNode.setAttribute("ext:qtip", a.qtip);
17912                    if(a.qtipTitle){
17913                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17914                    }
17915                }
17916             }else if(a.qtipCfg){
17917                 a.qtipCfg.target = Roo.id(this.textNode);
17918                 Roo.QuickTips.register(a.qtipCfg);
17919             }
17920             this.initEvents();
17921             if(!this.node.expanded){
17922                 this.updateExpandIcon();
17923             }
17924         }else{
17925             if(bulkRender === true) {
17926                 targetNode.appendChild(this.wrap);
17927             }
17928         }
17929     },
17930
17931     renderElements : function(n, a, targetNode, bulkRender){
17932         // add some indent caching, this helps performance when rendering a large tree
17933         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
17934         var t = n.getOwnerTree();
17935         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
17936         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
17937         var cb = typeof a.checked == 'boolean';
17938         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
17939         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
17940             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
17941             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
17942             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
17943             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
17944             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
17945              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
17946                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
17947             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
17948             "</li>"];
17949
17950         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
17951             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
17952                                 n.nextSibling.ui.getEl(), buf.join(""));
17953         }else{
17954             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
17955         }
17956
17957         this.elNode = this.wrap.childNodes[0];
17958         this.ctNode = this.wrap.childNodes[1];
17959         var cs = this.elNode.childNodes;
17960         this.indentNode = cs[0];
17961         this.ecNode = cs[1];
17962         this.iconNode = cs[2];
17963         var index = 3;
17964         if(cb){
17965             this.checkbox = cs[3];
17966             index++;
17967         }
17968         this.anchor = cs[index];
17969         this.textNode = cs[index].firstChild;
17970     },
17971
17972     getAnchor : function(){
17973         return this.anchor;
17974     },
17975
17976     getTextEl : function(){
17977         return this.textNode;
17978     },
17979
17980     getIconEl : function(){
17981         return this.iconNode;
17982     },
17983
17984     isChecked : function(){
17985         return this.checkbox ? this.checkbox.checked : false;
17986     },
17987
17988     updateExpandIcon : function(){
17989         if(this.rendered){
17990             var n = this.node, c1, c2;
17991             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
17992             var hasChild = n.hasChildNodes();
17993             if(hasChild){
17994                 if(n.expanded){
17995                     cls += "-minus";
17996                     c1 = "x-tree-node-collapsed";
17997                     c2 = "x-tree-node-expanded";
17998                 }else{
17999                     cls += "-plus";
18000                     c1 = "x-tree-node-expanded";
18001                     c2 = "x-tree-node-collapsed";
18002                 }
18003                 if(this.wasLeaf){
18004                     this.removeClass("x-tree-node-leaf");
18005                     this.wasLeaf = false;
18006                 }
18007                 if(this.c1 != c1 || this.c2 != c2){
18008                     Roo.fly(this.elNode).replaceClass(c1, c2);
18009                     this.c1 = c1; this.c2 = c2;
18010                 }
18011             }else{
18012                 if(!this.wasLeaf){
18013                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18014                     delete this.c1;
18015                     delete this.c2;
18016                     this.wasLeaf = true;
18017                 }
18018             }
18019             var ecc = "x-tree-ec-icon "+cls;
18020             if(this.ecc != ecc){
18021                 this.ecNode.className = ecc;
18022                 this.ecc = ecc;
18023             }
18024         }
18025     },
18026
18027     getChildIndent : function(){
18028         if(!this.childIndent){
18029             var buf = [];
18030             var p = this.node;
18031             while(p){
18032                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18033                     if(!p.isLast()) {
18034                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18035                     } else {
18036                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18037                     }
18038                 }
18039                 p = p.parentNode;
18040             }
18041             this.childIndent = buf.join("");
18042         }
18043         return this.childIndent;
18044     },
18045
18046     renderIndent : function(){
18047         if(this.rendered){
18048             var indent = "";
18049             var p = this.node.parentNode;
18050             if(p){
18051                 indent = p.ui.getChildIndent();
18052             }
18053             if(this.indentMarkup != indent){ // don't rerender if not required
18054                 this.indentNode.innerHTML = indent;
18055                 this.indentMarkup = indent;
18056             }
18057             this.updateExpandIcon();
18058         }
18059     }
18060 };
18061
18062 Roo.tree.RootTreeNodeUI = function(){
18063     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18064 };
18065 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18066     render : function(){
18067         if(!this.rendered){
18068             var targetNode = this.node.ownerTree.innerCt.dom;
18069             this.node.expanded = true;
18070             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18071             this.wrap = this.ctNode = targetNode.firstChild;
18072         }
18073     },
18074     collapse : function(){
18075     },
18076     expand : function(){
18077     }
18078 });/*
18079  * Based on:
18080  * Ext JS Library 1.1.1
18081  * Copyright(c) 2006-2007, Ext JS, LLC.
18082  *
18083  * Originally Released Under LGPL - original licence link has changed is not relivant.
18084  *
18085  * Fork - LGPL
18086  * <script type="text/javascript">
18087  */
18088 /**
18089  * @class Roo.tree.TreeLoader
18090  * @extends Roo.util.Observable
18091  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18092  * nodes from a specified URL. The response must be a javascript Array definition
18093  * who's elements are node definition objects. eg:
18094  * <pre><code>
18095    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18096     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18097 </code></pre>
18098  * <br><br>
18099  * A server request is sent, and child nodes are loaded only when a node is expanded.
18100  * The loading node's id is passed to the server under the parameter name "node" to
18101  * enable the server to produce the correct child nodes.
18102  * <br><br>
18103  * To pass extra parameters, an event handler may be attached to the "beforeload"
18104  * event, and the parameters specified in the TreeLoader's baseParams property:
18105  * <pre><code>
18106     myTreeLoader.on("beforeload", function(treeLoader, node) {
18107         this.baseParams.category = node.attributes.category;
18108     }, this);
18109 </code></pre><
18110  * This would pass an HTTP parameter called "category" to the server containing
18111  * the value of the Node's "category" attribute.
18112  * @constructor
18113  * Creates a new Treeloader.
18114  * @param {Object} config A config object containing config properties.
18115  */
18116 Roo.tree.TreeLoader = function(config){
18117     this.baseParams = {};
18118     this.requestMethod = "POST";
18119     Roo.apply(this, config);
18120
18121     this.addEvents({
18122     
18123         /**
18124          * @event beforeload
18125          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18126          * @param {Object} This TreeLoader object.
18127          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18128          * @param {Object} callback The callback function specified in the {@link #load} call.
18129          */
18130         beforeload : true,
18131         /**
18132          * @event load
18133          * Fires when the node has been successfuly loaded.
18134          * @param {Object} This TreeLoader object.
18135          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18136          * @param {Object} response The response object containing the data from the server.
18137          */
18138         load : true,
18139         /**
18140          * @event loadexception
18141          * Fires if the network request failed.
18142          * @param {Object} This TreeLoader object.
18143          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18144          * @param {Object} response The response object containing the data from the server.
18145          */
18146         loadexception : true,
18147         /**
18148          * @event create
18149          * Fires before a node is created, enabling you to return custom Node types 
18150          * @param {Object} This TreeLoader object.
18151          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18152          */
18153         create : true
18154     });
18155
18156     Roo.tree.TreeLoader.superclass.constructor.call(this);
18157 };
18158
18159 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18160     /**
18161     * @cfg {String} dataUrl The URL from which to request a Json string which
18162     * specifies an array of node definition object representing the child nodes
18163     * to be loaded.
18164     */
18165     /**
18166     * @cfg {Object} baseParams (optional) An object containing properties which
18167     * specify HTTP parameters to be passed to each request for child nodes.
18168     */
18169     /**
18170     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18171     * created by this loader. If the attributes sent by the server have an attribute in this object,
18172     * they take priority.
18173     */
18174     /**
18175     * @cfg {Object} uiProviders (optional) An object containing properties which
18176     * 
18177     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18178     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18179     * <i>uiProvider</i> attribute of a returned child node is a string rather
18180     * than a reference to a TreeNodeUI implementation, this that string value
18181     * is used as a property name in the uiProviders object. You can define the provider named
18182     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18183     */
18184     uiProviders : {},
18185
18186     /**
18187     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18188     * child nodes before loading.
18189     */
18190     clearOnLoad : true,
18191
18192     /**
18193     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18194     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18195     * Grid query { data : [ .....] }
18196     */
18197     
18198     root : false,
18199      /**
18200     * @cfg {String} queryParam (optional) 
18201     * Name of the query as it will be passed on the querystring (defaults to 'node')
18202     * eg. the request will be ?node=[id]
18203     */
18204     
18205     
18206     queryParam: false,
18207     
18208     /**
18209      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18210      * This is called automatically when a node is expanded, but may be used to reload
18211      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18212      * @param {Roo.tree.TreeNode} node
18213      * @param {Function} callback
18214      */
18215     load : function(node, callback){
18216         if(this.clearOnLoad){
18217             while(node.firstChild){
18218                 node.removeChild(node.firstChild);
18219             }
18220         }
18221         if(node.attributes.children){ // preloaded json children
18222             var cs = node.attributes.children;
18223             for(var i = 0, len = cs.length; i < len; i++){
18224                 node.appendChild(this.createNode(cs[i]));
18225             }
18226             if(typeof callback == "function"){
18227                 callback();
18228             }
18229         }else if(this.dataUrl){
18230             this.requestData(node, callback);
18231         }
18232     },
18233
18234     getParams: function(node){
18235         var buf = [], bp = this.baseParams;
18236         for(var key in bp){
18237             if(typeof bp[key] != "function"){
18238                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18239             }
18240         }
18241         var n = this.queryParam === false ? 'node' : this.queryParam;
18242         buf.push(n + "=", encodeURIComponent(node.id));
18243         return buf.join("");
18244     },
18245
18246     requestData : function(node, callback){
18247         if(this.fireEvent("beforeload", this, node, callback) !== false){
18248             this.transId = Roo.Ajax.request({
18249                 method:this.requestMethod,
18250                 url: this.dataUrl||this.url,
18251                 success: this.handleResponse,
18252                 failure: this.handleFailure,
18253                 scope: this,
18254                 argument: {callback: callback, node: node},
18255                 params: this.getParams(node)
18256             });
18257         }else{
18258             // if the load is cancelled, make sure we notify
18259             // the node that we are done
18260             if(typeof callback == "function"){
18261                 callback();
18262             }
18263         }
18264     },
18265
18266     isLoading : function(){
18267         return this.transId ? true : false;
18268     },
18269
18270     abort : function(){
18271         if(this.isLoading()){
18272             Roo.Ajax.abort(this.transId);
18273         }
18274     },
18275
18276     // private
18277     createNode : function(attr){
18278         // apply baseAttrs, nice idea Corey!
18279         if(this.baseAttrs){
18280             Roo.applyIf(attr, this.baseAttrs);
18281         }
18282         if(this.applyLoader !== false){
18283             attr.loader = this;
18284         }
18285         // uiProvider = depreciated..
18286         
18287         if(typeof(attr.uiProvider) == 'string'){
18288            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18289                 /**  eval:var:attr */ eval(attr.uiProvider);
18290         }
18291         if(typeof(this.uiProviders['default']) != 'undefined') {
18292             attr.uiProvider = this.uiProviders['default'];
18293         }
18294         
18295         this.fireEvent('create', this, attr);
18296         
18297         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18298         return(attr.leaf ?
18299                         new Roo.tree.TreeNode(attr) :
18300                         new Roo.tree.AsyncTreeNode(attr));
18301     },
18302
18303     processResponse : function(response, node, callback){
18304         var json = response.responseText;
18305         try {
18306             
18307             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18308             if (this.root !== false) {
18309                 o = o[this.root];
18310             }
18311             
18312             for(var i = 0, len = o.length; i < len; i++){
18313                 var n = this.createNode(o[i]);
18314                 if(n){
18315                     node.appendChild(n);
18316                 }
18317             }
18318             if(typeof callback == "function"){
18319                 callback(this, node);
18320             }
18321         }catch(e){
18322             this.handleFailure(response);
18323         }
18324     },
18325
18326     handleResponse : function(response){
18327         this.transId = false;
18328         var a = response.argument;
18329         this.processResponse(response, a.node, a.callback);
18330         this.fireEvent("load", this, a.node, response);
18331     },
18332
18333     handleFailure : function(response){
18334         this.transId = false;
18335         var a = response.argument;
18336         this.fireEvent("loadexception", this, a.node, response);
18337         if(typeof a.callback == "function"){
18338             a.callback(this, a.node);
18339         }
18340     }
18341 });/*
18342  * Based on:
18343  * Ext JS Library 1.1.1
18344  * Copyright(c) 2006-2007, Ext JS, LLC.
18345  *
18346  * Originally Released Under LGPL - original licence link has changed is not relivant.
18347  *
18348  * Fork - LGPL
18349  * <script type="text/javascript">
18350  */
18351
18352 /**
18353 * @class Roo.tree.TreeFilter
18354 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18355 * @param {TreePanel} tree
18356 * @param {Object} config (optional)
18357  */
18358 Roo.tree.TreeFilter = function(tree, config){
18359     this.tree = tree;
18360     this.filtered = {};
18361     Roo.apply(this, config);
18362 };
18363
18364 Roo.tree.TreeFilter.prototype = {
18365     clearBlank:false,
18366     reverse:false,
18367     autoClear:false,
18368     remove:false,
18369
18370      /**
18371      * Filter the data by a specific attribute.
18372      * @param {String/RegExp} value Either string that the attribute value
18373      * should start with or a RegExp to test against the attribute
18374      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18375      * @param {TreeNode} startNode (optional) The node to start the filter at.
18376      */
18377     filter : function(value, attr, startNode){
18378         attr = attr || "text";
18379         var f;
18380         if(typeof value == "string"){
18381             var vlen = value.length;
18382             // auto clear empty filter
18383             if(vlen == 0 && this.clearBlank){
18384                 this.clear();
18385                 return;
18386             }
18387             value = value.toLowerCase();
18388             f = function(n){
18389                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18390             };
18391         }else if(value.exec){ // regex?
18392             f = function(n){
18393                 return value.test(n.attributes[attr]);
18394             };
18395         }else{
18396             throw 'Illegal filter type, must be string or regex';
18397         }
18398         this.filterBy(f, null, startNode);
18399         },
18400
18401     /**
18402      * Filter by a function. The passed function will be called with each
18403      * node in the tree (or from the startNode). If the function returns true, the node is kept
18404      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18405      * @param {Function} fn The filter function
18406      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18407      */
18408     filterBy : function(fn, scope, startNode){
18409         startNode = startNode || this.tree.root;
18410         if(this.autoClear){
18411             this.clear();
18412         }
18413         var af = this.filtered, rv = this.reverse;
18414         var f = function(n){
18415             if(n == startNode){
18416                 return true;
18417             }
18418             if(af[n.id]){
18419                 return false;
18420             }
18421             var m = fn.call(scope || n, n);
18422             if(!m || rv){
18423                 af[n.id] = n;
18424                 n.ui.hide();
18425                 return false;
18426             }
18427             return true;
18428         };
18429         startNode.cascade(f);
18430         if(this.remove){
18431            for(var id in af){
18432                if(typeof id != "function"){
18433                    var n = af[id];
18434                    if(n && n.parentNode){
18435                        n.parentNode.removeChild(n);
18436                    }
18437                }
18438            }
18439         }
18440     },
18441
18442     /**
18443      * Clears the current filter. Note: with the "remove" option
18444      * set a filter cannot be cleared.
18445      */
18446     clear : function(){
18447         var t = this.tree;
18448         var af = this.filtered;
18449         for(var id in af){
18450             if(typeof id != "function"){
18451                 var n = af[id];
18452                 if(n){
18453                     n.ui.show();
18454                 }
18455             }
18456         }
18457         this.filtered = {};
18458     }
18459 };
18460 /*
18461  * Based on:
18462  * Ext JS Library 1.1.1
18463  * Copyright(c) 2006-2007, Ext JS, LLC.
18464  *
18465  * Originally Released Under LGPL - original licence link has changed is not relivant.
18466  *
18467  * Fork - LGPL
18468  * <script type="text/javascript">
18469  */
18470  
18471
18472 /**
18473  * @class Roo.tree.TreeSorter
18474  * Provides sorting of nodes in a TreePanel
18475  * 
18476  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18477  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18478  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18479  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18480  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18481  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18482  * @constructor
18483  * @param {TreePanel} tree
18484  * @param {Object} config
18485  */
18486 Roo.tree.TreeSorter = function(tree, config){
18487     Roo.apply(this, config);
18488     tree.on("beforechildrenrendered", this.doSort, this);
18489     tree.on("append", this.updateSort, this);
18490     tree.on("insert", this.updateSort, this);
18491     
18492     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18493     var p = this.property || "text";
18494     var sortType = this.sortType;
18495     var fs = this.folderSort;
18496     var cs = this.caseSensitive === true;
18497     var leafAttr = this.leafAttr || 'leaf';
18498
18499     this.sortFn = function(n1, n2){
18500         if(fs){
18501             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18502                 return 1;
18503             }
18504             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18505                 return -1;
18506             }
18507         }
18508         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18509         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18510         if(v1 < v2){
18511                         return dsc ? +1 : -1;
18512                 }else if(v1 > v2){
18513                         return dsc ? -1 : +1;
18514         }else{
18515                 return 0;
18516         }
18517     };
18518 };
18519
18520 Roo.tree.TreeSorter.prototype = {
18521     doSort : function(node){
18522         node.sort(this.sortFn);
18523     },
18524     
18525     compareNodes : function(n1, n2){
18526         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18527     },
18528     
18529     updateSort : function(tree, node){
18530         if(node.childrenRendered){
18531             this.doSort.defer(1, this, [node]);
18532         }
18533     }
18534 };/*
18535  * Based on:
18536  * Ext JS Library 1.1.1
18537  * Copyright(c) 2006-2007, Ext JS, LLC.
18538  *
18539  * Originally Released Under LGPL - original licence link has changed is not relivant.
18540  *
18541  * Fork - LGPL
18542  * <script type="text/javascript">
18543  */
18544
18545 if(Roo.dd.DropZone){
18546     
18547 Roo.tree.TreeDropZone = function(tree, config){
18548     this.allowParentInsert = false;
18549     this.allowContainerDrop = false;
18550     this.appendOnly = false;
18551     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18552     this.tree = tree;
18553     this.lastInsertClass = "x-tree-no-status";
18554     this.dragOverData = {};
18555 };
18556
18557 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18558     ddGroup : "TreeDD",
18559     
18560     expandDelay : 1000,
18561     
18562     expandNode : function(node){
18563         if(node.hasChildNodes() && !node.isExpanded()){
18564             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18565         }
18566     },
18567     
18568     queueExpand : function(node){
18569         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18570     },
18571     
18572     cancelExpand : function(){
18573         if(this.expandProcId){
18574             clearTimeout(this.expandProcId);
18575             this.expandProcId = false;
18576         }
18577     },
18578     
18579     isValidDropPoint : function(n, pt, dd, e, data){
18580         if(!n || !data){ return false; }
18581         var targetNode = n.node;
18582         var dropNode = data.node;
18583         // default drop rules
18584         if(!(targetNode && targetNode.isTarget && pt)){
18585             return false;
18586         }
18587         if(pt == "append" && targetNode.allowChildren === false){
18588             return false;
18589         }
18590         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18591             return false;
18592         }
18593         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18594             return false;
18595         }
18596         // reuse the object
18597         var overEvent = this.dragOverData;
18598         overEvent.tree = this.tree;
18599         overEvent.target = targetNode;
18600         overEvent.data = data;
18601         overEvent.point = pt;
18602         overEvent.source = dd;
18603         overEvent.rawEvent = e;
18604         overEvent.dropNode = dropNode;
18605         overEvent.cancel = false;  
18606         var result = this.tree.fireEvent("nodedragover", overEvent);
18607         return overEvent.cancel === false && result !== false;
18608     },
18609     
18610     getDropPoint : function(e, n, dd){
18611         var tn = n.node;
18612         if(tn.isRoot){
18613             return tn.allowChildren !== false ? "append" : false; // always append for root
18614         }
18615         var dragEl = n.ddel;
18616         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18617         var y = Roo.lib.Event.getPageY(e);
18618         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18619         
18620         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18621         var noAppend = tn.allowChildren === false;
18622         if(this.appendOnly || tn.parentNode.allowChildren === false){
18623             return noAppend ? false : "append";
18624         }
18625         var noBelow = false;
18626         if(!this.allowParentInsert){
18627             noBelow = tn.hasChildNodes() && tn.isExpanded();
18628         }
18629         var q = (b - t) / (noAppend ? 2 : 3);
18630         if(y >= t && y < (t + q)){
18631             return "above";
18632         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18633             return "below";
18634         }else{
18635             return "append";
18636         }
18637     },
18638     
18639     onNodeEnter : function(n, dd, e, data){
18640         this.cancelExpand();
18641     },
18642     
18643     onNodeOver : function(n, dd, e, data){
18644         var pt = this.getDropPoint(e, n, dd);
18645         var node = n.node;
18646         
18647         // auto node expand check
18648         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18649             this.queueExpand(node);
18650         }else if(pt != "append"){
18651             this.cancelExpand();
18652         }
18653         
18654         // set the insert point style on the target node
18655         var returnCls = this.dropNotAllowed;
18656         if(this.isValidDropPoint(n, pt, dd, e, data)){
18657            if(pt){
18658                var el = n.ddel;
18659                var cls;
18660                if(pt == "above"){
18661                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18662                    cls = "x-tree-drag-insert-above";
18663                }else if(pt == "below"){
18664                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18665                    cls = "x-tree-drag-insert-below";
18666                }else{
18667                    returnCls = "x-tree-drop-ok-append";
18668                    cls = "x-tree-drag-append";
18669                }
18670                if(this.lastInsertClass != cls){
18671                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18672                    this.lastInsertClass = cls;
18673                }
18674            }
18675        }
18676        return returnCls;
18677     },
18678     
18679     onNodeOut : function(n, dd, e, data){
18680         this.cancelExpand();
18681         this.removeDropIndicators(n);
18682     },
18683     
18684     onNodeDrop : function(n, dd, e, data){
18685         var point = this.getDropPoint(e, n, dd);
18686         var targetNode = n.node;
18687         targetNode.ui.startDrop();
18688         if(!this.isValidDropPoint(n, point, dd, e, data)){
18689             targetNode.ui.endDrop();
18690             return false;
18691         }
18692         // first try to find the drop node
18693         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18694         var dropEvent = {
18695             tree : this.tree,
18696             target: targetNode,
18697             data: data,
18698             point: point,
18699             source: dd,
18700             rawEvent: e,
18701             dropNode: dropNode,
18702             cancel: !dropNode   
18703         };
18704         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18705         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18706             targetNode.ui.endDrop();
18707             return false;
18708         }
18709         // allow target changing
18710         targetNode = dropEvent.target;
18711         if(point == "append" && !targetNode.isExpanded()){
18712             targetNode.expand(false, null, function(){
18713                 this.completeDrop(dropEvent);
18714             }.createDelegate(this));
18715         }else{
18716             this.completeDrop(dropEvent);
18717         }
18718         return true;
18719     },
18720     
18721     completeDrop : function(de){
18722         var ns = de.dropNode, p = de.point, t = de.target;
18723         if(!(ns instanceof Array)){
18724             ns = [ns];
18725         }
18726         var n;
18727         for(var i = 0, len = ns.length; i < len; i++){
18728             n = ns[i];
18729             if(p == "above"){
18730                 t.parentNode.insertBefore(n, t);
18731             }else if(p == "below"){
18732                 t.parentNode.insertBefore(n, t.nextSibling);
18733             }else{
18734                 t.appendChild(n);
18735             }
18736         }
18737         n.ui.focus();
18738         if(this.tree.hlDrop){
18739             n.ui.highlight();
18740         }
18741         t.ui.endDrop();
18742         this.tree.fireEvent("nodedrop", de);
18743     },
18744     
18745     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18746         if(this.tree.hlDrop){
18747             dropNode.ui.focus();
18748             dropNode.ui.highlight();
18749         }
18750         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18751     },
18752     
18753     getTree : function(){
18754         return this.tree;
18755     },
18756     
18757     removeDropIndicators : function(n){
18758         if(n && n.ddel){
18759             var el = n.ddel;
18760             Roo.fly(el).removeClass([
18761                     "x-tree-drag-insert-above",
18762                     "x-tree-drag-insert-below",
18763                     "x-tree-drag-append"]);
18764             this.lastInsertClass = "_noclass";
18765         }
18766     },
18767     
18768     beforeDragDrop : function(target, e, id){
18769         this.cancelExpand();
18770         return true;
18771     },
18772     
18773     afterRepair : function(data){
18774         if(data && Roo.enableFx){
18775             data.node.ui.highlight();
18776         }
18777         this.hideProxy();
18778     }    
18779 });
18780
18781 }
18782 /*
18783  * Based on:
18784  * Ext JS Library 1.1.1
18785  * Copyright(c) 2006-2007, Ext JS, LLC.
18786  *
18787  * Originally Released Under LGPL - original licence link has changed is not relivant.
18788  *
18789  * Fork - LGPL
18790  * <script type="text/javascript">
18791  */
18792  
18793
18794 if(Roo.dd.DragZone){
18795 Roo.tree.TreeDragZone = function(tree, config){
18796     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18797     this.tree = tree;
18798 };
18799
18800 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18801     ddGroup : "TreeDD",
18802     
18803     onBeforeDrag : function(data, e){
18804         var n = data.node;
18805         return n && n.draggable && !n.disabled;
18806     },
18807     
18808     onInitDrag : function(e){
18809         var data = this.dragData;
18810         this.tree.getSelectionModel().select(data.node);
18811         this.proxy.update("");
18812         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18813         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18814     },
18815     
18816     getRepairXY : function(e, data){
18817         return data.node.ui.getDDRepairXY();
18818     },
18819     
18820     onEndDrag : function(data, e){
18821         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18822     },
18823     
18824     onValidDrop : function(dd, e, id){
18825         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18826         this.hideProxy();
18827     },
18828     
18829     beforeInvalidDrop : function(e, id){
18830         // this scrolls the original position back into view
18831         var sm = this.tree.getSelectionModel();
18832         sm.clearSelections();
18833         sm.select(this.dragData.node);
18834     }
18835 });
18836 }/*
18837  * Based on:
18838  * Ext JS Library 1.1.1
18839  * Copyright(c) 2006-2007, Ext JS, LLC.
18840  *
18841  * Originally Released Under LGPL - original licence link has changed is not relivant.
18842  *
18843  * Fork - LGPL
18844  * <script type="text/javascript">
18845  */
18846 /**
18847  * @class Roo.tree.TreeEditor
18848  * @extends Roo.Editor
18849  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18850  * as the editor field.
18851  * @constructor
18852  * @param {TreePanel} tree
18853  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18854  */
18855 Roo.tree.TreeEditor = function(tree, config){
18856     config = config || {};
18857     var field = config.events ? config : new Roo.form.TextField(config);
18858     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18859
18860     this.tree = tree;
18861
18862     tree.on('beforeclick', this.beforeNodeClick, this);
18863     tree.getTreeEl().on('mousedown', this.hide, this);
18864     this.on('complete', this.updateNode, this);
18865     this.on('beforestartedit', this.fitToTree, this);
18866     this.on('startedit', this.bindScroll, this, {delay:10});
18867     this.on('specialkey', this.onSpecialKey, this);
18868 };
18869
18870 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18871     /**
18872      * @cfg {String} alignment
18873      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18874      */
18875     alignment: "l-l",
18876     // inherit
18877     autoSize: false,
18878     /**
18879      * @cfg {Boolean} hideEl
18880      * True to hide the bound element while the editor is displayed (defaults to false)
18881      */
18882     hideEl : false,
18883     /**
18884      * @cfg {String} cls
18885      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18886      */
18887     cls: "x-small-editor x-tree-editor",
18888     /**
18889      * @cfg {Boolean} shim
18890      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18891      */
18892     shim:false,
18893     // inherit
18894     shadow:"frame",
18895     /**
18896      * @cfg {Number} maxWidth
18897      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18898      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18899      * scroll and client offsets into account prior to each edit.
18900      */
18901     maxWidth: 250,
18902
18903     editDelay : 350,
18904
18905     // private
18906     fitToTree : function(ed, el){
18907         var td = this.tree.getTreeEl().dom, nd = el.dom;
18908         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18909             td.scrollLeft = nd.offsetLeft;
18910         }
18911         var w = Math.min(
18912                 this.maxWidth,
18913                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18914         this.setSize(w, '');
18915     },
18916
18917     // private
18918     triggerEdit : function(node){
18919         this.completeEdit();
18920         this.editNode = node;
18921         this.startEdit(node.ui.textNode, node.text);
18922     },
18923
18924     // private
18925     bindScroll : function(){
18926         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18927     },
18928
18929     // private
18930     beforeNodeClick : function(node, e){
18931         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
18932         this.lastClick = new Date();
18933         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
18934             e.stopEvent();
18935             this.triggerEdit(node);
18936             return false;
18937         }
18938     },
18939
18940     // private
18941     updateNode : function(ed, value){
18942         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
18943         this.editNode.setText(value);
18944     },
18945
18946     // private
18947     onHide : function(){
18948         Roo.tree.TreeEditor.superclass.onHide.call(this);
18949         if(this.editNode){
18950             this.editNode.ui.focus();
18951         }
18952     },
18953
18954     // private
18955     onSpecialKey : function(field, e){
18956         var k = e.getKey();
18957         if(k == e.ESC){
18958             e.stopEvent();
18959             this.cancelEdit();
18960         }else if(k == e.ENTER && !e.hasModifier()){
18961             e.stopEvent();
18962             this.completeEdit();
18963         }
18964     }
18965 });//<Script type="text/javascript">
18966 /*
18967  * Based on:
18968  * Ext JS Library 1.1.1
18969  * Copyright(c) 2006-2007, Ext JS, LLC.
18970  *
18971  * Originally Released Under LGPL - original licence link has changed is not relivant.
18972  *
18973  * Fork - LGPL
18974  * <script type="text/javascript">
18975  */
18976  
18977 /**
18978  * Not documented??? - probably should be...
18979  */
18980
18981 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
18982     //focus: Roo.emptyFn, // prevent odd scrolling behavior
18983     
18984     renderElements : function(n, a, targetNode, bulkRender){
18985         //consel.log("renderElements?");
18986         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18987
18988         var t = n.getOwnerTree();
18989         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
18990         
18991         var cols = t.columns;
18992         var bw = t.borderWidth;
18993         var c = cols[0];
18994         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18995          var cb = typeof a.checked == "boolean";
18996         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
18997         var colcls = 'x-t-' + tid + '-c0';
18998         var buf = [
18999             '<li class="x-tree-node">',
19000             
19001                 
19002                 '<div class="x-tree-node-el ', a.cls,'">',
19003                     // extran...
19004                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19005                 
19006                 
19007                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19008                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19009                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19010                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19011                            (a.iconCls ? ' '+a.iconCls : ''),
19012                            '" unselectable="on" />',
19013                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19014                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19015                              
19016                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19017                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19018                             '<span unselectable="on" qtip="' + tx + '">',
19019                              tx,
19020                              '</span></a>' ,
19021                     '</div>',
19022                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19023                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19024                  ];
19025         for(var i = 1, len = cols.length; i < len; i++){
19026             c = cols[i];
19027             colcls = 'x-t-' + tid + '-c' +i;
19028             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19029             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19030                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19031                       "</div>");
19032          }
19033          
19034          buf.push(
19035             '</a>',
19036             '<div class="x-clear"></div></div>',
19037             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19038             "</li>");
19039         
19040         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19041             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19042                                 n.nextSibling.ui.getEl(), buf.join(""));
19043         }else{
19044             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19045         }
19046         var el = this.wrap.firstChild;
19047         this.elRow = el;
19048         this.elNode = el.firstChild;
19049         this.ranchor = el.childNodes[1];
19050         this.ctNode = this.wrap.childNodes[1];
19051         var cs = el.firstChild.childNodes;
19052         this.indentNode = cs[0];
19053         this.ecNode = cs[1];
19054         this.iconNode = cs[2];
19055         var index = 3;
19056         if(cb){
19057             this.checkbox = cs[3];
19058             index++;
19059         }
19060         this.anchor = cs[index];
19061         
19062         this.textNode = cs[index].firstChild;
19063         
19064         //el.on("click", this.onClick, this);
19065         //el.on("dblclick", this.onDblClick, this);
19066         
19067         
19068        // console.log(this);
19069     },
19070     initEvents : function(){
19071         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19072         
19073             
19074         var a = this.ranchor;
19075
19076         var el = Roo.get(a);
19077
19078         if(Roo.isOpera){ // opera render bug ignores the CSS
19079             el.setStyle("text-decoration", "none");
19080         }
19081
19082         el.on("click", this.onClick, this);
19083         el.on("dblclick", this.onDblClick, this);
19084         el.on("contextmenu", this.onContextMenu, this);
19085         
19086     },
19087     
19088     /*onSelectedChange : function(state){
19089         if(state){
19090             this.focus();
19091             this.addClass("x-tree-selected");
19092         }else{
19093             //this.blur();
19094             this.removeClass("x-tree-selected");
19095         }
19096     },*/
19097     addClass : function(cls){
19098         if(this.elRow){
19099             Roo.fly(this.elRow).addClass(cls);
19100         }
19101         
19102     },
19103     
19104     
19105     removeClass : function(cls){
19106         if(this.elRow){
19107             Roo.fly(this.elRow).removeClass(cls);
19108         }
19109     }
19110
19111     
19112     
19113 });//<Script type="text/javascript">
19114
19115 /*
19116  * Based on:
19117  * Ext JS Library 1.1.1
19118  * Copyright(c) 2006-2007, Ext JS, LLC.
19119  *
19120  * Originally Released Under LGPL - original licence link has changed is not relivant.
19121  *
19122  * Fork - LGPL
19123  * <script type="text/javascript">
19124  */
19125  
19126
19127 /**
19128  * @class Roo.tree.ColumnTree
19129  * @extends Roo.data.TreePanel
19130  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19131  * @cfg {int} borderWidth  compined right/left border allowance
19132  * @constructor
19133  * @param {String/HTMLElement/Element} el The container element
19134  * @param {Object} config
19135  */
19136 Roo.tree.ColumnTree =  function(el, config)
19137 {
19138    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19139    this.addEvents({
19140         /**
19141         * @event resize
19142         * Fire this event on a container when it resizes
19143         * @param {int} w Width
19144         * @param {int} h Height
19145         */
19146        "resize" : true
19147     });
19148     this.on('resize', this.onResize, this);
19149 };
19150
19151 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19152     //lines:false,
19153     
19154     
19155     borderWidth: Roo.isBorderBox ? 0 : 2, 
19156     headEls : false,
19157     
19158     render : function(){
19159         // add the header.....
19160        
19161         Roo.tree.ColumnTree.superclass.render.apply(this);
19162         
19163         this.el.addClass('x-column-tree');
19164         
19165         this.headers = this.el.createChild(
19166             {cls:'x-tree-headers'},this.innerCt.dom);
19167    
19168         var cols = this.columns, c;
19169         var totalWidth = 0;
19170         this.headEls = [];
19171         var  len = cols.length;
19172         for(var i = 0; i < len; i++){
19173              c = cols[i];
19174              totalWidth += c.width;
19175             this.headEls.push(this.headers.createChild({
19176                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19177                  cn: {
19178                      cls:'x-tree-hd-text',
19179                      html: c.header
19180                  },
19181                  style:'width:'+(c.width-this.borderWidth)+'px;'
19182              }));
19183         }
19184         this.headers.createChild({cls:'x-clear'});
19185         // prevent floats from wrapping when clipped
19186         this.headers.setWidth(totalWidth);
19187         //this.innerCt.setWidth(totalWidth);
19188         this.innerCt.setStyle({ overflow: 'auto' });
19189         this.onResize(this.width, this.height);
19190              
19191         
19192     },
19193     onResize : function(w,h)
19194     {
19195         this.height = h;
19196         this.width = w;
19197         // resize cols..
19198         this.innerCt.setWidth(this.width);
19199         this.innerCt.setHeight(this.height-20);
19200         
19201         // headers...
19202         var cols = this.columns, c;
19203         var totalWidth = 0;
19204         var expEl = false;
19205         var len = cols.length;
19206         for(var i = 0; i < len; i++){
19207             c = cols[i];
19208             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19209                 // it's the expander..
19210                 expEl  = this.headEls[i];
19211                 continue;
19212             }
19213             totalWidth += c.width;
19214             
19215         }
19216         if (expEl) {
19217             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19218         }
19219         this.headers.setWidth(w-20);
19220
19221         
19222         
19223         
19224     }
19225 });
19226 /*
19227  * Based on:
19228  * Ext JS Library 1.1.1
19229  * Copyright(c) 2006-2007, Ext JS, LLC.
19230  *
19231  * Originally Released Under LGPL - original licence link has changed is not relivant.
19232  *
19233  * Fork - LGPL
19234  * <script type="text/javascript">
19235  */
19236  
19237 /**
19238  * @class Roo.menu.Menu
19239  * @extends Roo.util.Observable
19240  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19241  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19242  * @constructor
19243  * Creates a new Menu
19244  * @param {Object} config Configuration options
19245  */
19246 Roo.menu.Menu = function(config){
19247     Roo.apply(this, config);
19248     this.id = this.id || Roo.id();
19249     this.addEvents({
19250         /**
19251          * @event beforeshow
19252          * Fires before this menu is displayed
19253          * @param {Roo.menu.Menu} this
19254          */
19255         beforeshow : true,
19256         /**
19257          * @event beforehide
19258          * Fires before this menu is hidden
19259          * @param {Roo.menu.Menu} this
19260          */
19261         beforehide : true,
19262         /**
19263          * @event show
19264          * Fires after this menu is displayed
19265          * @param {Roo.menu.Menu} this
19266          */
19267         show : true,
19268         /**
19269          * @event hide
19270          * Fires after this menu is hidden
19271          * @param {Roo.menu.Menu} this
19272          */
19273         hide : true,
19274         /**
19275          * @event click
19276          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19277          * @param {Roo.menu.Menu} this
19278          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19279          * @param {Roo.EventObject} e
19280          */
19281         click : true,
19282         /**
19283          * @event mouseover
19284          * Fires when the mouse is hovering over this menu
19285          * @param {Roo.menu.Menu} this
19286          * @param {Roo.EventObject} e
19287          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19288          */
19289         mouseover : true,
19290         /**
19291          * @event mouseout
19292          * Fires when the mouse exits this menu
19293          * @param {Roo.menu.Menu} this
19294          * @param {Roo.EventObject} e
19295          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19296          */
19297         mouseout : true,
19298         /**
19299          * @event itemclick
19300          * Fires when a menu item contained in this menu is clicked
19301          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19302          * @param {Roo.EventObject} e
19303          */
19304         itemclick: true
19305     });
19306     if (this.registerMenu) {
19307         Roo.menu.MenuMgr.register(this);
19308     }
19309     
19310     var mis = this.items;
19311     this.items = new Roo.util.MixedCollection();
19312     if(mis){
19313         this.add.apply(this, mis);
19314     }
19315 };
19316
19317 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19318     /**
19319      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19320      */
19321     minWidth : 120,
19322     /**
19323      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19324      * for bottom-right shadow (defaults to "sides")
19325      */
19326     shadow : "sides",
19327     /**
19328      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19329      * this menu (defaults to "tl-tr?")
19330      */
19331     subMenuAlign : "tl-tr?",
19332     /**
19333      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19334      * relative to its element of origin (defaults to "tl-bl?")
19335      */
19336     defaultAlign : "tl-bl?",
19337     /**
19338      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19339      */
19340     allowOtherMenus : false,
19341     /**
19342      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19343      */
19344     registerMenu : true,
19345
19346     hidden:true,
19347
19348     // private
19349     render : function(){
19350         if(this.el){
19351             return;
19352         }
19353         var el = this.el = new Roo.Layer({
19354             cls: "x-menu",
19355             shadow:this.shadow,
19356             constrain: false,
19357             parentEl: this.parentEl || document.body,
19358             zindex:15000
19359         });
19360
19361         this.keyNav = new Roo.menu.MenuNav(this);
19362
19363         if(this.plain){
19364             el.addClass("x-menu-plain");
19365         }
19366         if(this.cls){
19367             el.addClass(this.cls);
19368         }
19369         // generic focus element
19370         this.focusEl = el.createChild({
19371             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19372         });
19373         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19374         ul.on("click", this.onClick, this);
19375         ul.on("mouseover", this.onMouseOver, this);
19376         ul.on("mouseout", this.onMouseOut, this);
19377         this.items.each(function(item){
19378             var li = document.createElement("li");
19379             li.className = "x-menu-list-item";
19380             ul.dom.appendChild(li);
19381             item.render(li, this);
19382         }, this);
19383         this.ul = ul;
19384         this.autoWidth();
19385     },
19386
19387     // private
19388     autoWidth : function(){
19389         var el = this.el, ul = this.ul;
19390         if(!el){
19391             return;
19392         }
19393         var w = this.width;
19394         if(w){
19395             el.setWidth(w);
19396         }else if(Roo.isIE){
19397             el.setWidth(this.minWidth);
19398             var t = el.dom.offsetWidth; // force recalc
19399             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19400         }
19401     },
19402
19403     // private
19404     delayAutoWidth : function(){
19405         if(this.rendered){
19406             if(!this.awTask){
19407                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19408             }
19409             this.awTask.delay(20);
19410         }
19411     },
19412
19413     // private
19414     findTargetItem : function(e){
19415         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19416         if(t && t.menuItemId){
19417             return this.items.get(t.menuItemId);
19418         }
19419     },
19420
19421     // private
19422     onClick : function(e){
19423         var t;
19424         if(t = this.findTargetItem(e)){
19425             t.onClick(e);
19426             this.fireEvent("click", this, t, e);
19427         }
19428     },
19429
19430     // private
19431     setActiveItem : function(item, autoExpand){
19432         if(item != this.activeItem){
19433             if(this.activeItem){
19434                 this.activeItem.deactivate();
19435             }
19436             this.activeItem = item;
19437             item.activate(autoExpand);
19438         }else if(autoExpand){
19439             item.expandMenu();
19440         }
19441     },
19442
19443     // private
19444     tryActivate : function(start, step){
19445         var items = this.items;
19446         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19447             var item = items.get(i);
19448             if(!item.disabled && item.canActivate){
19449                 this.setActiveItem(item, false);
19450                 return item;
19451             }
19452         }
19453         return false;
19454     },
19455
19456     // private
19457     onMouseOver : function(e){
19458         var t;
19459         if(t = this.findTargetItem(e)){
19460             if(t.canActivate && !t.disabled){
19461                 this.setActiveItem(t, true);
19462             }
19463         }
19464         this.fireEvent("mouseover", this, e, t);
19465     },
19466
19467     // private
19468     onMouseOut : function(e){
19469         var t;
19470         if(t = this.findTargetItem(e)){
19471             if(t == this.activeItem && t.shouldDeactivate(e)){
19472                 this.activeItem.deactivate();
19473                 delete this.activeItem;
19474             }
19475         }
19476         this.fireEvent("mouseout", this, e, t);
19477     },
19478
19479     /**
19480      * Read-only.  Returns true if the menu is currently displayed, else false.
19481      * @type Boolean
19482      */
19483     isVisible : function(){
19484         return this.el && !this.hidden;
19485     },
19486
19487     /**
19488      * Displays this menu relative to another element
19489      * @param {String/HTMLElement/Roo.Element} element The element to align to
19490      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19491      * the element (defaults to this.defaultAlign)
19492      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19493      */
19494     show : function(el, pos, parentMenu){
19495         this.parentMenu = parentMenu;
19496         if(!this.el){
19497             this.render();
19498         }
19499         this.fireEvent("beforeshow", this);
19500         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19501     },
19502
19503     /**
19504      * Displays this menu at a specific xy position
19505      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19506      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19507      */
19508     showAt : function(xy, parentMenu, /* private: */_e){
19509         this.parentMenu = parentMenu;
19510         if(!this.el){
19511             this.render();
19512         }
19513         if(_e !== false){
19514             this.fireEvent("beforeshow", this);
19515             xy = this.el.adjustForConstraints(xy);
19516         }
19517         this.el.setXY(xy);
19518         this.el.show();
19519         this.hidden = false;
19520         this.focus();
19521         this.fireEvent("show", this);
19522     },
19523
19524     focus : function(){
19525         if(!this.hidden){
19526             this.doFocus.defer(50, this);
19527         }
19528     },
19529
19530     doFocus : function(){
19531         if(!this.hidden){
19532             this.focusEl.focus();
19533         }
19534     },
19535
19536     /**
19537      * Hides this menu and optionally all parent menus
19538      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19539      */
19540     hide : function(deep){
19541         if(this.el && this.isVisible()){
19542             this.fireEvent("beforehide", this);
19543             if(this.activeItem){
19544                 this.activeItem.deactivate();
19545                 this.activeItem = null;
19546             }
19547             this.el.hide();
19548             this.hidden = true;
19549             this.fireEvent("hide", this);
19550         }
19551         if(deep === true && this.parentMenu){
19552             this.parentMenu.hide(true);
19553         }
19554     },
19555
19556     /**
19557      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19558      * Any of the following are valid:
19559      * <ul>
19560      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19561      * <li>An HTMLElement object which will be converted to a menu item</li>
19562      * <li>A menu item config object that will be created as a new menu item</li>
19563      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19564      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19565      * </ul>
19566      * Usage:
19567      * <pre><code>
19568 // Create the menu
19569 var menu = new Roo.menu.Menu();
19570
19571 // Create a menu item to add by reference
19572 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19573
19574 // Add a bunch of items at once using different methods.
19575 // Only the last item added will be returned.
19576 var item = menu.add(
19577     menuItem,                // add existing item by ref
19578     'Dynamic Item',          // new TextItem
19579     '-',                     // new separator
19580     { text: 'Config Item' }  // new item by config
19581 );
19582 </code></pre>
19583      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19584      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19585      */
19586     add : function(){
19587         var a = arguments, l = a.length, item;
19588         for(var i = 0; i < l; i++){
19589             var el = a[i];
19590             if ((typeof(el) == "object") && el.xtype && el.xns) {
19591                 el = Roo.factory(el, Roo.menu);
19592             }
19593             
19594             if(el.render){ // some kind of Item
19595                 item = this.addItem(el);
19596             }else if(typeof el == "string"){ // string
19597                 if(el == "separator" || el == "-"){
19598                     item = this.addSeparator();
19599                 }else{
19600                     item = this.addText(el);
19601                 }
19602             }else if(el.tagName || el.el){ // element
19603                 item = this.addElement(el);
19604             }else if(typeof el == "object"){ // must be menu item config?
19605                 item = this.addMenuItem(el);
19606             }
19607         }
19608         return item;
19609     },
19610
19611     /**
19612      * Returns this menu's underlying {@link Roo.Element} object
19613      * @return {Roo.Element} The element
19614      */
19615     getEl : function(){
19616         if(!this.el){
19617             this.render();
19618         }
19619         return this.el;
19620     },
19621
19622     /**
19623      * Adds a separator bar to the menu
19624      * @return {Roo.menu.Item} The menu item that was added
19625      */
19626     addSeparator : function(){
19627         return this.addItem(new Roo.menu.Separator());
19628     },
19629
19630     /**
19631      * Adds an {@link Roo.Element} object to the menu
19632      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19633      * @return {Roo.menu.Item} The menu item that was added
19634      */
19635     addElement : function(el){
19636         return this.addItem(new Roo.menu.BaseItem(el));
19637     },
19638
19639     /**
19640      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19641      * @param {Roo.menu.Item} item The menu item to add
19642      * @return {Roo.menu.Item} The menu item that was added
19643      */
19644     addItem : function(item){
19645         this.items.add(item);
19646         if(this.ul){
19647             var li = document.createElement("li");
19648             li.className = "x-menu-list-item";
19649             this.ul.dom.appendChild(li);
19650             item.render(li, this);
19651             this.delayAutoWidth();
19652         }
19653         return item;
19654     },
19655
19656     /**
19657      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19658      * @param {Object} config A MenuItem config object
19659      * @return {Roo.menu.Item} The menu item that was added
19660      */
19661     addMenuItem : function(config){
19662         if(!(config instanceof Roo.menu.Item)){
19663             if(typeof config.checked == "boolean"){ // must be check menu item config?
19664                 config = new Roo.menu.CheckItem(config);
19665             }else{
19666                 config = new Roo.menu.Item(config);
19667             }
19668         }
19669         return this.addItem(config);
19670     },
19671
19672     /**
19673      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19674      * @param {String} text The text to display in the menu item
19675      * @return {Roo.menu.Item} The menu item that was added
19676      */
19677     addText : function(text){
19678         return this.addItem(new Roo.menu.TextItem({ text : text }));
19679     },
19680
19681     /**
19682      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19683      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19684      * @param {Roo.menu.Item} item The menu item to add
19685      * @return {Roo.menu.Item} The menu item that was added
19686      */
19687     insert : function(index, item){
19688         this.items.insert(index, item);
19689         if(this.ul){
19690             var li = document.createElement("li");
19691             li.className = "x-menu-list-item";
19692             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19693             item.render(li, this);
19694             this.delayAutoWidth();
19695         }
19696         return item;
19697     },
19698
19699     /**
19700      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19701      * @param {Roo.menu.Item} item The menu item to remove
19702      */
19703     remove : function(item){
19704         this.items.removeKey(item.id);
19705         item.destroy();
19706     },
19707
19708     /**
19709      * Removes and destroys all items in the menu
19710      */
19711     removeAll : function(){
19712         var f;
19713         while(f = this.items.first()){
19714             this.remove(f);
19715         }
19716     }
19717 });
19718
19719 // MenuNav is a private utility class used internally by the Menu
19720 Roo.menu.MenuNav = function(menu){
19721     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19722     this.scope = this.menu = menu;
19723 };
19724
19725 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19726     doRelay : function(e, h){
19727         var k = e.getKey();
19728         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19729             this.menu.tryActivate(0, 1);
19730             return false;
19731         }
19732         return h.call(this.scope || this, e, this.menu);
19733     },
19734
19735     up : function(e, m){
19736         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19737             m.tryActivate(m.items.length-1, -1);
19738         }
19739     },
19740
19741     down : function(e, m){
19742         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19743             m.tryActivate(0, 1);
19744         }
19745     },
19746
19747     right : function(e, m){
19748         if(m.activeItem){
19749             m.activeItem.expandMenu(true);
19750         }
19751     },
19752
19753     left : function(e, m){
19754         m.hide();
19755         if(m.parentMenu && m.parentMenu.activeItem){
19756             m.parentMenu.activeItem.activate();
19757         }
19758     },
19759
19760     enter : function(e, m){
19761         if(m.activeItem){
19762             e.stopPropagation();
19763             m.activeItem.onClick(e);
19764             m.fireEvent("click", this, m.activeItem);
19765             return true;
19766         }
19767     }
19768 });/*
19769  * Based on:
19770  * Ext JS Library 1.1.1
19771  * Copyright(c) 2006-2007, Ext JS, LLC.
19772  *
19773  * Originally Released Under LGPL - original licence link has changed is not relivant.
19774  *
19775  * Fork - LGPL
19776  * <script type="text/javascript">
19777  */
19778  
19779 /**
19780  * @class Roo.menu.MenuMgr
19781  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19782  * @singleton
19783  */
19784 Roo.menu.MenuMgr = function(){
19785    var menus, active, groups = {}, attached = false, lastShow = new Date();
19786
19787    // private - called when first menu is created
19788    function init(){
19789        menus = {};
19790        active = new Roo.util.MixedCollection();
19791        Roo.get(document).addKeyListener(27, function(){
19792            if(active.length > 0){
19793                hideAll();
19794            }
19795        });
19796    }
19797
19798    // private
19799    function hideAll(){
19800        if(active && active.length > 0){
19801            var c = active.clone();
19802            c.each(function(m){
19803                m.hide();
19804            });
19805        }
19806    }
19807
19808    // private
19809    function onHide(m){
19810        active.remove(m);
19811        if(active.length < 1){
19812            Roo.get(document).un("mousedown", onMouseDown);
19813            attached = false;
19814        }
19815    }
19816
19817    // private
19818    function onShow(m){
19819        var last = active.last();
19820        lastShow = new Date();
19821        active.add(m);
19822        if(!attached){
19823            Roo.get(document).on("mousedown", onMouseDown);
19824            attached = true;
19825        }
19826        if(m.parentMenu){
19827           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19828           m.parentMenu.activeChild = m;
19829        }else if(last && last.isVisible()){
19830           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19831        }
19832    }
19833
19834    // private
19835    function onBeforeHide(m){
19836        if(m.activeChild){
19837            m.activeChild.hide();
19838        }
19839        if(m.autoHideTimer){
19840            clearTimeout(m.autoHideTimer);
19841            delete m.autoHideTimer;
19842        }
19843    }
19844
19845    // private
19846    function onBeforeShow(m){
19847        var pm = m.parentMenu;
19848        if(!pm && !m.allowOtherMenus){
19849            hideAll();
19850        }else if(pm && pm.activeChild && active != m){
19851            pm.activeChild.hide();
19852        }
19853    }
19854
19855    // private
19856    function onMouseDown(e){
19857        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19858            hideAll();
19859        }
19860    }
19861
19862    // private
19863    function onBeforeCheck(mi, state){
19864        if(state){
19865            var g = groups[mi.group];
19866            for(var i = 0, l = g.length; i < l; i++){
19867                if(g[i] != mi){
19868                    g[i].setChecked(false);
19869                }
19870            }
19871        }
19872    }
19873
19874    return {
19875
19876        /**
19877         * Hides all menus that are currently visible
19878         */
19879        hideAll : function(){
19880             hideAll();  
19881        },
19882
19883        // private
19884        register : function(menu){
19885            if(!menus){
19886                init();
19887            }
19888            menus[menu.id] = menu;
19889            menu.on("beforehide", onBeforeHide);
19890            menu.on("hide", onHide);
19891            menu.on("beforeshow", onBeforeShow);
19892            menu.on("show", onShow);
19893            var g = menu.group;
19894            if(g && menu.events["checkchange"]){
19895                if(!groups[g]){
19896                    groups[g] = [];
19897                }
19898                groups[g].push(menu);
19899                menu.on("checkchange", onCheck);
19900            }
19901        },
19902
19903         /**
19904          * Returns a {@link Roo.menu.Menu} object
19905          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19906          * be used to generate and return a new Menu instance.
19907          */
19908        get : function(menu){
19909            if(typeof menu == "string"){ // menu id
19910                return menus[menu];
19911            }else if(menu.events){  // menu instance
19912                return menu;
19913            }else if(typeof menu.length == 'number'){ // array of menu items?
19914                return new Roo.menu.Menu({items:menu});
19915            }else{ // otherwise, must be a config
19916                return new Roo.menu.Menu(menu);
19917            }
19918        },
19919
19920        // private
19921        unregister : function(menu){
19922            delete menus[menu.id];
19923            menu.un("beforehide", onBeforeHide);
19924            menu.un("hide", onHide);
19925            menu.un("beforeshow", onBeforeShow);
19926            menu.un("show", onShow);
19927            var g = menu.group;
19928            if(g && menu.events["checkchange"]){
19929                groups[g].remove(menu);
19930                menu.un("checkchange", onCheck);
19931            }
19932        },
19933
19934        // private
19935        registerCheckable : function(menuItem){
19936            var g = menuItem.group;
19937            if(g){
19938                if(!groups[g]){
19939                    groups[g] = [];
19940                }
19941                groups[g].push(menuItem);
19942                menuItem.on("beforecheckchange", onBeforeCheck);
19943            }
19944        },
19945
19946        // private
19947        unregisterCheckable : function(menuItem){
19948            var g = menuItem.group;
19949            if(g){
19950                groups[g].remove(menuItem);
19951                menuItem.un("beforecheckchange", onBeforeCheck);
19952            }
19953        }
19954    };
19955 }();/*
19956  * Based on:
19957  * Ext JS Library 1.1.1
19958  * Copyright(c) 2006-2007, Ext JS, LLC.
19959  *
19960  * Originally Released Under LGPL - original licence link has changed is not relivant.
19961  *
19962  * Fork - LGPL
19963  * <script type="text/javascript">
19964  */
19965  
19966
19967 /**
19968  * @class Roo.menu.BaseItem
19969  * @extends Roo.Component
19970  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
19971  * management and base configuration options shared by all menu components.
19972  * @constructor
19973  * Creates a new BaseItem
19974  * @param {Object} config Configuration options
19975  */
19976 Roo.menu.BaseItem = function(config){
19977     Roo.menu.BaseItem.superclass.constructor.call(this, config);
19978
19979     this.addEvents({
19980         /**
19981          * @event click
19982          * Fires when this item is clicked
19983          * @param {Roo.menu.BaseItem} this
19984          * @param {Roo.EventObject} e
19985          */
19986         click: true,
19987         /**
19988          * @event activate
19989          * Fires when this item is activated
19990          * @param {Roo.menu.BaseItem} this
19991          */
19992         activate : true,
19993         /**
19994          * @event deactivate
19995          * Fires when this item is deactivated
19996          * @param {Roo.menu.BaseItem} this
19997          */
19998         deactivate : true
19999     });
20000
20001     if(this.handler){
20002         this.on("click", this.handler, this.scope, true);
20003     }
20004 };
20005
20006 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20007     /**
20008      * @cfg {Function} handler
20009      * A function that will handle the click event of this menu item (defaults to undefined)
20010      */
20011     /**
20012      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20013      */
20014     canActivate : false,
20015     /**
20016      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20017      */
20018     activeClass : "x-menu-item-active",
20019     /**
20020      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20021      */
20022     hideOnClick : true,
20023     /**
20024      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20025      */
20026     hideDelay : 100,
20027
20028     // private
20029     ctype: "Roo.menu.BaseItem",
20030
20031     // private
20032     actionMode : "container",
20033
20034     // private
20035     render : function(container, parentMenu){
20036         this.parentMenu = parentMenu;
20037         Roo.menu.BaseItem.superclass.render.call(this, container);
20038         this.container.menuItemId = this.id;
20039     },
20040
20041     // private
20042     onRender : function(container, position){
20043         this.el = Roo.get(this.el);
20044         container.dom.appendChild(this.el.dom);
20045     },
20046
20047     // private
20048     onClick : function(e){
20049         if(!this.disabled && this.fireEvent("click", this, e) !== false
20050                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20051             this.handleClick(e);
20052         }else{
20053             e.stopEvent();
20054         }
20055     },
20056
20057     // private
20058     activate : function(){
20059         if(this.disabled){
20060             return false;
20061         }
20062         var li = this.container;
20063         li.addClass(this.activeClass);
20064         this.region = li.getRegion().adjust(2, 2, -2, -2);
20065         this.fireEvent("activate", this);
20066         return true;
20067     },
20068
20069     // private
20070     deactivate : function(){
20071         this.container.removeClass(this.activeClass);
20072         this.fireEvent("deactivate", this);
20073     },
20074
20075     // private
20076     shouldDeactivate : function(e){
20077         return !this.region || !this.region.contains(e.getPoint());
20078     },
20079
20080     // private
20081     handleClick : function(e){
20082         if(this.hideOnClick){
20083             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20084         }
20085     },
20086
20087     // private
20088     expandMenu : function(autoActivate){
20089         // do nothing
20090     },
20091
20092     // private
20093     hideMenu : function(){
20094         // do nothing
20095     }
20096 });/*
20097  * Based on:
20098  * Ext JS Library 1.1.1
20099  * Copyright(c) 2006-2007, Ext JS, LLC.
20100  *
20101  * Originally Released Under LGPL - original licence link has changed is not relivant.
20102  *
20103  * Fork - LGPL
20104  * <script type="text/javascript">
20105  */
20106  
20107 /**
20108  * @class Roo.menu.Adapter
20109  * @extends Roo.menu.BaseItem
20110  * 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.
20111  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20112  * @constructor
20113  * Creates a new Adapter
20114  * @param {Object} config Configuration options
20115  */
20116 Roo.menu.Adapter = function(component, config){
20117     Roo.menu.Adapter.superclass.constructor.call(this, config);
20118     this.component = component;
20119 };
20120 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20121     // private
20122     canActivate : true,
20123
20124     // private
20125     onRender : function(container, position){
20126         this.component.render(container);
20127         this.el = this.component.getEl();
20128     },
20129
20130     // private
20131     activate : function(){
20132         if(this.disabled){
20133             return false;
20134         }
20135         this.component.focus();
20136         this.fireEvent("activate", this);
20137         return true;
20138     },
20139
20140     // private
20141     deactivate : function(){
20142         this.fireEvent("deactivate", this);
20143     },
20144
20145     // private
20146     disable : function(){
20147         this.component.disable();
20148         Roo.menu.Adapter.superclass.disable.call(this);
20149     },
20150
20151     // private
20152     enable : function(){
20153         this.component.enable();
20154         Roo.menu.Adapter.superclass.enable.call(this);
20155     }
20156 });/*
20157  * Based on:
20158  * Ext JS Library 1.1.1
20159  * Copyright(c) 2006-2007, Ext JS, LLC.
20160  *
20161  * Originally Released Under LGPL - original licence link has changed is not relivant.
20162  *
20163  * Fork - LGPL
20164  * <script type="text/javascript">
20165  */
20166
20167 /**
20168  * @class Roo.menu.TextItem
20169  * @extends Roo.menu.BaseItem
20170  * Adds a static text string to a menu, usually used as either a heading or group separator.
20171  * Note: old style constructor with text is still supported.
20172  * 
20173  * @constructor
20174  * Creates a new TextItem
20175  * @param {Object} cfg Configuration
20176  */
20177 Roo.menu.TextItem = function(cfg){
20178     if (typeof(cfg) == 'string') {
20179         this.text = cfg;
20180     } else {
20181         Roo.apply(this,cfg);
20182     }
20183     
20184     Roo.menu.TextItem.superclass.constructor.call(this);
20185 };
20186
20187 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20188     /**
20189      * @cfg {Boolean} text Text to show on item.
20190      */
20191     text : '',
20192     
20193     /**
20194      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20195      */
20196     hideOnClick : false,
20197     /**
20198      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20199      */
20200     itemCls : "x-menu-text",
20201
20202     // private
20203     onRender : function(){
20204         var s = document.createElement("span");
20205         s.className = this.itemCls;
20206         s.innerHTML = this.text;
20207         this.el = s;
20208         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20209     }
20210 });/*
20211  * Based on:
20212  * Ext JS Library 1.1.1
20213  * Copyright(c) 2006-2007, Ext JS, LLC.
20214  *
20215  * Originally Released Under LGPL - original licence link has changed is not relivant.
20216  *
20217  * Fork - LGPL
20218  * <script type="text/javascript">
20219  */
20220
20221 /**
20222  * @class Roo.menu.Separator
20223  * @extends Roo.menu.BaseItem
20224  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20225  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20226  * @constructor
20227  * @param {Object} config Configuration options
20228  */
20229 Roo.menu.Separator = function(config){
20230     Roo.menu.Separator.superclass.constructor.call(this, config);
20231 };
20232
20233 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20234     /**
20235      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20236      */
20237     itemCls : "x-menu-sep",
20238     /**
20239      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20240      */
20241     hideOnClick : false,
20242
20243     // private
20244     onRender : function(li){
20245         var s = document.createElement("span");
20246         s.className = this.itemCls;
20247         s.innerHTML = "&#160;";
20248         this.el = s;
20249         li.addClass("x-menu-sep-li");
20250         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20251     }
20252 });/*
20253  * Based on:
20254  * Ext JS Library 1.1.1
20255  * Copyright(c) 2006-2007, Ext JS, LLC.
20256  *
20257  * Originally Released Under LGPL - original licence link has changed is not relivant.
20258  *
20259  * Fork - LGPL
20260  * <script type="text/javascript">
20261  */
20262 /**
20263  * @class Roo.menu.Item
20264  * @extends Roo.menu.BaseItem
20265  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20266  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20267  * activation and click handling.
20268  * @constructor
20269  * Creates a new Item
20270  * @param {Object} config Configuration options
20271  */
20272 Roo.menu.Item = function(config){
20273     Roo.menu.Item.superclass.constructor.call(this, config);
20274     if(this.menu){
20275         this.menu = Roo.menu.MenuMgr.get(this.menu);
20276     }
20277 };
20278 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20279     
20280     /**
20281      * @cfg {String} text
20282      * The text to show on the menu item.
20283      */
20284     text: '',
20285      /**
20286      * @cfg {String} HTML to render in menu
20287      * The text to show on the menu item (HTML version).
20288      */
20289     html: '',
20290     /**
20291      * @cfg {String} icon
20292      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20293      */
20294     icon: undefined,
20295     /**
20296      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20297      */
20298     itemCls : "x-menu-item",
20299     /**
20300      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20301      */
20302     canActivate : true,
20303     /**
20304      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20305      */
20306     showDelay: 200,
20307     // doc'd in BaseItem
20308     hideDelay: 200,
20309
20310     // private
20311     ctype: "Roo.menu.Item",
20312     
20313     // private
20314     onRender : function(container, position){
20315         var el = document.createElement("a");
20316         el.hideFocus = true;
20317         el.unselectable = "on";
20318         el.href = this.href || "#";
20319         if(this.hrefTarget){
20320             el.target = this.hrefTarget;
20321         }
20322         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20323         
20324         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20325         
20326         el.innerHTML = String.format(
20327                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20328                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20329         this.el = el;
20330         Roo.menu.Item.superclass.onRender.call(this, container, position);
20331     },
20332
20333     /**
20334      * Sets the text to display in this menu item
20335      * @param {String} text The text to display
20336      * @param {Boolean} isHTML true to indicate text is pure html.
20337      */
20338     setText : function(text, isHTML){
20339         if (isHTML) {
20340             this.html = text;
20341         } else {
20342             this.text = text;
20343             this.html = '';
20344         }
20345         if(this.rendered){
20346             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20347      
20348             this.el.update(String.format(
20349                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20350                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20351             this.parentMenu.autoWidth();
20352         }
20353     },
20354
20355     // private
20356     handleClick : function(e){
20357         if(!this.href){ // if no link defined, stop the event automatically
20358             e.stopEvent();
20359         }
20360         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20361     },
20362
20363     // private
20364     activate : function(autoExpand){
20365         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20366             this.focus();
20367             if(autoExpand){
20368                 this.expandMenu();
20369             }
20370         }
20371         return true;
20372     },
20373
20374     // private
20375     shouldDeactivate : function(e){
20376         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20377             if(this.menu && this.menu.isVisible()){
20378                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20379             }
20380             return true;
20381         }
20382         return false;
20383     },
20384
20385     // private
20386     deactivate : function(){
20387         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20388         this.hideMenu();
20389     },
20390
20391     // private
20392     expandMenu : function(autoActivate){
20393         if(!this.disabled && this.menu){
20394             clearTimeout(this.hideTimer);
20395             delete this.hideTimer;
20396             if(!this.menu.isVisible() && !this.showTimer){
20397                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20398             }else if (this.menu.isVisible() && autoActivate){
20399                 this.menu.tryActivate(0, 1);
20400             }
20401         }
20402     },
20403
20404     // private
20405     deferExpand : function(autoActivate){
20406         delete this.showTimer;
20407         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20408         if(autoActivate){
20409             this.menu.tryActivate(0, 1);
20410         }
20411     },
20412
20413     // private
20414     hideMenu : function(){
20415         clearTimeout(this.showTimer);
20416         delete this.showTimer;
20417         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20418             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20419         }
20420     },
20421
20422     // private
20423     deferHide : function(){
20424         delete this.hideTimer;
20425         this.menu.hide();
20426     }
20427 });/*
20428  * Based on:
20429  * Ext JS Library 1.1.1
20430  * Copyright(c) 2006-2007, Ext JS, LLC.
20431  *
20432  * Originally Released Under LGPL - original licence link has changed is not relivant.
20433  *
20434  * Fork - LGPL
20435  * <script type="text/javascript">
20436  */
20437  
20438 /**
20439  * @class Roo.menu.CheckItem
20440  * @extends Roo.menu.Item
20441  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20442  * @constructor
20443  * Creates a new CheckItem
20444  * @param {Object} config Configuration options
20445  */
20446 Roo.menu.CheckItem = function(config){
20447     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20448     this.addEvents({
20449         /**
20450          * @event beforecheckchange
20451          * Fires before the checked value is set, providing an opportunity to cancel if needed
20452          * @param {Roo.menu.CheckItem} this
20453          * @param {Boolean} checked The new checked value that will be set
20454          */
20455         "beforecheckchange" : true,
20456         /**
20457          * @event checkchange
20458          * Fires after the checked value has been set
20459          * @param {Roo.menu.CheckItem} this
20460          * @param {Boolean} checked The checked value that was set
20461          */
20462         "checkchange" : true
20463     });
20464     if(this.checkHandler){
20465         this.on('checkchange', this.checkHandler, this.scope);
20466     }
20467 };
20468 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20469     /**
20470      * @cfg {String} group
20471      * All check items with the same group name will automatically be grouped into a single-select
20472      * radio button group (defaults to '')
20473      */
20474     /**
20475      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20476      */
20477     itemCls : "x-menu-item x-menu-check-item",
20478     /**
20479      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20480      */
20481     groupClass : "x-menu-group-item",
20482
20483     /**
20484      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20485      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20486      * initialized with checked = true will be rendered as checked.
20487      */
20488     checked: false,
20489
20490     // private
20491     ctype: "Roo.menu.CheckItem",
20492
20493     // private
20494     onRender : function(c){
20495         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20496         if(this.group){
20497             this.el.addClass(this.groupClass);
20498         }
20499         Roo.menu.MenuMgr.registerCheckable(this);
20500         if(this.checked){
20501             this.checked = false;
20502             this.setChecked(true, true);
20503         }
20504     },
20505
20506     // private
20507     destroy : function(){
20508         if(this.rendered){
20509             Roo.menu.MenuMgr.unregisterCheckable(this);
20510         }
20511         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20512     },
20513
20514     /**
20515      * Set the checked state of this item
20516      * @param {Boolean} checked The new checked value
20517      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20518      */
20519     setChecked : function(state, suppressEvent){
20520         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20521             if(this.container){
20522                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20523             }
20524             this.checked = state;
20525             if(suppressEvent !== true){
20526                 this.fireEvent("checkchange", this, state);
20527             }
20528         }
20529     },
20530
20531     // private
20532     handleClick : function(e){
20533        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20534            this.setChecked(!this.checked);
20535        }
20536        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20537     }
20538 });/*
20539  * Based on:
20540  * Ext JS Library 1.1.1
20541  * Copyright(c) 2006-2007, Ext JS, LLC.
20542  *
20543  * Originally Released Under LGPL - original licence link has changed is not relivant.
20544  *
20545  * Fork - LGPL
20546  * <script type="text/javascript">
20547  */
20548  
20549 /**
20550  * @class Roo.menu.DateItem
20551  * @extends Roo.menu.Adapter
20552  * A menu item that wraps the {@link Roo.DatPicker} component.
20553  * @constructor
20554  * Creates a new DateItem
20555  * @param {Object} config Configuration options
20556  */
20557 Roo.menu.DateItem = function(config){
20558     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20559     /** The Roo.DatePicker object @type Roo.DatePicker */
20560     this.picker = this.component;
20561     this.addEvents({select: true});
20562     
20563     this.picker.on("render", function(picker){
20564         picker.getEl().swallowEvent("click");
20565         picker.container.addClass("x-menu-date-item");
20566     });
20567
20568     this.picker.on("select", this.onSelect, this);
20569 };
20570
20571 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20572     // private
20573     onSelect : function(picker, date){
20574         this.fireEvent("select", this, date, picker);
20575         Roo.menu.DateItem.superclass.handleClick.call(this);
20576     }
20577 });/*
20578  * Based on:
20579  * Ext JS Library 1.1.1
20580  * Copyright(c) 2006-2007, Ext JS, LLC.
20581  *
20582  * Originally Released Under LGPL - original licence link has changed is not relivant.
20583  *
20584  * Fork - LGPL
20585  * <script type="text/javascript">
20586  */
20587  
20588 /**
20589  * @class Roo.menu.ColorItem
20590  * @extends Roo.menu.Adapter
20591  * A menu item that wraps the {@link Roo.ColorPalette} component.
20592  * @constructor
20593  * Creates a new ColorItem
20594  * @param {Object} config Configuration options
20595  */
20596 Roo.menu.ColorItem = function(config){
20597     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20598     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20599     this.palette = this.component;
20600     this.relayEvents(this.palette, ["select"]);
20601     if(this.selectHandler){
20602         this.on('select', this.selectHandler, this.scope);
20603     }
20604 };
20605 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20606  * Based on:
20607  * Ext JS Library 1.1.1
20608  * Copyright(c) 2006-2007, Ext JS, LLC.
20609  *
20610  * Originally Released Under LGPL - original licence link has changed is not relivant.
20611  *
20612  * Fork - LGPL
20613  * <script type="text/javascript">
20614  */
20615  
20616
20617 /**
20618  * @class Roo.menu.DateMenu
20619  * @extends Roo.menu.Menu
20620  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20621  * @constructor
20622  * Creates a new DateMenu
20623  * @param {Object} config Configuration options
20624  */
20625 Roo.menu.DateMenu = function(config){
20626     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20627     this.plain = true;
20628     var di = new Roo.menu.DateItem(config);
20629     this.add(di);
20630     /**
20631      * The {@link Roo.DatePicker} instance for this DateMenu
20632      * @type DatePicker
20633      */
20634     this.picker = di.picker;
20635     /**
20636      * @event select
20637      * @param {DatePicker} picker
20638      * @param {Date} date
20639      */
20640     this.relayEvents(di, ["select"]);
20641
20642     this.on('beforeshow', function(){
20643         if(this.picker){
20644             this.picker.hideMonthPicker(true);
20645         }
20646     }, this);
20647 };
20648 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20649     cls:'x-date-menu'
20650 });/*
20651  * Based on:
20652  * Ext JS Library 1.1.1
20653  * Copyright(c) 2006-2007, Ext JS, LLC.
20654  *
20655  * Originally Released Under LGPL - original licence link has changed is not relivant.
20656  *
20657  * Fork - LGPL
20658  * <script type="text/javascript">
20659  */
20660  
20661
20662 /**
20663  * @class Roo.menu.ColorMenu
20664  * @extends Roo.menu.Menu
20665  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20666  * @constructor
20667  * Creates a new ColorMenu
20668  * @param {Object} config Configuration options
20669  */
20670 Roo.menu.ColorMenu = function(config){
20671     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20672     this.plain = true;
20673     var ci = new Roo.menu.ColorItem(config);
20674     this.add(ci);
20675     /**
20676      * The {@link Roo.ColorPalette} instance for this ColorMenu
20677      * @type ColorPalette
20678      */
20679     this.palette = ci.palette;
20680     /**
20681      * @event select
20682      * @param {ColorPalette} palette
20683      * @param {String} color
20684      */
20685     this.relayEvents(ci, ["select"]);
20686 };
20687 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20688  * Based on:
20689  * Ext JS Library 1.1.1
20690  * Copyright(c) 2006-2007, Ext JS, LLC.
20691  *
20692  * Originally Released Under LGPL - original licence link has changed is not relivant.
20693  *
20694  * Fork - LGPL
20695  * <script type="text/javascript">
20696  */
20697  
20698 /**
20699  * @class Roo.form.Field
20700  * @extends Roo.BoxComponent
20701  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20702  * @constructor
20703  * Creates a new Field
20704  * @param {Object} config Configuration options
20705  */
20706 Roo.form.Field = function(config){
20707     Roo.form.Field.superclass.constructor.call(this, config);
20708 };
20709
20710 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20711     /**
20712      * @cfg {String} fieldLabel Label to use when rendering a form.
20713      */
20714        /**
20715      * @cfg {String} qtip Mouse over tip
20716      */
20717      
20718     /**
20719      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20720      */
20721     invalidClass : "x-form-invalid",
20722     /**
20723      * @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")
20724      */
20725     invalidText : "The value in this field is invalid",
20726     /**
20727      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20728      */
20729     focusClass : "x-form-focus",
20730     /**
20731      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20732       automatic validation (defaults to "keyup").
20733      */
20734     validationEvent : "keyup",
20735     /**
20736      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20737      */
20738     validateOnBlur : true,
20739     /**
20740      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20741      */
20742     validationDelay : 250,
20743     /**
20744      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20745      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20746      */
20747     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20748     /**
20749      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20750      */
20751     fieldClass : "x-form-field",
20752     /**
20753      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20754      *<pre>
20755 Value         Description
20756 -----------   ----------------------------------------------------------------------
20757 qtip          Display a quick tip when the user hovers over the field
20758 title         Display a default browser title attribute popup
20759 under         Add a block div beneath the field containing the error text
20760 side          Add an error icon to the right of the field with a popup on hover
20761 [element id]  Add the error text directly to the innerHTML of the specified element
20762 </pre>
20763      */
20764     msgTarget : 'qtip',
20765     /**
20766      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20767      */
20768     msgFx : 'normal',
20769
20770     /**
20771      * @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.
20772      */
20773     readOnly : false,
20774
20775     /**
20776      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20777      */
20778     disabled : false,
20779
20780     /**
20781      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20782      */
20783     inputType : undefined,
20784     
20785     /**
20786      * @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).
20787          */
20788         tabIndex : undefined,
20789         
20790     // private
20791     isFormField : true,
20792
20793     // private
20794     hasFocus : false,
20795     /**
20796      * @property {Roo.Element} fieldEl
20797      * Element Containing the rendered Field (with label etc.)
20798      */
20799     /**
20800      * @cfg {Mixed} value A value to initialize this field with.
20801      */
20802     value : undefined,
20803
20804     /**
20805      * @cfg {String} name The field's HTML name attribute.
20806      */
20807     /**
20808      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20809      */
20810
20811         // private ??
20812         initComponent : function(){
20813         Roo.form.Field.superclass.initComponent.call(this);
20814         this.addEvents({
20815             /**
20816              * @event focus
20817              * Fires when this field receives input focus.
20818              * @param {Roo.form.Field} this
20819              */
20820             focus : true,
20821             /**
20822              * @event blur
20823              * Fires when this field loses input focus.
20824              * @param {Roo.form.Field} this
20825              */
20826             blur : true,
20827             /**
20828              * @event specialkey
20829              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20830              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20831              * @param {Roo.form.Field} this
20832              * @param {Roo.EventObject} e The event object
20833              */
20834             specialkey : true,
20835             /**
20836              * @event change
20837              * Fires just before the field blurs if the field value has changed.
20838              * @param {Roo.form.Field} this
20839              * @param {Mixed} newValue The new value
20840              * @param {Mixed} oldValue The original value
20841              */
20842             change : true,
20843             /**
20844              * @event invalid
20845              * Fires after the field has been marked as invalid.
20846              * @param {Roo.form.Field} this
20847              * @param {String} msg The validation message
20848              */
20849             invalid : true,
20850             /**
20851              * @event valid
20852              * Fires after the field has been validated with no errors.
20853              * @param {Roo.form.Field} this
20854              */
20855             valid : true,
20856              /**
20857              * @event keyup
20858              * Fires after the key up
20859              * @param {Roo.form.Field} this
20860              * @param {Roo.EventObject}  e The event Object
20861              */
20862             keyup : true
20863         });
20864     },
20865
20866     /**
20867      * Returns the name attribute of the field if available
20868      * @return {String} name The field name
20869      */
20870     getName: function(){
20871          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20872     },
20873
20874     // private
20875     onRender : function(ct, position){
20876         Roo.form.Field.superclass.onRender.call(this, ct, position);
20877         if(!this.el){
20878             var cfg = this.getAutoCreate();
20879             if(!cfg.name){
20880                 cfg.name = this.name || this.id;
20881             }
20882             if(this.inputType){
20883                 cfg.type = this.inputType;
20884             }
20885             this.el = ct.createChild(cfg, position);
20886         }
20887         var type = this.el.dom.type;
20888         if(type){
20889             if(type == 'password'){
20890                 type = 'text';
20891             }
20892             this.el.addClass('x-form-'+type);
20893         }
20894         if(this.readOnly){
20895             this.el.dom.readOnly = true;
20896         }
20897         if(this.tabIndex !== undefined){
20898             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20899         }
20900
20901         this.el.addClass([this.fieldClass, this.cls]);
20902         this.initValue();
20903     },
20904
20905     /**
20906      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20907      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20908      * @return {Roo.form.Field} this
20909      */
20910     applyTo : function(target){
20911         this.allowDomMove = false;
20912         this.el = Roo.get(target);
20913         this.render(this.el.dom.parentNode);
20914         return this;
20915     },
20916
20917     // private
20918     initValue : function(){
20919         if(this.value !== undefined){
20920             this.setValue(this.value);
20921         }else if(this.el.dom.value.length > 0){
20922             this.setValue(this.el.dom.value);
20923         }
20924     },
20925
20926     /**
20927      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20928      */
20929     isDirty : function() {
20930         if(this.disabled) {
20931             return false;
20932         }
20933         return String(this.getValue()) !== String(this.originalValue);
20934     },
20935
20936     // private
20937     afterRender : function(){
20938         Roo.form.Field.superclass.afterRender.call(this);
20939         this.initEvents();
20940     },
20941
20942     // private
20943     fireKey : function(e){
20944         //Roo.log('field ' + e.getKey());
20945         if(e.isNavKeyPress()){
20946             this.fireEvent("specialkey", this, e);
20947         }
20948     },
20949
20950     /**
20951      * Resets the current field value to the originally loaded value and clears any validation messages
20952      */
20953     reset : function(){
20954         this.setValue(this.originalValue);
20955         this.clearInvalid();
20956     },
20957
20958     // private
20959     initEvents : function(){
20960         // safari killled keypress - so keydown is now used..
20961         this.el.on("keydown" , this.fireKey,  this);
20962         this.el.on("focus", this.onFocus,  this);
20963         this.el.on("blur", this.onBlur,  this);
20964         this.el.relayEvent('keyup', this);
20965
20966         // reference to original value for reset
20967         this.originalValue = this.getValue();
20968     },
20969
20970     // private
20971     onFocus : function(){
20972         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20973             this.el.addClass(this.focusClass);
20974         }
20975         if(!this.hasFocus){
20976             this.hasFocus = true;
20977             this.startValue = this.getValue();
20978             this.fireEvent("focus", this);
20979         }
20980     },
20981
20982     beforeBlur : Roo.emptyFn,
20983
20984     // private
20985     onBlur : function(){
20986         this.beforeBlur();
20987         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
20988             this.el.removeClass(this.focusClass);
20989         }
20990         this.hasFocus = false;
20991         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
20992             this.validate();
20993         }
20994         var v = this.getValue();
20995         if(String(v) !== String(this.startValue)){
20996             this.fireEvent('change', this, v, this.startValue);
20997         }
20998         this.fireEvent("blur", this);
20999     },
21000
21001     /**
21002      * Returns whether or not the field value is currently valid
21003      * @param {Boolean} preventMark True to disable marking the field invalid
21004      * @return {Boolean} True if the value is valid, else false
21005      */
21006     isValid : function(preventMark){
21007         if(this.disabled){
21008             return true;
21009         }
21010         var restore = this.preventMark;
21011         this.preventMark = preventMark === true;
21012         var v = this.validateValue(this.processValue(this.getRawValue()));
21013         this.preventMark = restore;
21014         return v;
21015     },
21016
21017     /**
21018      * Validates the field value
21019      * @return {Boolean} True if the value is valid, else false
21020      */
21021     validate : function(){
21022         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21023             this.clearInvalid();
21024             return true;
21025         }
21026         return false;
21027     },
21028
21029     processValue : function(value){
21030         return value;
21031     },
21032
21033     // private
21034     // Subclasses should provide the validation implementation by overriding this
21035     validateValue : function(value){
21036         return true;
21037     },
21038
21039     /**
21040      * Mark this field as invalid
21041      * @param {String} msg The validation message
21042      */
21043     markInvalid : function(msg){
21044         if(!this.rendered || this.preventMark){ // not rendered
21045             return;
21046         }
21047         this.el.addClass(this.invalidClass);
21048         msg = msg || this.invalidText;
21049         switch(this.msgTarget){
21050             case 'qtip':
21051                 this.el.dom.qtip = msg;
21052                 this.el.dom.qclass = 'x-form-invalid-tip';
21053                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21054                     Roo.QuickTips.enable();
21055                 }
21056                 break;
21057             case 'title':
21058                 this.el.dom.title = msg;
21059                 break;
21060             case 'under':
21061                 if(!this.errorEl){
21062                     var elp = this.el.findParent('.x-form-element', 5, true);
21063                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21064                     this.errorEl.setWidth(elp.getWidth(true)-20);
21065                 }
21066                 this.errorEl.update(msg);
21067                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21068                 break;
21069             case 'side':
21070                 if(!this.errorIcon){
21071                     var elp = this.el.findParent('.x-form-element', 5, true);
21072                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21073                 }
21074                 this.alignErrorIcon();
21075                 this.errorIcon.dom.qtip = msg;
21076                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21077                 this.errorIcon.show();
21078                 this.on('resize', this.alignErrorIcon, this);
21079                 break;
21080             default:
21081                 var t = Roo.getDom(this.msgTarget);
21082                 t.innerHTML = msg;
21083                 t.style.display = this.msgDisplay;
21084                 break;
21085         }
21086         this.fireEvent('invalid', this, msg);
21087     },
21088
21089     // private
21090     alignErrorIcon : function(){
21091         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21092     },
21093
21094     /**
21095      * Clear any invalid styles/messages for this field
21096      */
21097     clearInvalid : function(){
21098         if(!this.rendered || this.preventMark){ // not rendered
21099             return;
21100         }
21101         this.el.removeClass(this.invalidClass);
21102         switch(this.msgTarget){
21103             case 'qtip':
21104                 this.el.dom.qtip = '';
21105                 break;
21106             case 'title':
21107                 this.el.dom.title = '';
21108                 break;
21109             case 'under':
21110                 if(this.errorEl){
21111                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21112                 }
21113                 break;
21114             case 'side':
21115                 if(this.errorIcon){
21116                     this.errorIcon.dom.qtip = '';
21117                     this.errorIcon.hide();
21118                     this.un('resize', this.alignErrorIcon, this);
21119                 }
21120                 break;
21121             default:
21122                 var t = Roo.getDom(this.msgTarget);
21123                 t.innerHTML = '';
21124                 t.style.display = 'none';
21125                 break;
21126         }
21127         this.fireEvent('valid', this);
21128     },
21129
21130     /**
21131      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21132      * @return {Mixed} value The field value
21133      */
21134     getRawValue : function(){
21135         var v = this.el.getValue();
21136         if(v === this.emptyText){
21137             v = '';
21138         }
21139         return v;
21140     },
21141
21142     /**
21143      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21144      * @return {Mixed} value The field value
21145      */
21146     getValue : function(){
21147         var v = this.el.getValue();
21148         if(v === this.emptyText || v === undefined){
21149             v = '';
21150         }
21151         return v;
21152     },
21153
21154     /**
21155      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21156      * @param {Mixed} value The value to set
21157      */
21158     setRawValue : function(v){
21159         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21160     },
21161
21162     /**
21163      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21164      * @param {Mixed} value The value to set
21165      */
21166     setValue : function(v){
21167         this.value = v;
21168         if(this.rendered){
21169             this.el.dom.value = (v === null || v === undefined ? '' : v);
21170             this.validate();
21171         }
21172     },
21173
21174     adjustSize : function(w, h){
21175         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21176         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21177         return s;
21178     },
21179
21180     adjustWidth : function(tag, w){
21181         tag = tag.toLowerCase();
21182         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21183             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21184                 if(tag == 'input'){
21185                     return w + 2;
21186                 }
21187                 if(tag = 'textarea'){
21188                     return w-2;
21189                 }
21190             }else if(Roo.isOpera){
21191                 if(tag == 'input'){
21192                     return w + 2;
21193                 }
21194                 if(tag = 'textarea'){
21195                     return w-2;
21196                 }
21197             }
21198         }
21199         return w;
21200     }
21201 });
21202
21203
21204 // anything other than normal should be considered experimental
21205 Roo.form.Field.msgFx = {
21206     normal : {
21207         show: function(msgEl, f){
21208             msgEl.setDisplayed('block');
21209         },
21210
21211         hide : function(msgEl, f){
21212             msgEl.setDisplayed(false).update('');
21213         }
21214     },
21215
21216     slide : {
21217         show: function(msgEl, f){
21218             msgEl.slideIn('t', {stopFx:true});
21219         },
21220
21221         hide : function(msgEl, f){
21222             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21223         }
21224     },
21225
21226     slideRight : {
21227         show: function(msgEl, f){
21228             msgEl.fixDisplay();
21229             msgEl.alignTo(f.el, 'tl-tr');
21230             msgEl.slideIn('l', {stopFx:true});
21231         },
21232
21233         hide : function(msgEl, f){
21234             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21235         }
21236     }
21237 };/*
21238  * Based on:
21239  * Ext JS Library 1.1.1
21240  * Copyright(c) 2006-2007, Ext JS, LLC.
21241  *
21242  * Originally Released Under LGPL - original licence link has changed is not relivant.
21243  *
21244  * Fork - LGPL
21245  * <script type="text/javascript">
21246  */
21247  
21248
21249 /**
21250  * @class Roo.form.TextField
21251  * @extends Roo.form.Field
21252  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21253  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21254  * @constructor
21255  * Creates a new TextField
21256  * @param {Object} config Configuration options
21257  */
21258 Roo.form.TextField = function(config){
21259     Roo.form.TextField.superclass.constructor.call(this, config);
21260     this.addEvents({
21261         /**
21262          * @event autosize
21263          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21264          * according to the default logic, but this event provides a hook for the developer to apply additional
21265          * logic at runtime to resize the field if needed.
21266              * @param {Roo.form.Field} this This text field
21267              * @param {Number} width The new field width
21268              */
21269         autosize : true
21270     });
21271 };
21272
21273 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21274     /**
21275      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21276      */
21277     grow : false,
21278     /**
21279      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21280      */
21281     growMin : 30,
21282     /**
21283      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21284      */
21285     growMax : 800,
21286     /**
21287      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21288      */
21289     vtype : null,
21290     /**
21291      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21292      */
21293     maskRe : null,
21294     /**
21295      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21296      */
21297     disableKeyFilter : false,
21298     /**
21299      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21300      */
21301     allowBlank : true,
21302     /**
21303      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21304      */
21305     minLength : 0,
21306     /**
21307      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21308      */
21309     maxLength : Number.MAX_VALUE,
21310     /**
21311      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21312      */
21313     minLengthText : "The minimum length for this field is {0}",
21314     /**
21315      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21316      */
21317     maxLengthText : "The maximum length for this field is {0}",
21318     /**
21319      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21320      */
21321     selectOnFocus : false,
21322     /**
21323      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21324      */
21325     blankText : "This field is required",
21326     /**
21327      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21328      * If available, this function will be called only after the basic validators all return true, and will be passed the
21329      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21330      */
21331     validator : null,
21332     /**
21333      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21334      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21335      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21336      */
21337     regex : null,
21338     /**
21339      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21340      */
21341     regexText : "",
21342     /**
21343      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21344      */
21345     emptyText : null,
21346     /**
21347      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21348      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21349      */
21350     emptyClass : 'x-form-empty-field',
21351
21352     // private
21353     initEvents : function(){
21354         Roo.form.TextField.superclass.initEvents.call(this);
21355         if(this.validationEvent == 'keyup'){
21356             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21357             this.el.on('keyup', this.filterValidation, this);
21358         }
21359         else if(this.validationEvent !== false){
21360             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21361         }
21362         if(this.selectOnFocus || this.emptyText){
21363             this.on("focus", this.preFocus, this);
21364             if(this.emptyText){
21365                 this.on('blur', this.postBlur, this);
21366                 this.applyEmptyText();
21367             }
21368         }
21369         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21370             this.el.on("keypress", this.filterKeys, this);
21371         }
21372         if(this.grow){
21373             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21374             this.el.on("click", this.autoSize,  this);
21375         }
21376     },
21377
21378     processValue : function(value){
21379         if(this.stripCharsRe){
21380             var newValue = value.replace(this.stripCharsRe, '');
21381             if(newValue !== value){
21382                 this.setRawValue(newValue);
21383                 return newValue;
21384             }
21385         }
21386         return value;
21387     },
21388
21389     filterValidation : function(e){
21390         if(!e.isNavKeyPress()){
21391             this.validationTask.delay(this.validationDelay);
21392         }
21393     },
21394
21395     // private
21396     onKeyUp : function(e){
21397         if(!e.isNavKeyPress()){
21398             this.autoSize();
21399         }
21400     },
21401
21402     /**
21403      * Resets the current field value to the originally-loaded value and clears any validation messages.
21404      * Also adds emptyText and emptyClass if the original value was blank.
21405      */
21406     reset : function(){
21407         Roo.form.TextField.superclass.reset.call(this);
21408         this.applyEmptyText();
21409     },
21410
21411     applyEmptyText : function(){
21412         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21413             this.setRawValue(this.emptyText);
21414             this.el.addClass(this.emptyClass);
21415         }
21416     },
21417
21418     // private
21419     preFocus : function(){
21420         if(this.emptyText){
21421             if(this.el.dom.value == this.emptyText){
21422                 this.setRawValue('');
21423             }
21424             this.el.removeClass(this.emptyClass);
21425         }
21426         if(this.selectOnFocus){
21427             this.el.dom.select();
21428         }
21429     },
21430
21431     // private
21432     postBlur : function(){
21433         this.applyEmptyText();
21434     },
21435
21436     // private
21437     filterKeys : function(e){
21438         var k = e.getKey();
21439         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21440             return;
21441         }
21442         var c = e.getCharCode(), cc = String.fromCharCode(c);
21443         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21444             return;
21445         }
21446         if(!this.maskRe.test(cc)){
21447             e.stopEvent();
21448         }
21449     },
21450
21451     setValue : function(v){
21452         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21453             this.el.removeClass(this.emptyClass);
21454         }
21455         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21456         this.applyEmptyText();
21457         this.autoSize();
21458     },
21459
21460     /**
21461      * Validates a value according to the field's validation rules and marks the field as invalid
21462      * if the validation fails
21463      * @param {Mixed} value The value to validate
21464      * @return {Boolean} True if the value is valid, else false
21465      */
21466     validateValue : function(value){
21467         if(value.length < 1 || value === this.emptyText){ // if it's blank
21468              if(this.allowBlank){
21469                 this.clearInvalid();
21470                 return true;
21471              }else{
21472                 this.markInvalid(this.blankText);
21473                 return false;
21474              }
21475         }
21476         if(value.length < this.minLength){
21477             this.markInvalid(String.format(this.minLengthText, this.minLength));
21478             return false;
21479         }
21480         if(value.length > this.maxLength){
21481             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21482             return false;
21483         }
21484         if(this.vtype){
21485             var vt = Roo.form.VTypes;
21486             if(!vt[this.vtype](value, this)){
21487                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21488                 return false;
21489             }
21490         }
21491         if(typeof this.validator == "function"){
21492             var msg = this.validator(value);
21493             if(msg !== true){
21494                 this.markInvalid(msg);
21495                 return false;
21496             }
21497         }
21498         if(this.regex && !this.regex.test(value)){
21499             this.markInvalid(this.regexText);
21500             return false;
21501         }
21502         return true;
21503     },
21504
21505     /**
21506      * Selects text in this field
21507      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21508      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21509      */
21510     selectText : function(start, end){
21511         var v = this.getRawValue();
21512         if(v.length > 0){
21513             start = start === undefined ? 0 : start;
21514             end = end === undefined ? v.length : end;
21515             var d = this.el.dom;
21516             if(d.setSelectionRange){
21517                 d.setSelectionRange(start, end);
21518             }else if(d.createTextRange){
21519                 var range = d.createTextRange();
21520                 range.moveStart("character", start);
21521                 range.moveEnd("character", v.length-end);
21522                 range.select();
21523             }
21524         }
21525     },
21526
21527     /**
21528      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21529      * This only takes effect if grow = true, and fires the autosize event.
21530      */
21531     autoSize : function(){
21532         if(!this.grow || !this.rendered){
21533             return;
21534         }
21535         if(!this.metrics){
21536             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21537         }
21538         var el = this.el;
21539         var v = el.dom.value;
21540         var d = document.createElement('div');
21541         d.appendChild(document.createTextNode(v));
21542         v = d.innerHTML;
21543         d = null;
21544         v += "&#160;";
21545         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21546         this.el.setWidth(w);
21547         this.fireEvent("autosize", this, w);
21548     }
21549 });/*
21550  * Based on:
21551  * Ext JS Library 1.1.1
21552  * Copyright(c) 2006-2007, Ext JS, LLC.
21553  *
21554  * Originally Released Under LGPL - original licence link has changed is not relivant.
21555  *
21556  * Fork - LGPL
21557  * <script type="text/javascript">
21558  */
21559  
21560 /**
21561  * @class Roo.form.Hidden
21562  * @extends Roo.form.TextField
21563  * Simple Hidden element used on forms 
21564  * 
21565  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21566  * 
21567  * @constructor
21568  * Creates a new Hidden form element.
21569  * @param {Object} config Configuration options
21570  */
21571
21572
21573
21574 // easy hidden field...
21575 Roo.form.Hidden = function(config){
21576     Roo.form.Hidden.superclass.constructor.call(this, config);
21577 };
21578   
21579 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21580     fieldLabel:      '',
21581     inputType:      'hidden',
21582     width:          50,
21583     allowBlank:     true,
21584     labelSeparator: '',
21585     hidden:         true,
21586     itemCls :       'x-form-item-display-none'
21587
21588
21589 });
21590
21591
21592 /*
21593  * Based on:
21594  * Ext JS Library 1.1.1
21595  * Copyright(c) 2006-2007, Ext JS, LLC.
21596  *
21597  * Originally Released Under LGPL - original licence link has changed is not relivant.
21598  *
21599  * Fork - LGPL
21600  * <script type="text/javascript">
21601  */
21602  
21603 /**
21604  * @class Roo.form.TriggerField
21605  * @extends Roo.form.TextField
21606  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21607  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21608  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21609  * for which you can provide a custom implementation.  For example:
21610  * <pre><code>
21611 var trigger = new Roo.form.TriggerField();
21612 trigger.onTriggerClick = myTriggerFn;
21613 trigger.applyTo('my-field');
21614 </code></pre>
21615  *
21616  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21617  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21618  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21619  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21620  * @constructor
21621  * Create a new TriggerField.
21622  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21623  * to the base TextField)
21624  */
21625 Roo.form.TriggerField = function(config){
21626     this.mimicing = false;
21627     Roo.form.TriggerField.superclass.constructor.call(this, config);
21628 };
21629
21630 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21631     /**
21632      * @cfg {String} triggerClass A CSS class to apply to the trigger
21633      */
21634     /**
21635      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21636      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21637      */
21638     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21639     /**
21640      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21641      */
21642     hideTrigger:false,
21643
21644     /** @cfg {Boolean} grow @hide */
21645     /** @cfg {Number} growMin @hide */
21646     /** @cfg {Number} growMax @hide */
21647
21648     /**
21649      * @hide 
21650      * @method
21651      */
21652     autoSize: Roo.emptyFn,
21653     // private
21654     monitorTab : true,
21655     // private
21656     deferHeight : true,
21657
21658     
21659     actionMode : 'wrap',
21660     // private
21661     onResize : function(w, h){
21662         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21663         if(typeof w == 'number'){
21664             var x = w - this.trigger.getWidth();
21665             this.el.setWidth(this.adjustWidth('input', x));
21666             this.trigger.setStyle('left', x+'px');
21667         }
21668     },
21669
21670     // private
21671     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21672
21673     // private
21674     getResizeEl : function(){
21675         return this.wrap;
21676     },
21677
21678     // private
21679     getPositionEl : function(){
21680         return this.wrap;
21681     },
21682
21683     // private
21684     alignErrorIcon : function(){
21685         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21686     },
21687
21688     // private
21689     onRender : function(ct, position){
21690         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21691         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21692         this.trigger = this.wrap.createChild(this.triggerConfig ||
21693                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21694         if(this.hideTrigger){
21695             this.trigger.setDisplayed(false);
21696         }
21697         this.initTrigger();
21698         if(!this.width){
21699             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21700         }
21701     },
21702
21703     // private
21704     initTrigger : function(){
21705         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21706         this.trigger.addClassOnOver('x-form-trigger-over');
21707         this.trigger.addClassOnClick('x-form-trigger-click');
21708     },
21709
21710     // private
21711     onDestroy : function(){
21712         if(this.trigger){
21713             this.trigger.removeAllListeners();
21714             this.trigger.remove();
21715         }
21716         if(this.wrap){
21717             this.wrap.remove();
21718         }
21719         Roo.form.TriggerField.superclass.onDestroy.call(this);
21720     },
21721
21722     // private
21723     onFocus : function(){
21724         Roo.form.TriggerField.superclass.onFocus.call(this);
21725         if(!this.mimicing){
21726             this.wrap.addClass('x-trigger-wrap-focus');
21727             this.mimicing = true;
21728             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21729             if(this.monitorTab){
21730                 this.el.on("keydown", this.checkTab, this);
21731             }
21732         }
21733     },
21734
21735     // private
21736     checkTab : function(e){
21737         if(e.getKey() == e.TAB){
21738             this.triggerBlur();
21739         }
21740     },
21741
21742     // private
21743     onBlur : function(){
21744         // do nothing
21745     },
21746
21747     // private
21748     mimicBlur : function(e, t){
21749         if(!this.wrap.contains(t) && this.validateBlur()){
21750             this.triggerBlur();
21751         }
21752     },
21753
21754     // private
21755     triggerBlur : function(){
21756         this.mimicing = false;
21757         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21758         if(this.monitorTab){
21759             this.el.un("keydown", this.checkTab, this);
21760         }
21761         this.wrap.removeClass('x-trigger-wrap-focus');
21762         Roo.form.TriggerField.superclass.onBlur.call(this);
21763     },
21764
21765     // private
21766     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21767     validateBlur : function(e, t){
21768         return true;
21769     },
21770
21771     // private
21772     onDisable : function(){
21773         Roo.form.TriggerField.superclass.onDisable.call(this);
21774         if(this.wrap){
21775             this.wrap.addClass('x-item-disabled');
21776         }
21777     },
21778
21779     // private
21780     onEnable : function(){
21781         Roo.form.TriggerField.superclass.onEnable.call(this);
21782         if(this.wrap){
21783             this.wrap.removeClass('x-item-disabled');
21784         }
21785     },
21786
21787     // private
21788     onShow : function(){
21789         var ae = this.getActionEl();
21790         
21791         if(ae){
21792             ae.dom.style.display = '';
21793             ae.dom.style.visibility = 'visible';
21794         }
21795     },
21796
21797     // private
21798     
21799     onHide : function(){
21800         var ae = this.getActionEl();
21801         ae.dom.style.display = 'none';
21802     },
21803
21804     /**
21805      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21806      * by an implementing function.
21807      * @method
21808      * @param {EventObject} e
21809      */
21810     onTriggerClick : Roo.emptyFn
21811 });
21812
21813 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21814 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21815 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21816 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21817     initComponent : function(){
21818         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21819
21820         this.triggerConfig = {
21821             tag:'span', cls:'x-form-twin-triggers', cn:[
21822             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21823             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21824         ]};
21825     },
21826
21827     getTrigger : function(index){
21828         return this.triggers[index];
21829     },
21830
21831     initTrigger : function(){
21832         var ts = this.trigger.select('.x-form-trigger', true);
21833         this.wrap.setStyle('overflow', 'hidden');
21834         var triggerField = this;
21835         ts.each(function(t, all, index){
21836             t.hide = function(){
21837                 var w = triggerField.wrap.getWidth();
21838                 this.dom.style.display = 'none';
21839                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21840             };
21841             t.show = function(){
21842                 var w = triggerField.wrap.getWidth();
21843                 this.dom.style.display = '';
21844                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21845             };
21846             var triggerIndex = 'Trigger'+(index+1);
21847
21848             if(this['hide'+triggerIndex]){
21849                 t.dom.style.display = 'none';
21850             }
21851             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21852             t.addClassOnOver('x-form-trigger-over');
21853             t.addClassOnClick('x-form-trigger-click');
21854         }, this);
21855         this.triggers = ts.elements;
21856     },
21857
21858     onTrigger1Click : Roo.emptyFn,
21859     onTrigger2Click : Roo.emptyFn
21860 });/*
21861  * Based on:
21862  * Ext JS Library 1.1.1
21863  * Copyright(c) 2006-2007, Ext JS, LLC.
21864  *
21865  * Originally Released Under LGPL - original licence link has changed is not relivant.
21866  *
21867  * Fork - LGPL
21868  * <script type="text/javascript">
21869  */
21870  
21871 /**
21872  * @class Roo.form.TextArea
21873  * @extends Roo.form.TextField
21874  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21875  * support for auto-sizing.
21876  * @constructor
21877  * Creates a new TextArea
21878  * @param {Object} config Configuration options
21879  */
21880 Roo.form.TextArea = function(config){
21881     Roo.form.TextArea.superclass.constructor.call(this, config);
21882     // these are provided exchanges for backwards compat
21883     // minHeight/maxHeight were replaced by growMin/growMax to be
21884     // compatible with TextField growing config values
21885     if(this.minHeight !== undefined){
21886         this.growMin = this.minHeight;
21887     }
21888     if(this.maxHeight !== undefined){
21889         this.growMax = this.maxHeight;
21890     }
21891 };
21892
21893 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21894     /**
21895      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21896      */
21897     growMin : 60,
21898     /**
21899      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21900      */
21901     growMax: 1000,
21902     /**
21903      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21904      * in the field (equivalent to setting overflow: hidden, defaults to false)
21905      */
21906     preventScrollbars: false,
21907     /**
21908      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21909      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21910      */
21911
21912     // private
21913     onRender : function(ct, position){
21914         if(!this.el){
21915             this.defaultAutoCreate = {
21916                 tag: "textarea",
21917                 style:"width:300px;height:60px;",
21918                 autocomplete: "off"
21919             };
21920         }
21921         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21922         if(this.grow){
21923             this.textSizeEl = Roo.DomHelper.append(document.body, {
21924                 tag: "pre", cls: "x-form-grow-sizer"
21925             });
21926             if(this.preventScrollbars){
21927                 this.el.setStyle("overflow", "hidden");
21928             }
21929             this.el.setHeight(this.growMin);
21930         }
21931     },
21932
21933     onDestroy : function(){
21934         if(this.textSizeEl){
21935             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
21936         }
21937         Roo.form.TextArea.superclass.onDestroy.call(this);
21938     },
21939
21940     // private
21941     onKeyUp : function(e){
21942         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
21943             this.autoSize();
21944         }
21945     },
21946
21947     /**
21948      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
21949      * This only takes effect if grow = true, and fires the autosize event if the height changes.
21950      */
21951     autoSize : function(){
21952         if(!this.grow || !this.textSizeEl){
21953             return;
21954         }
21955         var el = this.el;
21956         var v = el.dom.value;
21957         var ts = this.textSizeEl;
21958
21959         ts.innerHTML = '';
21960         ts.appendChild(document.createTextNode(v));
21961         v = ts.innerHTML;
21962
21963         Roo.fly(ts).setWidth(this.el.getWidth());
21964         if(v.length < 1){
21965             v = "&#160;&#160;";
21966         }else{
21967             if(Roo.isIE){
21968                 v = v.replace(/\n/g, '<p>&#160;</p>');
21969             }
21970             v += "&#160;\n&#160;";
21971         }
21972         ts.innerHTML = v;
21973         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
21974         if(h != this.lastHeight){
21975             this.lastHeight = h;
21976             this.el.setHeight(h);
21977             this.fireEvent("autosize", this, h);
21978         }
21979     }
21980 });/*
21981  * Based on:
21982  * Ext JS Library 1.1.1
21983  * Copyright(c) 2006-2007, Ext JS, LLC.
21984  *
21985  * Originally Released Under LGPL - original licence link has changed is not relivant.
21986  *
21987  * Fork - LGPL
21988  * <script type="text/javascript">
21989  */
21990  
21991
21992 /**
21993  * @class Roo.form.NumberField
21994  * @extends Roo.form.TextField
21995  * Numeric text field that provides automatic keystroke filtering and numeric validation.
21996  * @constructor
21997  * Creates a new NumberField
21998  * @param {Object} config Configuration options
21999  */
22000 Roo.form.NumberField = function(config){
22001     Roo.form.NumberField.superclass.constructor.call(this, config);
22002 };
22003
22004 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22005     /**
22006      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22007      */
22008     fieldClass: "x-form-field x-form-num-field",
22009     /**
22010      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22011      */
22012     allowDecimals : true,
22013     /**
22014      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22015      */
22016     decimalSeparator : ".",
22017     /**
22018      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22019      */
22020     decimalPrecision : 2,
22021     /**
22022      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22023      */
22024     allowNegative : true,
22025     /**
22026      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22027      */
22028     minValue : Number.NEGATIVE_INFINITY,
22029     /**
22030      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22031      */
22032     maxValue : Number.MAX_VALUE,
22033     /**
22034      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22035      */
22036     minText : "The minimum value for this field is {0}",
22037     /**
22038      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22039      */
22040     maxText : "The maximum value for this field is {0}",
22041     /**
22042      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22043      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22044      */
22045     nanText : "{0} is not a valid number",
22046
22047     // private
22048     initEvents : function(){
22049         Roo.form.NumberField.superclass.initEvents.call(this);
22050         var allowed = "0123456789";
22051         if(this.allowDecimals){
22052             allowed += this.decimalSeparator;
22053         }
22054         if(this.allowNegative){
22055             allowed += "-";
22056         }
22057         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22058         var keyPress = function(e){
22059             var k = e.getKey();
22060             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22061                 return;
22062             }
22063             var c = e.getCharCode();
22064             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22065                 e.stopEvent();
22066             }
22067         };
22068         this.el.on("keypress", keyPress, this);
22069     },
22070
22071     // private
22072     validateValue : function(value){
22073         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22074             return false;
22075         }
22076         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22077              return true;
22078         }
22079         var num = this.parseValue(value);
22080         if(isNaN(num)){
22081             this.markInvalid(String.format(this.nanText, value));
22082             return false;
22083         }
22084         if(num < this.minValue){
22085             this.markInvalid(String.format(this.minText, this.minValue));
22086             return false;
22087         }
22088         if(num > this.maxValue){
22089             this.markInvalid(String.format(this.maxText, this.maxValue));
22090             return false;
22091         }
22092         return true;
22093     },
22094
22095     getValue : function(){
22096         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22097     },
22098
22099     // private
22100     parseValue : function(value){
22101         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22102         return isNaN(value) ? '' : value;
22103     },
22104
22105     // private
22106     fixPrecision : function(value){
22107         var nan = isNaN(value);
22108         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22109             return nan ? '' : value;
22110         }
22111         return parseFloat(value).toFixed(this.decimalPrecision);
22112     },
22113
22114     setValue : function(v){
22115         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22116     },
22117
22118     // private
22119     decimalPrecisionFcn : function(v){
22120         return Math.floor(v);
22121     },
22122
22123     beforeBlur : function(){
22124         var v = this.parseValue(this.getRawValue());
22125         if(v){
22126             this.setValue(this.fixPrecision(v));
22127         }
22128     }
22129 });/*
22130  * Based on:
22131  * Ext JS Library 1.1.1
22132  * Copyright(c) 2006-2007, Ext JS, LLC.
22133  *
22134  * Originally Released Under LGPL - original licence link has changed is not relivant.
22135  *
22136  * Fork - LGPL
22137  * <script type="text/javascript">
22138  */
22139  
22140 /**
22141  * @class Roo.form.DateField
22142  * @extends Roo.form.TriggerField
22143  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22144 * @constructor
22145 * Create a new DateField
22146 * @param {Object} config
22147  */
22148 Roo.form.DateField = function(config){
22149     Roo.form.DateField.superclass.constructor.call(this, config);
22150     
22151       this.addEvents({
22152          
22153         /**
22154          * @event select
22155          * Fires when a date is selected
22156              * @param {Roo.form.DateField} combo This combo box
22157              * @param {Date} date The date selected
22158              */
22159         'select' : true
22160          
22161     });
22162     
22163     
22164     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22165     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22166     this.ddMatch = null;
22167     if(this.disabledDates){
22168         var dd = this.disabledDates;
22169         var re = "(?:";
22170         for(var i = 0; i < dd.length; i++){
22171             re += dd[i];
22172             if(i != dd.length-1) re += "|";
22173         }
22174         this.ddMatch = new RegExp(re + ")");
22175     }
22176 };
22177
22178 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22179     /**
22180      * @cfg {String} format
22181      * The default date format string which can be overriden for localization support.  The format must be
22182      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22183      */
22184     format : "m/d/y",
22185     /**
22186      * @cfg {String} altFormats
22187      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22188      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22189      */
22190     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22191     /**
22192      * @cfg {Array} disabledDays
22193      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22194      */
22195     disabledDays : null,
22196     /**
22197      * @cfg {String} disabledDaysText
22198      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22199      */
22200     disabledDaysText : "Disabled",
22201     /**
22202      * @cfg {Array} disabledDates
22203      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22204      * expression so they are very powerful. Some examples:
22205      * <ul>
22206      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22207      * <li>["03/08", "09/16"] would disable those days for every year</li>
22208      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22209      * <li>["03/../2006"] would disable every day in March 2006</li>
22210      * <li>["^03"] would disable every day in every March</li>
22211      * </ul>
22212      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22213      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22214      */
22215     disabledDates : null,
22216     /**
22217      * @cfg {String} disabledDatesText
22218      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22219      */
22220     disabledDatesText : "Disabled",
22221     /**
22222      * @cfg {Date/String} minValue
22223      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22224      * valid format (defaults to null).
22225      */
22226     minValue : null,
22227     /**
22228      * @cfg {Date/String} maxValue
22229      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22230      * valid format (defaults to null).
22231      */
22232     maxValue : null,
22233     /**
22234      * @cfg {String} minText
22235      * The error text to display when the date in the cell is before minValue (defaults to
22236      * 'The date in this field must be after {minValue}').
22237      */
22238     minText : "The date in this field must be equal to or after {0}",
22239     /**
22240      * @cfg {String} maxText
22241      * The error text to display when the date in the cell is after maxValue (defaults to
22242      * 'The date in this field must be before {maxValue}').
22243      */
22244     maxText : "The date in this field must be equal to or before {0}",
22245     /**
22246      * @cfg {String} invalidText
22247      * The error text to display when the date in the field is invalid (defaults to
22248      * '{value} is not a valid date - it must be in the format {format}').
22249      */
22250     invalidText : "{0} is not a valid date - it must be in the format {1}",
22251     /**
22252      * @cfg {String} triggerClass
22253      * An additional CSS class used to style the trigger button.  The trigger will always get the
22254      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22255      * which displays a calendar icon).
22256      */
22257     triggerClass : 'x-form-date-trigger',
22258     
22259
22260     /**
22261      * @cfg {bool} useIso
22262      * if enabled, then the date field will use a hidden field to store the 
22263      * real value as iso formated date. default (false)
22264      */ 
22265     useIso : false,
22266     /**
22267      * @cfg {String/Object} autoCreate
22268      * A DomHelper element spec, or true for a default element spec (defaults to
22269      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22270      */ 
22271     // private
22272     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22273     
22274     // private
22275     hiddenField: false,
22276     
22277     onRender : function(ct, position)
22278     {
22279         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22280         if (this.useIso) {
22281             this.el.dom.removeAttribute('name'); 
22282             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22283                     'before', true);
22284             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22285             // prevent input submission
22286             this.hiddenName = this.name;
22287         }
22288             
22289             
22290     },
22291     
22292     // private
22293     validateValue : function(value)
22294     {
22295         value = this.formatDate(value);
22296         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22297             return false;
22298         }
22299         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22300              return true;
22301         }
22302         var svalue = value;
22303         value = this.parseDate(value);
22304         if(!value){
22305             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22306             return false;
22307         }
22308         var time = value.getTime();
22309         if(this.minValue && time < this.minValue.getTime()){
22310             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22311             return false;
22312         }
22313         if(this.maxValue && time > this.maxValue.getTime()){
22314             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22315             return false;
22316         }
22317         if(this.disabledDays){
22318             var day = value.getDay();
22319             for(var i = 0; i < this.disabledDays.length; i++) {
22320                 if(day === this.disabledDays[i]){
22321                     this.markInvalid(this.disabledDaysText);
22322                     return false;
22323                 }
22324             }
22325         }
22326         var fvalue = this.formatDate(value);
22327         if(this.ddMatch && this.ddMatch.test(fvalue)){
22328             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22329             return false;
22330         }
22331         return true;
22332     },
22333
22334     // private
22335     // Provides logic to override the default TriggerField.validateBlur which just returns true
22336     validateBlur : function(){
22337         return !this.menu || !this.menu.isVisible();
22338     },
22339
22340     /**
22341      * Returns the current date value of the date field.
22342      * @return {Date} The date value
22343      */
22344     getValue : function(){
22345         
22346         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22347     },
22348
22349     /**
22350      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22351      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22352      * (the default format used is "m/d/y").
22353      * <br />Usage:
22354      * <pre><code>
22355 //All of these calls set the same date value (May 4, 2006)
22356
22357 //Pass a date object:
22358 var dt = new Date('5/4/06');
22359 dateField.setValue(dt);
22360
22361 //Pass a date string (default format):
22362 dateField.setValue('5/4/06');
22363
22364 //Pass a date string (custom format):
22365 dateField.format = 'Y-m-d';
22366 dateField.setValue('2006-5-4');
22367 </code></pre>
22368      * @param {String/Date} date The date or valid date string
22369      */
22370     setValue : function(date){
22371         if (this.hiddenField) {
22372             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22373         }
22374         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22375     },
22376
22377     // private
22378     parseDate : function(value){
22379         if(!value || value instanceof Date){
22380             return value;
22381         }
22382         var v = Date.parseDate(value, this.format);
22383         if(!v && this.altFormats){
22384             if(!this.altFormatsArray){
22385                 this.altFormatsArray = this.altFormats.split("|");
22386             }
22387             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22388                 v = Date.parseDate(value, this.altFormatsArray[i]);
22389             }
22390         }
22391         return v;
22392     },
22393
22394     // private
22395     formatDate : function(date, fmt){
22396         return (!date || !(date instanceof Date)) ?
22397                date : date.dateFormat(fmt || this.format);
22398     },
22399
22400     // private
22401     menuListeners : {
22402         select: function(m, d){
22403             this.setValue(d);
22404             this.fireEvent('select', this, d);
22405         },
22406         show : function(){ // retain focus styling
22407             this.onFocus();
22408         },
22409         hide : function(){
22410             this.focus.defer(10, this);
22411             var ml = this.menuListeners;
22412             this.menu.un("select", ml.select,  this);
22413             this.menu.un("show", ml.show,  this);
22414             this.menu.un("hide", ml.hide,  this);
22415         }
22416     },
22417
22418     // private
22419     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22420     onTriggerClick : function(){
22421         if(this.disabled){
22422             return;
22423         }
22424         if(this.menu == null){
22425             this.menu = new Roo.menu.DateMenu();
22426         }
22427         Roo.apply(this.menu.picker,  {
22428             showClear: this.allowBlank,
22429             minDate : this.minValue,
22430             maxDate : this.maxValue,
22431             disabledDatesRE : this.ddMatch,
22432             disabledDatesText : this.disabledDatesText,
22433             disabledDays : this.disabledDays,
22434             disabledDaysText : this.disabledDaysText,
22435             format : this.format,
22436             minText : String.format(this.minText, this.formatDate(this.minValue)),
22437             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22438         });
22439         this.menu.on(Roo.apply({}, this.menuListeners, {
22440             scope:this
22441         }));
22442         this.menu.picker.setValue(this.getValue() || new Date());
22443         this.menu.show(this.el, "tl-bl?");
22444     },
22445
22446     beforeBlur : function(){
22447         var v = this.parseDate(this.getRawValue());
22448         if(v){
22449             this.setValue(v);
22450         }
22451     }
22452
22453     /** @cfg {Boolean} grow @hide */
22454     /** @cfg {Number} growMin @hide */
22455     /** @cfg {Number} growMax @hide */
22456     /**
22457      * @hide
22458      * @method autoSize
22459      */
22460 });/*
22461  * Based on:
22462  * Ext JS Library 1.1.1
22463  * Copyright(c) 2006-2007, Ext JS, LLC.
22464  *
22465  * Originally Released Under LGPL - original licence link has changed is not relivant.
22466  *
22467  * Fork - LGPL
22468  * <script type="text/javascript">
22469  */
22470  
22471
22472 /**
22473  * @class Roo.form.ComboBox
22474  * @extends Roo.form.TriggerField
22475  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22476  * @constructor
22477  * Create a new ComboBox.
22478  * @param {Object} config Configuration options
22479  */
22480 Roo.form.ComboBox = function(config){
22481     Roo.form.ComboBox.superclass.constructor.call(this, config);
22482     this.addEvents({
22483         /**
22484          * @event expand
22485          * Fires when the dropdown list is expanded
22486              * @param {Roo.form.ComboBox} combo This combo box
22487              */
22488         'expand' : true,
22489         /**
22490          * @event collapse
22491          * Fires when the dropdown list is collapsed
22492              * @param {Roo.form.ComboBox} combo This combo box
22493              */
22494         'collapse' : true,
22495         /**
22496          * @event beforeselect
22497          * Fires before a list item is selected. Return false to cancel the selection.
22498              * @param {Roo.form.ComboBox} combo This combo box
22499              * @param {Roo.data.Record} record The data record returned from the underlying store
22500              * @param {Number} index The index of the selected item in the dropdown list
22501              */
22502         'beforeselect' : true,
22503         /**
22504          * @event select
22505          * Fires when a list item is selected
22506              * @param {Roo.form.ComboBox} combo This combo box
22507              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22508              * @param {Number} index The index of the selected item in the dropdown list
22509              */
22510         'select' : true,
22511         /**
22512          * @event beforequery
22513          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22514          * The event object passed has these properties:
22515              * @param {Roo.form.ComboBox} combo This combo box
22516              * @param {String} query The query
22517              * @param {Boolean} forceAll true to force "all" query
22518              * @param {Boolean} cancel true to cancel the query
22519              * @param {Object} e The query event object
22520              */
22521         'beforequery': true,
22522          /**
22523          * @event add
22524          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22525              * @param {Roo.form.ComboBox} combo This combo box
22526              */
22527         'add' : true,
22528         /**
22529          * @event edit
22530          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22531              * @param {Roo.form.ComboBox} combo This combo box
22532              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22533              */
22534         'edit' : true
22535         
22536         
22537     });
22538     if(this.transform){
22539         this.allowDomMove = false;
22540         var s = Roo.getDom(this.transform);
22541         if(!this.hiddenName){
22542             this.hiddenName = s.name;
22543         }
22544         if(!this.store){
22545             this.mode = 'local';
22546             var d = [], opts = s.options;
22547             for(var i = 0, len = opts.length;i < len; i++){
22548                 var o = opts[i];
22549                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22550                 if(o.selected) {
22551                     this.value = value;
22552                 }
22553                 d.push([value, o.text]);
22554             }
22555             this.store = new Roo.data.SimpleStore({
22556                 'id': 0,
22557                 fields: ['value', 'text'],
22558                 data : d
22559             });
22560             this.valueField = 'value';
22561             this.displayField = 'text';
22562         }
22563         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22564         if(!this.lazyRender){
22565             this.target = true;
22566             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22567             s.parentNode.removeChild(s); // remove it
22568             this.render(this.el.parentNode);
22569         }else{
22570             s.parentNode.removeChild(s); // remove it
22571         }
22572
22573     }
22574     if (this.store) {
22575         this.store = Roo.factory(this.store, Roo.data);
22576     }
22577     
22578     this.selectedIndex = -1;
22579     if(this.mode == 'local'){
22580         if(config.queryDelay === undefined){
22581             this.queryDelay = 10;
22582         }
22583         if(config.minChars === undefined){
22584             this.minChars = 0;
22585         }
22586     }
22587 };
22588
22589 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22590     /**
22591      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22592      */
22593     /**
22594      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22595      * rendering into an Roo.Editor, defaults to false)
22596      */
22597     /**
22598      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22599      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22600      */
22601     /**
22602      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22603      */
22604     /**
22605      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22606      * the dropdown list (defaults to undefined, with no header element)
22607      */
22608
22609      /**
22610      * @cfg {String/Roo.Template} tpl The template to use to render the output
22611      */
22612      
22613     // private
22614     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22615     /**
22616      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22617      */
22618     listWidth: undefined,
22619     /**
22620      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22621      * mode = 'remote' or 'text' if mode = 'local')
22622      */
22623     displayField: undefined,
22624     /**
22625      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22626      * mode = 'remote' or 'value' if mode = 'local'). 
22627      * Note: use of a valueField requires the user make a selection
22628      * in order for a value to be mapped.
22629      */
22630     valueField: undefined,
22631     /**
22632      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22633      * field's data value (defaults to the underlying DOM element's name)
22634      */
22635     hiddenName: undefined,
22636     /**
22637      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22638      */
22639     listClass: '',
22640     /**
22641      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22642      */
22643     selectedClass: 'x-combo-selected',
22644     /**
22645      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22646      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22647      * which displays a downward arrow icon).
22648      */
22649     triggerClass : 'x-form-arrow-trigger',
22650     /**
22651      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22652      */
22653     shadow:'sides',
22654     /**
22655      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22656      * anchor positions (defaults to 'tl-bl')
22657      */
22658     listAlign: 'tl-bl?',
22659     /**
22660      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22661      */
22662     maxHeight: 300,
22663     /**
22664      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22665      * query specified by the allQuery config option (defaults to 'query')
22666      */
22667     triggerAction: 'query',
22668     /**
22669      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22670      * (defaults to 4, does not apply if editable = false)
22671      */
22672     minChars : 4,
22673     /**
22674      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22675      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22676      */
22677     typeAhead: false,
22678     /**
22679      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22680      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22681      */
22682     queryDelay: 500,
22683     /**
22684      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22685      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22686      */
22687     pageSize: 0,
22688     /**
22689      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22690      * when editable = true (defaults to false)
22691      */
22692     selectOnFocus:false,
22693     /**
22694      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22695      */
22696     queryParam: 'query',
22697     /**
22698      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22699      * when mode = 'remote' (defaults to 'Loading...')
22700      */
22701     loadingText: 'Loading...',
22702     /**
22703      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22704      */
22705     resizable: false,
22706     /**
22707      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22708      */
22709     handleHeight : 8,
22710     /**
22711      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22712      * traditional select (defaults to true)
22713      */
22714     editable: true,
22715     /**
22716      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22717      */
22718     allQuery: '',
22719     /**
22720      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22721      */
22722     mode: 'remote',
22723     /**
22724      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22725      * listWidth has a higher value)
22726      */
22727     minListWidth : 70,
22728     /**
22729      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22730      * allow the user to set arbitrary text into the field (defaults to false)
22731      */
22732     forceSelection:false,
22733     /**
22734      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22735      * if typeAhead = true (defaults to 250)
22736      */
22737     typeAheadDelay : 250,
22738     /**
22739      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22740      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22741      */
22742     valueNotFoundText : undefined,
22743     /**
22744      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22745      */
22746     blockFocus : false,
22747     
22748     /**
22749      * @cfg {Boolean} disableClear Disable showing of clear button.
22750      */
22751     disableClear : false,
22752     /**
22753      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22754      */
22755     alwaysQuery : false,
22756     
22757     //private
22758     addicon : false,
22759     editicon: false,
22760     
22761     
22762     // private
22763     onRender : function(ct, position){
22764         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22765         if(this.hiddenName){
22766             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22767                     'before', true);
22768             this.hiddenField.value =
22769                 this.hiddenValue !== undefined ? this.hiddenValue :
22770                 this.value !== undefined ? this.value : '';
22771
22772             // prevent input submission
22773             this.el.dom.removeAttribute('name');
22774         }
22775         if(Roo.isGecko){
22776             this.el.dom.setAttribute('autocomplete', 'off');
22777         }
22778
22779         var cls = 'x-combo-list';
22780
22781         this.list = new Roo.Layer({
22782             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22783         });
22784
22785         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22786         this.list.setWidth(lw);
22787         this.list.swallowEvent('mousewheel');
22788         this.assetHeight = 0;
22789
22790         if(this.title){
22791             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22792             this.assetHeight += this.header.getHeight();
22793         }
22794
22795         this.innerList = this.list.createChild({cls:cls+'-inner'});
22796         this.innerList.on('mouseover', this.onViewOver, this);
22797         this.innerList.on('mousemove', this.onViewMove, this);
22798         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22799         
22800         if(this.allowBlank && !this.pageSize && !this.disableClear){
22801             this.footer = this.list.createChild({cls:cls+'-ft'});
22802             this.pageTb = new Roo.Toolbar(this.footer);
22803            
22804         }
22805         if(this.pageSize){
22806             this.footer = this.list.createChild({cls:cls+'-ft'});
22807             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22808                     {pageSize: this.pageSize});
22809             
22810         }
22811         
22812         if (this.pageTb && this.allowBlank && !this.disableClear) {
22813             var _this = this;
22814             this.pageTb.add(new Roo.Toolbar.Fill(), {
22815                 cls: 'x-btn-icon x-btn-clear',
22816                 text: '&#160;',
22817                 handler: function()
22818                 {
22819                     _this.collapse();
22820                     _this.clearValue();
22821                     _this.onSelect(false, -1);
22822                 }
22823             });
22824         }
22825         if (this.footer) {
22826             this.assetHeight += this.footer.getHeight();
22827         }
22828         
22829
22830         if(!this.tpl){
22831             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22832         }
22833
22834         this.view = new Roo.View(this.innerList, this.tpl, {
22835             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22836         });
22837
22838         this.view.on('click', this.onViewClick, this);
22839
22840         this.store.on('beforeload', this.onBeforeLoad, this);
22841         this.store.on('load', this.onLoad, this);
22842         this.store.on('loadexception', this.collapse, this);
22843
22844         if(this.resizable){
22845             this.resizer = new Roo.Resizable(this.list,  {
22846                pinned:true, handles:'se'
22847             });
22848             this.resizer.on('resize', function(r, w, h){
22849                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22850                 this.listWidth = w;
22851                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22852                 this.restrictHeight();
22853             }, this);
22854             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22855         }
22856         if(!this.editable){
22857             this.editable = true;
22858             this.setEditable(false);
22859         }  
22860         
22861         
22862         if (typeof(this.events.add.listeners) != 'undefined') {
22863             
22864             this.addicon = this.wrap.createChild(
22865                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22866        
22867             this.addicon.on('click', function(e) {
22868                 this.fireEvent('add', this);
22869             }, this);
22870         }
22871         if (typeof(this.events.edit.listeners) != 'undefined') {
22872             
22873             this.editicon = this.wrap.createChild(
22874                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22875             if (this.addicon) {
22876                 this.editicon.setStyle('margin-left', '40px');
22877             }
22878             this.editicon.on('click', function(e) {
22879                 
22880                 // we fire even  if inothing is selected..
22881                 this.fireEvent('edit', this, this.lastData );
22882                 
22883             }, this);
22884         }
22885         
22886         
22887         
22888     },
22889
22890     // private
22891     initEvents : function(){
22892         Roo.form.ComboBox.superclass.initEvents.call(this);
22893
22894         this.keyNav = new Roo.KeyNav(this.el, {
22895             "up" : function(e){
22896                 this.inKeyMode = true;
22897                 this.selectPrev();
22898             },
22899
22900             "down" : function(e){
22901                 if(!this.isExpanded()){
22902                     this.onTriggerClick();
22903                 }else{
22904                     this.inKeyMode = true;
22905                     this.selectNext();
22906                 }
22907             },
22908
22909             "enter" : function(e){
22910                 this.onViewClick();
22911                 //return true;
22912             },
22913
22914             "esc" : function(e){
22915                 this.collapse();
22916             },
22917
22918             "tab" : function(e){
22919                 this.onViewClick(false);
22920                 return true;
22921             },
22922
22923             scope : this,
22924
22925             doRelay : function(foo, bar, hname){
22926                 if(hname == 'down' || this.scope.isExpanded()){
22927                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22928                 }
22929                 return true;
22930             },
22931
22932             forceKeyDown: true
22933         });
22934         this.queryDelay = Math.max(this.queryDelay || 10,
22935                 this.mode == 'local' ? 10 : 250);
22936         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
22937         if(this.typeAhead){
22938             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
22939         }
22940         if(this.editable !== false){
22941             this.el.on("keyup", this.onKeyUp, this);
22942         }
22943         if(this.forceSelection){
22944             this.on('blur', this.doForce, this);
22945         }
22946     },
22947
22948     onDestroy : function(){
22949         if(this.view){
22950             this.view.setStore(null);
22951             this.view.el.removeAllListeners();
22952             this.view.el.remove();
22953             this.view.purgeListeners();
22954         }
22955         if(this.list){
22956             this.list.destroy();
22957         }
22958         if(this.store){
22959             this.store.un('beforeload', this.onBeforeLoad, this);
22960             this.store.un('load', this.onLoad, this);
22961             this.store.un('loadexception', this.collapse, this);
22962         }
22963         Roo.form.ComboBox.superclass.onDestroy.call(this);
22964     },
22965
22966     // private
22967     fireKey : function(e){
22968         if(e.isNavKeyPress() && !this.list.isVisible()){
22969             this.fireEvent("specialkey", this, e);
22970         }
22971     },
22972
22973     // private
22974     onResize: function(w, h){
22975         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
22976         
22977         if(typeof w != 'number'){
22978             // we do not handle it!?!?
22979             return;
22980         }
22981         var tw = this.trigger.getWidth();
22982         tw += this.addicon ? this.addicon.getWidth() : 0;
22983         tw += this.editicon ? this.editicon.getWidth() : 0;
22984         var x = w - tw;
22985         this.el.setWidth( this.adjustWidth('input', x));
22986             
22987         this.trigger.setStyle('left', x+'px');
22988         
22989         if(this.list && this.listWidth === undefined){
22990             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
22991             this.list.setWidth(lw);
22992             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22993         }
22994         
22995     
22996         
22997     },
22998
22999     /**
23000      * Allow or prevent the user from directly editing the field text.  If false is passed,
23001      * the user will only be able to select from the items defined in the dropdown list.  This method
23002      * is the runtime equivalent of setting the 'editable' config option at config time.
23003      * @param {Boolean} value True to allow the user to directly edit the field text
23004      */
23005     setEditable : function(value){
23006         if(value == this.editable){
23007             return;
23008         }
23009         this.editable = value;
23010         if(!value){
23011             this.el.dom.setAttribute('readOnly', true);
23012             this.el.on('mousedown', this.onTriggerClick,  this);
23013             this.el.addClass('x-combo-noedit');
23014         }else{
23015             this.el.dom.setAttribute('readOnly', false);
23016             this.el.un('mousedown', this.onTriggerClick,  this);
23017             this.el.removeClass('x-combo-noedit');
23018         }
23019     },
23020
23021     // private
23022     onBeforeLoad : function(){
23023         if(!this.hasFocus){
23024             return;
23025         }
23026         this.innerList.update(this.loadingText ?
23027                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23028         this.restrictHeight();
23029         this.selectedIndex = -1;
23030     },
23031
23032     // private
23033     onLoad : function(){
23034         if(!this.hasFocus){
23035             return;
23036         }
23037         if(this.store.getCount() > 0){
23038             this.expand();
23039             this.restrictHeight();
23040             if(this.lastQuery == this.allQuery){
23041                 if(this.editable){
23042                     this.el.dom.select();
23043                 }
23044                 if(!this.selectByValue(this.value, true)){
23045                     this.select(0, true);
23046                 }
23047             }else{
23048                 this.selectNext();
23049                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23050                     this.taTask.delay(this.typeAheadDelay);
23051                 }
23052             }
23053         }else{
23054             this.onEmptyResults();
23055         }
23056         //this.el.focus();
23057     },
23058
23059     // private
23060     onTypeAhead : function(){
23061         if(this.store.getCount() > 0){
23062             var r = this.store.getAt(0);
23063             var newValue = r.data[this.displayField];
23064             var len = newValue.length;
23065             var selStart = this.getRawValue().length;
23066             if(selStart != len){
23067                 this.setRawValue(newValue);
23068                 this.selectText(selStart, newValue.length);
23069             }
23070         }
23071     },
23072
23073     // private
23074     onSelect : function(record, index){
23075         if(this.fireEvent('beforeselect', this, record, index) !== false){
23076             this.setFromData(index > -1 ? record.data : false);
23077             this.collapse();
23078             this.fireEvent('select', this, record, index);
23079         }
23080     },
23081
23082     /**
23083      * Returns the currently selected field value or empty string if no value is set.
23084      * @return {String} value The selected value
23085      */
23086     getValue : function(){
23087         if(this.valueField){
23088             return typeof this.value != 'undefined' ? this.value : '';
23089         }else{
23090             return Roo.form.ComboBox.superclass.getValue.call(this);
23091         }
23092     },
23093
23094     /**
23095      * Clears any text/value currently set in the field
23096      */
23097     clearValue : function(){
23098         if(this.hiddenField){
23099             this.hiddenField.value = '';
23100         }
23101         this.value = '';
23102         this.setRawValue('');
23103         this.lastSelectionText = '';
23104         this.applyEmptyText();
23105     },
23106
23107     /**
23108      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23109      * will be displayed in the field.  If the value does not match the data value of an existing item,
23110      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23111      * Otherwise the field will be blank (although the value will still be set).
23112      * @param {String} value The value to match
23113      */
23114     setValue : function(v){
23115         var text = v;
23116         if(this.valueField){
23117             var r = this.findRecord(this.valueField, v);
23118             if(r){
23119                 text = r.data[this.displayField];
23120             }else if(this.valueNotFoundText !== undefined){
23121                 text = this.valueNotFoundText;
23122             }
23123         }
23124         this.lastSelectionText = text;
23125         if(this.hiddenField){
23126             this.hiddenField.value = v;
23127         }
23128         Roo.form.ComboBox.superclass.setValue.call(this, text);
23129         this.value = v;
23130     },
23131     /**
23132      * @property {Object} the last set data for the element
23133      */
23134     
23135     lastData : false,
23136     /**
23137      * Sets the value of the field based on a object which is related to the record format for the store.
23138      * @param {Object} value the value to set as. or false on reset?
23139      */
23140     setFromData : function(o){
23141         var dv = ''; // display value
23142         var vv = ''; // value value..
23143         this.lastData = o;
23144         if (this.displayField) {
23145             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23146         } else {
23147             // this is an error condition!!!
23148             console.log('no value field set for '+ this.name);
23149         }
23150         
23151         if(this.valueField){
23152             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23153         }
23154         if(this.hiddenField){
23155             this.hiddenField.value = vv;
23156             
23157             this.lastSelectionText = dv;
23158             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23159             this.value = vv;
23160             return;
23161         }
23162         // no hidden field.. - we store the value in 'value', but still display
23163         // display field!!!!
23164         this.lastSelectionText = dv;
23165         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23166         this.value = vv;
23167         
23168         
23169     },
23170     // private
23171     reset : function(){
23172         // overridden so that last data is reset..
23173         this.setValue(this.originalValue);
23174         this.clearInvalid();
23175         this.lastData = false;
23176     },
23177     // private
23178     findRecord : function(prop, value){
23179         var record;
23180         if(this.store.getCount() > 0){
23181             this.store.each(function(r){
23182                 if(r.data[prop] == value){
23183                     record = r;
23184                     return false;
23185                 }
23186             });
23187         }
23188         return record;
23189     },
23190
23191     // private
23192     onViewMove : function(e, t){
23193         this.inKeyMode = false;
23194     },
23195
23196     // private
23197     onViewOver : function(e, t){
23198         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23199             return;
23200         }
23201         var item = this.view.findItemFromChild(t);
23202         if(item){
23203             var index = this.view.indexOf(item);
23204             this.select(index, false);
23205         }
23206     },
23207
23208     // private
23209     onViewClick : function(doFocus){
23210         var index = this.view.getSelectedIndexes()[0];
23211         var r = this.store.getAt(index);
23212         if(r){
23213             this.onSelect(r, index);
23214         }
23215         if(doFocus !== false && !this.blockFocus){
23216             this.el.focus();
23217         }
23218     },
23219
23220     // private
23221     restrictHeight : function(){
23222         this.innerList.dom.style.height = '';
23223         var inner = this.innerList.dom;
23224         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23225         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23226         this.list.beginUpdate();
23227         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23228         this.list.alignTo(this.el, this.listAlign);
23229         this.list.endUpdate();
23230     },
23231
23232     // private
23233     onEmptyResults : function(){
23234         this.collapse();
23235     },
23236
23237     /**
23238      * Returns true if the dropdown list is expanded, else false.
23239      */
23240     isExpanded : function(){
23241         return this.list.isVisible();
23242     },
23243
23244     /**
23245      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23246      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23247      * @param {String} value The data value of the item to select
23248      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23249      * selected item if it is not currently in view (defaults to true)
23250      * @return {Boolean} True if the value matched an item in the list, else false
23251      */
23252     selectByValue : function(v, scrollIntoView){
23253         if(v !== undefined && v !== null){
23254             var r = this.findRecord(this.valueField || this.displayField, v);
23255             if(r){
23256                 this.select(this.store.indexOf(r), scrollIntoView);
23257                 return true;
23258             }
23259         }
23260         return false;
23261     },
23262
23263     /**
23264      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23265      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23266      * @param {Number} index The zero-based index of the list item to select
23267      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23268      * selected item if it is not currently in view (defaults to true)
23269      */
23270     select : function(index, scrollIntoView){
23271         this.selectedIndex = index;
23272         this.view.select(index);
23273         if(scrollIntoView !== false){
23274             var el = this.view.getNode(index);
23275             if(el){
23276                 this.innerList.scrollChildIntoView(el, false);
23277             }
23278         }
23279     },
23280
23281     // private
23282     selectNext : function(){
23283         var ct = this.store.getCount();
23284         if(ct > 0){
23285             if(this.selectedIndex == -1){
23286                 this.select(0);
23287             }else if(this.selectedIndex < ct-1){
23288                 this.select(this.selectedIndex+1);
23289             }
23290         }
23291     },
23292
23293     // private
23294     selectPrev : function(){
23295         var ct = this.store.getCount();
23296         if(ct > 0){
23297             if(this.selectedIndex == -1){
23298                 this.select(0);
23299             }else if(this.selectedIndex != 0){
23300                 this.select(this.selectedIndex-1);
23301             }
23302         }
23303     },
23304
23305     // private
23306     onKeyUp : function(e){
23307         if(this.editable !== false && !e.isSpecialKey()){
23308             this.lastKey = e.getKey();
23309             this.dqTask.delay(this.queryDelay);
23310         }
23311     },
23312
23313     // private
23314     validateBlur : function(){
23315         return !this.list || !this.list.isVisible();   
23316     },
23317
23318     // private
23319     initQuery : function(){
23320         this.doQuery(this.getRawValue());
23321     },
23322
23323     // private
23324     doForce : function(){
23325         if(this.el.dom.value.length > 0){
23326             this.el.dom.value =
23327                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23328             this.applyEmptyText();
23329         }
23330     },
23331
23332     /**
23333      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23334      * query allowing the query action to be canceled if needed.
23335      * @param {String} query The SQL query to execute
23336      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23337      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23338      * saved in the current store (defaults to false)
23339      */
23340     doQuery : function(q, forceAll){
23341         if(q === undefined || q === null){
23342             q = '';
23343         }
23344         var qe = {
23345             query: q,
23346             forceAll: forceAll,
23347             combo: this,
23348             cancel:false
23349         };
23350         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23351             return false;
23352         }
23353         q = qe.query;
23354         forceAll = qe.forceAll;
23355         if(forceAll === true || (q.length >= this.minChars)){
23356             if(this.lastQuery != q || this.alwaysQuery){
23357                 this.lastQuery = q;
23358                 if(this.mode == 'local'){
23359                     this.selectedIndex = -1;
23360                     if(forceAll){
23361                         this.store.clearFilter();
23362                     }else{
23363                         this.store.filter(this.displayField, q);
23364                     }
23365                     this.onLoad();
23366                 }else{
23367                     this.store.baseParams[this.queryParam] = q;
23368                     this.store.load({
23369                         params: this.getParams(q)
23370                     });
23371                     this.expand();
23372                 }
23373             }else{
23374                 this.selectedIndex = -1;
23375                 this.onLoad();   
23376             }
23377         }
23378     },
23379
23380     // private
23381     getParams : function(q){
23382         var p = {};
23383         //p[this.queryParam] = q;
23384         if(this.pageSize){
23385             p.start = 0;
23386             p.limit = this.pageSize;
23387         }
23388         return p;
23389     },
23390
23391     /**
23392      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23393      */
23394     collapse : function(){
23395         if(!this.isExpanded()){
23396             return;
23397         }
23398         this.list.hide();
23399         Roo.get(document).un('mousedown', this.collapseIf, this);
23400         Roo.get(document).un('mousewheel', this.collapseIf, this);
23401         if (!this.editable) {
23402             Roo.get(document).un('keydown', this.listKeyPress, this);
23403         }
23404         this.fireEvent('collapse', this);
23405     },
23406
23407     // private
23408     collapseIf : function(e){
23409         if(!e.within(this.wrap) && !e.within(this.list)){
23410             this.collapse();
23411         }
23412     },
23413
23414     /**
23415      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23416      */
23417     expand : function(){
23418         if(this.isExpanded() || !this.hasFocus){
23419             return;
23420         }
23421         this.list.alignTo(this.el, this.listAlign);
23422         this.list.show();
23423         Roo.get(document).on('mousedown', this.collapseIf, this);
23424         Roo.get(document).on('mousewheel', this.collapseIf, this);
23425         if (!this.editable) {
23426             Roo.get(document).on('keydown', this.listKeyPress, this);
23427         }
23428         
23429         this.fireEvent('expand', this);
23430     },
23431
23432     // private
23433     // Implements the default empty TriggerField.onTriggerClick function
23434     onTriggerClick : function(){
23435         if(this.disabled){
23436             return;
23437         }
23438         if(this.isExpanded()){
23439             this.collapse();
23440             if (!this.blockFocus) {
23441                 this.el.focus();
23442             }
23443             
23444         }else {
23445             this.hasFocus = true;
23446             if(this.triggerAction == 'all') {
23447                 this.doQuery(this.allQuery, true);
23448             } else {
23449                 this.doQuery(this.getRawValue());
23450             }
23451             if (!this.blockFocus) {
23452                 this.el.focus();
23453             }
23454         }
23455     },
23456     listKeyPress : function(e)
23457     {
23458         //Roo.log('listkeypress');
23459         // scroll to first matching element based on key pres..
23460         if (e.isSpecialKey()) {
23461             return false;
23462         }
23463         var k = String.fromCharCode(e.getKey()).toUpperCase();
23464         //Roo.log(k);
23465         var match  = false;
23466         var csel = this.view.getSelectedNodes();
23467         var cselitem = false;
23468         if (csel.length) {
23469             var ix = this.view.indexOf(csel[0]);
23470             cselitem  = this.store.getAt(ix);
23471             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23472                 cselitem = false;
23473             }
23474             
23475         }
23476         
23477         this.store.each(function(v) { 
23478             if (cselitem) {
23479                 // start at existing selection.
23480                 if (cselitem.id == v.id) {
23481                     cselitem = false;
23482                 }
23483                 return;
23484             }
23485                 
23486             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23487                 match = this.store.indexOf(v);
23488                 return false;
23489             }
23490         }, this);
23491         
23492         if (match === false) {
23493             return true; // no more action?
23494         }
23495         // scroll to?
23496         this.view.select(match);
23497         var sn = Roo.get(this.view.getSelectedNodes()[0])
23498         sn.scrollIntoView(sn.dom.parentNode, false);
23499     }
23500
23501     /** 
23502     * @cfg {Boolean} grow 
23503     * @hide 
23504     */
23505     /** 
23506     * @cfg {Number} growMin 
23507     * @hide 
23508     */
23509     /** 
23510     * @cfg {Number} growMax 
23511     * @hide 
23512     */
23513     /**
23514      * @hide
23515      * @method autoSize
23516      */
23517 });/*
23518  * Based on:
23519  * Ext JS Library 1.1.1
23520  * Copyright(c) 2006-2007, Ext JS, LLC.
23521  *
23522  * Originally Released Under LGPL - original licence link has changed is not relivant.
23523  *
23524  * Fork - LGPL
23525  * <script type="text/javascript">
23526  */
23527 /**
23528  * @class Roo.form.Checkbox
23529  * @extends Roo.form.Field
23530  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23531  * @constructor
23532  * Creates a new Checkbox
23533  * @param {Object} config Configuration options
23534  */
23535 Roo.form.Checkbox = function(config){
23536     Roo.form.Checkbox.superclass.constructor.call(this, config);
23537     this.addEvents({
23538         /**
23539          * @event check
23540          * Fires when the checkbox is checked or unchecked.
23541              * @param {Roo.form.Checkbox} this This checkbox
23542              * @param {Boolean} checked The new checked value
23543              */
23544         check : true
23545     });
23546 };
23547
23548 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23549     /**
23550      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23551      */
23552     focusClass : undefined,
23553     /**
23554      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23555      */
23556     fieldClass: "x-form-field",
23557     /**
23558      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23559      */
23560     checked: false,
23561     /**
23562      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23563      * {tag: "input", type: "checkbox", autocomplete: "off"})
23564      */
23565     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23566     /**
23567      * @cfg {String} boxLabel The text that appears beside the checkbox
23568      */
23569     boxLabel : "",
23570     /**
23571      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23572      */  
23573     inputValue : '1',
23574     /**
23575      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23576      */
23577      valueOff: '0', // value when not checked..
23578
23579     actionMode : 'viewEl', 
23580     //
23581     // private
23582     itemCls : 'x-menu-check-item x-form-item',
23583     groupClass : 'x-menu-group-item',
23584     inputType : 'hidden',
23585     
23586     
23587     inSetChecked: false, // check that we are not calling self...
23588     
23589     inputElement: false, // real input element?
23590     basedOn: false, // ????
23591     
23592     isFormField: true, // not sure where this is needed!!!!
23593
23594     onResize : function(){
23595         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23596         if(!this.boxLabel){
23597             this.el.alignTo(this.wrap, 'c-c');
23598         }
23599     },
23600
23601     initEvents : function(){
23602         Roo.form.Checkbox.superclass.initEvents.call(this);
23603         this.el.on("click", this.onClick,  this);
23604         this.el.on("change", this.onClick,  this);
23605     },
23606
23607
23608     getResizeEl : function(){
23609         return this.wrap;
23610     },
23611
23612     getPositionEl : function(){
23613         return this.wrap;
23614     },
23615
23616     // private
23617     onRender : function(ct, position){
23618         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23619         /*
23620         if(this.inputValue !== undefined){
23621             this.el.dom.value = this.inputValue;
23622         }
23623         */
23624         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23625         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23626         var viewEl = this.wrap.createChild({ 
23627             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23628         this.viewEl = viewEl;   
23629         this.wrap.on('click', this.onClick,  this); 
23630         
23631         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23632         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23633         
23634         
23635         
23636         if(this.boxLabel){
23637             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23638         //    viewEl.on('click', this.onClick,  this); 
23639         }
23640         //if(this.checked){
23641             this.setChecked(this.checked);
23642         //}else{
23643             //this.checked = this.el.dom;
23644         //}
23645
23646     },
23647
23648     // private
23649     initValue : Roo.emptyFn,
23650
23651     /**
23652      * Returns the checked state of the checkbox.
23653      * @return {Boolean} True if checked, else false
23654      */
23655     getValue : function(){
23656         if(this.el){
23657             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23658         }
23659         return this.valueOff;
23660         
23661     },
23662
23663         // private
23664     onClick : function(){ 
23665         this.setChecked(!this.checked);
23666
23667         //if(this.el.dom.checked != this.checked){
23668         //    this.setValue(this.el.dom.checked);
23669        // }
23670     },
23671
23672     /**
23673      * Sets the checked state of the checkbox.
23674      * On is always based on a string comparison between inputValue and the param.
23675      * @param {Boolean/String} value - the value to set 
23676      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23677      */
23678     setValue : function(v,suppressEvent){
23679         
23680         
23681         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23682         //if(this.el && this.el.dom){
23683         //    this.el.dom.checked = this.checked;
23684         //    this.el.dom.defaultChecked = this.checked;
23685         //}
23686         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23687         //this.fireEvent("check", this, this.checked);
23688     },
23689     // private..
23690     setChecked : function(state,suppressEvent)
23691     {
23692         if (this.inSetChecked) {
23693             this.checked = state;
23694             return;
23695         }
23696         
23697     
23698         if(this.wrap){
23699             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23700         }
23701         this.checked = state;
23702         if(suppressEvent !== true){
23703             this.fireEvent('check', this, state);
23704         }
23705         this.inSetChecked = true;
23706         this.el.dom.value = state ? this.inputValue : this.valueOff;
23707         this.inSetChecked = false;
23708         
23709     },
23710     // handle setting of hidden value by some other method!!?!?
23711     setFromHidden: function()
23712     {
23713         if(!this.el){
23714             return;
23715         }
23716         //console.log("SET FROM HIDDEN");
23717         //alert('setFrom hidden');
23718         this.setValue(this.el.dom.value);
23719     },
23720     
23721     onDestroy : function()
23722     {
23723         if(this.viewEl){
23724             Roo.get(this.viewEl).remove();
23725         }
23726          
23727         Roo.form.Checkbox.superclass.onDestroy.call(this);
23728     }
23729
23730 });/*
23731  * Based on:
23732  * Ext JS Library 1.1.1
23733  * Copyright(c) 2006-2007, Ext JS, LLC.
23734  *
23735  * Originally Released Under LGPL - original licence link has changed is not relivant.
23736  *
23737  * Fork - LGPL
23738  * <script type="text/javascript">
23739  */
23740  
23741 /**
23742  * @class Roo.form.Radio
23743  * @extends Roo.form.Checkbox
23744  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23745  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23746  * @constructor
23747  * Creates a new Radio
23748  * @param {Object} config Configuration options
23749  */
23750 Roo.form.Radio = function(){
23751     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23752 };
23753 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23754     inputType: 'radio',
23755
23756     /**
23757      * If this radio is part of a group, it will return the selected value
23758      * @return {String}
23759      */
23760     getGroupValue : function(){
23761         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23762     }
23763 });//<script type="text/javascript">
23764
23765 /*
23766  * Ext JS Library 1.1.1
23767  * Copyright(c) 2006-2007, Ext JS, LLC.
23768  * licensing@extjs.com
23769  * 
23770  * http://www.extjs.com/license
23771  */
23772  
23773  /*
23774   * 
23775   * Known bugs:
23776   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23777   * - IE ? - no idea how much works there.
23778   * 
23779   * 
23780   * 
23781   */
23782  
23783
23784 /**
23785  * @class Ext.form.HtmlEditor
23786  * @extends Ext.form.Field
23787  * Provides a lightweight HTML Editor component.
23788  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23789  * 
23790  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23791  * supported by this editor.</b><br/><br/>
23792  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23793  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23794  */
23795 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23796       /**
23797      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23798      */
23799     toolbars : false,
23800     /**
23801      * @cfg {String} createLinkText The default text for the create link prompt
23802      */
23803     createLinkText : 'Please enter the URL for the link:',
23804     /**
23805      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23806      */
23807     defaultLinkValue : 'http:/'+'/',
23808    
23809     
23810     // id of frame..
23811     frameId: false,
23812     
23813     // private properties
23814     validationEvent : false,
23815     deferHeight: true,
23816     initialized : false,
23817     activated : false,
23818     sourceEditMode : false,
23819     onFocus : Roo.emptyFn,
23820     iframePad:3,
23821     hideMode:'offsets',
23822     defaultAutoCreate : {
23823         tag: "textarea",
23824         style:"width:500px;height:300px;",
23825         autocomplete: "off"
23826     },
23827
23828     // private
23829     initComponent : function(){
23830         this.addEvents({
23831             /**
23832              * @event initialize
23833              * Fires when the editor is fully initialized (including the iframe)
23834              * @param {HtmlEditor} this
23835              */
23836             initialize: true,
23837             /**
23838              * @event activate
23839              * Fires when the editor is first receives the focus. Any insertion must wait
23840              * until after this event.
23841              * @param {HtmlEditor} this
23842              */
23843             activate: true,
23844              /**
23845              * @event beforesync
23846              * Fires before the textarea is updated with content from the editor iframe. Return false
23847              * to cancel the sync.
23848              * @param {HtmlEditor} this
23849              * @param {String} html
23850              */
23851             beforesync: true,
23852              /**
23853              * @event beforepush
23854              * Fires before the iframe editor is updated with content from the textarea. Return false
23855              * to cancel the push.
23856              * @param {HtmlEditor} this
23857              * @param {String} html
23858              */
23859             beforepush: true,
23860              /**
23861              * @event sync
23862              * Fires when the textarea is updated with content from the editor iframe.
23863              * @param {HtmlEditor} this
23864              * @param {String} html
23865              */
23866             sync: true,
23867              /**
23868              * @event push
23869              * Fires when the iframe editor is updated with content from the textarea.
23870              * @param {HtmlEditor} this
23871              * @param {String} html
23872              */
23873             push: true,
23874              /**
23875              * @event editmodechange
23876              * Fires when the editor switches edit modes
23877              * @param {HtmlEditor} this
23878              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23879              */
23880             editmodechange: true,
23881             /**
23882              * @event editorevent
23883              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23884              * @param {HtmlEditor} this
23885              */
23886             editorevent: true
23887         })
23888     },
23889
23890     /**
23891      * Protected method that will not generally be called directly. It
23892      * is called when the editor creates its toolbar. Override this method if you need to
23893      * add custom toolbar buttons.
23894      * @param {HtmlEditor} editor
23895      */
23896     createToolbar : function(editor){
23897         if (!editor.toolbars || !editor.toolbars.length) {
23898             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23899         }
23900         
23901         for (var i =0 ; i < editor.toolbars.length;i++) {
23902             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23903             editor.toolbars[i].init(editor);
23904         }
23905          
23906         
23907     },
23908
23909     /**
23910      * Protected method that will not generally be called directly. It
23911      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23912      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23913      */
23914     getDocMarkup : function(){
23915         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23916     },
23917
23918     // private
23919     onRender : function(ct, position){
23920         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23921         this.el.dom.style.border = '0 none';
23922         this.el.dom.setAttribute('tabIndex', -1);
23923         this.el.addClass('x-hidden');
23924         if(Roo.isIE){ // fix IE 1px bogus margin
23925             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23926         }
23927         this.wrap = this.el.wrap({
23928             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23929         });
23930
23931         this.frameId = Roo.id();
23932         this.createToolbar(this);
23933         
23934         
23935         
23936         
23937       
23938         
23939         var iframe = this.wrap.createChild({
23940             tag: 'iframe',
23941             id: this.frameId,
23942             name: this.frameId,
23943             frameBorder : 'no',
23944             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23945         });
23946         
23947        // console.log(iframe);
23948         //this.wrap.dom.appendChild(iframe);
23949
23950         this.iframe = iframe.dom;
23951
23952          this.assignDocWin();
23953         
23954         this.doc.designMode = 'on';
23955        
23956         this.doc.open();
23957         this.doc.write(this.getDocMarkup());
23958         this.doc.close();
23959
23960         
23961         var task = { // must defer to wait for browser to be ready
23962             run : function(){
23963                 //console.log("run task?" + this.doc.readyState);
23964                 this.assignDocWin();
23965                 if(this.doc.body || this.doc.readyState == 'complete'){
23966                     try {
23967                         this.doc.designMode="on";
23968                     } catch (e) {
23969                         return;
23970                     }
23971                     Roo.TaskMgr.stop(task);
23972                     this.initEditor.defer(10, this);
23973                 }
23974             },
23975             interval : 10,
23976             duration:10000,
23977             scope: this
23978         };
23979         Roo.TaskMgr.start(task);
23980
23981         if(!this.width){
23982             this.setSize(this.el.getSize());
23983         }
23984     },
23985
23986     // private
23987     onResize : function(w, h){
23988         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
23989         if(this.el && this.iframe){
23990             if(typeof w == 'number'){
23991                 var aw = w - this.wrap.getFrameWidth('lr');
23992                 this.el.setWidth(this.adjustWidth('textarea', aw));
23993                 this.iframe.style.width = aw + 'px';
23994             }
23995             if(typeof h == 'number'){
23996                 var tbh = 0;
23997                 for (var i =0; i < this.toolbars.length;i++) {
23998                     // fixme - ask toolbars for heights?
23999                     tbh += this.toolbars[i].tb.el.getHeight();
24000                 }
24001                 
24002                 
24003                 
24004                 
24005                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24006                 this.el.setHeight(this.adjustWidth('textarea', ah));
24007                 this.iframe.style.height = ah + 'px';
24008                 if(this.doc){
24009                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24010                 }
24011             }
24012         }
24013     },
24014
24015     /**
24016      * Toggles the editor between standard and source edit mode.
24017      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24018      */
24019     toggleSourceEdit : function(sourceEditMode){
24020         
24021         this.sourceEditMode = sourceEditMode === true;
24022         
24023         if(this.sourceEditMode){
24024           
24025             this.syncValue();
24026             this.iframe.className = 'x-hidden';
24027             this.el.removeClass('x-hidden');
24028             this.el.dom.removeAttribute('tabIndex');
24029             this.el.focus();
24030         }else{
24031              
24032             this.pushValue();
24033             this.iframe.className = '';
24034             this.el.addClass('x-hidden');
24035             this.el.dom.setAttribute('tabIndex', -1);
24036             this.deferFocus();
24037         }
24038         this.setSize(this.wrap.getSize());
24039         this.fireEvent('editmodechange', this, this.sourceEditMode);
24040     },
24041
24042     // private used internally
24043     createLink : function(){
24044         var url = prompt(this.createLinkText, this.defaultLinkValue);
24045         if(url && url != 'http:/'+'/'){
24046             this.relayCmd('createlink', url);
24047         }
24048     },
24049
24050     // private (for BoxComponent)
24051     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24052
24053     // private (for BoxComponent)
24054     getResizeEl : function(){
24055         return this.wrap;
24056     },
24057
24058     // private (for BoxComponent)
24059     getPositionEl : function(){
24060         return this.wrap;
24061     },
24062
24063     // private
24064     initEvents : function(){
24065         this.originalValue = this.getValue();
24066     },
24067
24068     /**
24069      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24070      * @method
24071      */
24072     markInvalid : Roo.emptyFn,
24073     /**
24074      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24075      * @method
24076      */
24077     clearInvalid : Roo.emptyFn,
24078
24079     setValue : function(v){
24080         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24081         this.pushValue();
24082     },
24083
24084     /**
24085      * Protected method that will not generally be called directly. If you need/want
24086      * custom HTML cleanup, this is the method you should override.
24087      * @param {String} html The HTML to be cleaned
24088      * return {String} The cleaned HTML
24089      */
24090     cleanHtml : function(html){
24091         html = String(html);
24092         if(html.length > 5){
24093             if(Roo.isSafari){ // strip safari nonsense
24094                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24095             }
24096         }
24097         if(html == '&nbsp;'){
24098             html = '';
24099         }
24100         return html;
24101     },
24102
24103     /**
24104      * Protected method that will not generally be called directly. Syncs the contents
24105      * of the editor iframe with the textarea.
24106      */
24107     syncValue : function(){
24108         if(this.initialized){
24109             var bd = (this.doc.body || this.doc.documentElement);
24110             var html = bd.innerHTML;
24111             if(Roo.isSafari){
24112                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24113                 var m = bs.match(/text-align:(.*?);/i);
24114                 if(m && m[1]){
24115                     html = '<div style="'+m[0]+'">' + html + '</div>';
24116                 }
24117             }
24118             html = this.cleanHtml(html);
24119             if(this.fireEvent('beforesync', this, html) !== false){
24120                 this.el.dom.value = html;
24121                 this.fireEvent('sync', this, html);
24122             }
24123         }
24124     },
24125
24126     /**
24127      * Protected method that will not generally be called directly. Pushes the value of the textarea
24128      * into the iframe editor.
24129      */
24130     pushValue : function(){
24131         if(this.initialized){
24132             var v = this.el.dom.value;
24133             if(v.length < 1){
24134                 v = '&#160;';
24135             }
24136             if(this.fireEvent('beforepush', this, v) !== false){
24137                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24138                 this.fireEvent('push', this, v);
24139             }
24140         }
24141     },
24142
24143     // private
24144     deferFocus : function(){
24145         this.focus.defer(10, this);
24146     },
24147
24148     // doc'ed in Field
24149     focus : function(){
24150         if(this.win && !this.sourceEditMode){
24151             this.win.focus();
24152         }else{
24153             this.el.focus();
24154         }
24155     },
24156     
24157     assignDocWin: function()
24158     {
24159         var iframe = this.iframe;
24160         
24161          if(Roo.isIE){
24162             this.doc = iframe.contentWindow.document;
24163             this.win = iframe.contentWindow;
24164         } else {
24165             if (!Roo.get(this.frameId)) {
24166                 return;
24167             }
24168             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24169             this.win = Roo.get(this.frameId).dom.contentWindow;
24170         }
24171     },
24172     
24173     // private
24174     initEditor : function(){
24175         //console.log("INIT EDITOR");
24176         this.assignDocWin();
24177         
24178         
24179         
24180         this.doc.designMode="on";
24181         this.doc.open();
24182         this.doc.write(this.getDocMarkup());
24183         this.doc.close();
24184         
24185         var dbody = (this.doc.body || this.doc.documentElement);
24186         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24187         // this copies styles from the containing element into thsi one..
24188         // not sure why we need all of this..
24189         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24190         ss['background-attachment'] = 'fixed'; // w3c
24191         dbody.bgProperties = 'fixed'; // ie
24192         Roo.DomHelper.applyStyles(dbody, ss);
24193         Roo.EventManager.on(this.doc, {
24194             'mousedown': this.onEditorEvent,
24195             'dblclick': this.onEditorEvent,
24196             'click': this.onEditorEvent,
24197             'keyup': this.onEditorEvent,
24198             buffer:100,
24199             scope: this
24200         });
24201         if(Roo.isGecko){
24202             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24203         }
24204         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24205             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24206         }
24207         this.initialized = true;
24208
24209         this.fireEvent('initialize', this);
24210         this.pushValue();
24211     },
24212
24213     // private
24214     onDestroy : function(){
24215         
24216         
24217         
24218         if(this.rendered){
24219             
24220             for (var i =0; i < this.toolbars.length;i++) {
24221                 // fixme - ask toolbars for heights?
24222                 this.toolbars[i].onDestroy();
24223             }
24224             
24225             this.wrap.dom.innerHTML = '';
24226             this.wrap.remove();
24227         }
24228     },
24229
24230     // private
24231     onFirstFocus : function(){
24232         
24233         this.assignDocWin();
24234         
24235         
24236         this.activated = true;
24237         for (var i =0; i < this.toolbars.length;i++) {
24238             this.toolbars[i].onFirstFocus();
24239         }
24240        
24241         if(Roo.isGecko){ // prevent silly gecko errors
24242             this.win.focus();
24243             var s = this.win.getSelection();
24244             if(!s.focusNode || s.focusNode.nodeType != 3){
24245                 var r = s.getRangeAt(0);
24246                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24247                 r.collapse(true);
24248                 this.deferFocus();
24249             }
24250             try{
24251                 this.execCmd('useCSS', true);
24252                 this.execCmd('styleWithCSS', false);
24253             }catch(e){}
24254         }
24255         this.fireEvent('activate', this);
24256     },
24257
24258     // private
24259     adjustFont: function(btn){
24260         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24261         //if(Roo.isSafari){ // safari
24262         //    adjust *= 2;
24263        // }
24264         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24265         if(Roo.isSafari){ // safari
24266             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24267             v =  (v < 10) ? 10 : v;
24268             v =  (v > 48) ? 48 : v;
24269             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24270             
24271         }
24272         
24273         
24274         v = Math.max(1, v+adjust);
24275         
24276         this.execCmd('FontSize', v  );
24277     },
24278
24279     onEditorEvent : function(e){
24280         this.fireEvent('editorevent', this, e);
24281       //  this.updateToolbar();
24282         this.syncValue();
24283     },
24284
24285     insertTag : function(tg)
24286     {
24287         // could be a bit smarter... -> wrap the current selected tRoo..
24288         
24289         this.execCmd("formatblock",   tg);
24290         
24291     },
24292     
24293     insertText : function(txt)
24294     {
24295         
24296         
24297         range = this.createRange();
24298         range.deleteContents();
24299                //alert(Sender.getAttribute('label'));
24300                
24301         range.insertNode(this.doc.createTextNode(txt));
24302     } ,
24303     
24304     // private
24305     relayBtnCmd : function(btn){
24306         this.relayCmd(btn.cmd);
24307     },
24308
24309     /**
24310      * Executes a Midas editor command on the editor document and performs necessary focus and
24311      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24312      * @param {String} cmd The Midas command
24313      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24314      */
24315     relayCmd : function(cmd, value){
24316         this.win.focus();
24317         this.execCmd(cmd, value);
24318         this.fireEvent('editorevent', this);
24319         //this.updateToolbar();
24320         this.deferFocus();
24321     },
24322
24323     /**
24324      * Executes a Midas editor command directly on the editor document.
24325      * For visual commands, you should use {@link #relayCmd} instead.
24326      * <b>This should only be called after the editor is initialized.</b>
24327      * @param {String} cmd The Midas command
24328      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24329      */
24330     execCmd : function(cmd, value){
24331         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24332         this.syncValue();
24333     },
24334
24335    
24336     /**
24337      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24338      * to insert tRoo.
24339      * @param {String} text
24340      */
24341     insertAtCursor : function(text){
24342         if(!this.activated){
24343             return;
24344         }
24345         if(Roo.isIE){
24346             this.win.focus();
24347             var r = this.doc.selection.createRange();
24348             if(r){
24349                 r.collapse(true);
24350                 r.pasteHTML(text);
24351                 this.syncValue();
24352                 this.deferFocus();
24353             }
24354         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24355             this.win.focus();
24356             this.execCmd('InsertHTML', text);
24357             this.deferFocus();
24358         }
24359     },
24360  // private
24361     mozKeyPress : function(e){
24362         if(e.ctrlKey){
24363             var c = e.getCharCode(), cmd;
24364           
24365             if(c > 0){
24366                 c = String.fromCharCode(c).toLowerCase();
24367                 switch(c){
24368                     case 'b':
24369                         cmd = 'bold';
24370                     break;
24371                     case 'i':
24372                         cmd = 'italic';
24373                     break;
24374                     case 'u':
24375                         cmd = 'underline';
24376                     case 'v':
24377                         this.cleanUpPaste.defer(100, this);
24378                         return;
24379                     break;
24380                 }
24381                 if(cmd){
24382                     this.win.focus();
24383                     this.execCmd(cmd);
24384                     this.deferFocus();
24385                     e.preventDefault();
24386                 }
24387                 
24388             }
24389         }
24390     },
24391
24392     // private
24393     fixKeys : function(){ // load time branching for fastest keydown performance
24394         if(Roo.isIE){
24395             return function(e){
24396                 var k = e.getKey(), r;
24397                 if(k == e.TAB){
24398                     e.stopEvent();
24399                     r = this.doc.selection.createRange();
24400                     if(r){
24401                         r.collapse(true);
24402                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24403                         this.deferFocus();
24404                     }
24405                     return;
24406                 }
24407                 
24408                 if(k == e.ENTER){
24409                     r = this.doc.selection.createRange();
24410                     if(r){
24411                         var target = r.parentElement();
24412                         if(!target || target.tagName.toLowerCase() != 'li'){
24413                             e.stopEvent();
24414                             r.pasteHTML('<br />');
24415                             r.collapse(false);
24416                             r.select();
24417                         }
24418                     }
24419                 }
24420                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24421                     this.cleanUpPaste.defer(100, this);
24422                     return;
24423                 }
24424                 
24425                 
24426             };
24427         }else if(Roo.isOpera){
24428             return function(e){
24429                 var k = e.getKey();
24430                 if(k == e.TAB){
24431                     e.stopEvent();
24432                     this.win.focus();
24433                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24434                     this.deferFocus();
24435                 }
24436                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24437                     this.cleanUpPaste.defer(100, this);
24438                     return;
24439                 }
24440                 
24441             };
24442         }else if(Roo.isSafari){
24443             return function(e){
24444                 var k = e.getKey();
24445                 
24446                 if(k == e.TAB){
24447                     e.stopEvent();
24448                     this.execCmd('InsertText','\t');
24449                     this.deferFocus();
24450                     return;
24451                 }
24452                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24453                     this.cleanUpPaste.defer(100, this);
24454                     return;
24455                 }
24456                 
24457              };
24458         }
24459     }(),
24460     
24461     getAllAncestors: function()
24462     {
24463         var p = this.getSelectedNode();
24464         var a = [];
24465         if (!p) {
24466             a.push(p); // push blank onto stack..
24467             p = this.getParentElement();
24468         }
24469         
24470         
24471         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24472             a.push(p);
24473             p = p.parentNode;
24474         }
24475         a.push(this.doc.body);
24476         return a;
24477     },
24478     lastSel : false,
24479     lastSelNode : false,
24480     
24481     
24482     getSelection : function() 
24483     {
24484         this.assignDocWin();
24485         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24486     },
24487     
24488     getSelectedNode: function() 
24489     {
24490         // this may only work on Gecko!!!
24491         
24492         // should we cache this!!!!
24493         
24494         
24495         
24496          
24497         var range = this.createRange(this.getSelection());
24498         
24499         if (Roo.isIE) {
24500             var parent = range.parentElement();
24501             while (true) {
24502                 var testRange = range.duplicate();
24503                 testRange.moveToElementText(parent);
24504                 if (testRange.inRange(range)) {
24505                     break;
24506                 }
24507                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24508                     break;
24509                 }
24510                 parent = parent.parentElement;
24511             }
24512             return parent;
24513         }
24514         
24515         
24516         var ar = range.endContainer.childNodes;
24517         if (!ar.length) {
24518             ar = range.commonAncestorContainer.childNodes;
24519             //alert(ar.length);
24520         }
24521         var nodes = [];
24522         var other_nodes = [];
24523         var has_other_nodes = false;
24524         for (var i=0;i<ar.length;i++) {
24525             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24526                 continue;
24527             }
24528             // fullly contained node.
24529             
24530             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24531                 nodes.push(ar[i]);
24532                 continue;
24533             }
24534             
24535             // probably selected..
24536             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24537                 other_nodes.push(ar[i]);
24538                 continue;
24539             }
24540             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24541                 continue;
24542             }
24543             
24544             
24545             has_other_nodes = true;
24546         }
24547         if (!nodes.length && other_nodes.length) {
24548             nodes= other_nodes;
24549         }
24550         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24551             return false;
24552         }
24553         
24554         return nodes[0];
24555     },
24556     createRange: function(sel)
24557     {
24558         // this has strange effects when using with 
24559         // top toolbar - not sure if it's a great idea.
24560         //this.editor.contentWindow.focus();
24561         if (typeof sel != "undefined") {
24562             try {
24563                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24564             } catch(e) {
24565                 return this.doc.createRange();
24566             }
24567         } else {
24568             return this.doc.createRange();
24569         }
24570     },
24571     getParentElement: function()
24572     {
24573         
24574         this.assignDocWin();
24575         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24576         
24577         var range = this.createRange(sel);
24578          
24579         try {
24580             var p = range.commonAncestorContainer;
24581             while (p.nodeType == 3) { // text node
24582                 p = p.parentNode;
24583             }
24584             return p;
24585         } catch (e) {
24586             return null;
24587         }
24588     
24589     },
24590     
24591     
24592     
24593     // BC Hacks - cause I cant work out what i was trying to do..
24594     rangeIntersectsNode : function(range, node)
24595     {
24596         var nodeRange = node.ownerDocument.createRange();
24597         try {
24598             nodeRange.selectNode(node);
24599         }
24600         catch (e) {
24601             nodeRange.selectNodeContents(node);
24602         }
24603
24604         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24605                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24606     },
24607     rangeCompareNode : function(range, node) {
24608         var nodeRange = node.ownerDocument.createRange();
24609         try {
24610             nodeRange.selectNode(node);
24611         } catch (e) {
24612             nodeRange.selectNodeContents(node);
24613         }
24614         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24615         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24616
24617         if (nodeIsBefore && !nodeIsAfter)
24618             return 0;
24619         if (!nodeIsBefore && nodeIsAfter)
24620             return 1;
24621         if (nodeIsBefore && nodeIsAfter)
24622             return 2;
24623
24624         return 3;
24625     },
24626
24627     // private? - in a new class?
24628     cleanUpPaste :  function()
24629     {
24630         // cleans up the whole document..
24631       //  console.log('cleanuppaste');
24632         this.cleanUpChildren(this.doc.body)
24633         
24634         
24635     },
24636     cleanUpChildren : function (n)
24637     {
24638         if (!n.childNodes.length) {
24639             return;
24640         }
24641         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24642            this.cleanUpChild(n.childNodes[i]);
24643         }
24644     },
24645     
24646     
24647         
24648     
24649     cleanUpChild : function (node)
24650     {
24651         //console.log(node);
24652         if (node.nodeName == "#text") {
24653             // clean up silly Windows -- stuff?
24654             return; 
24655         }
24656         if (node.nodeName == "#comment") {
24657             node.parentNode.removeChild(node);
24658             // clean up silly Windows -- stuff?
24659             return; 
24660         }
24661         
24662         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24663             // remove node.
24664             node.parentNode.removeChild(node);
24665             return;
24666             
24667         }
24668         if (!node.attributes || !node.attributes.length) {
24669             this.cleanUpChildren(node);
24670             return;
24671         }
24672         
24673         function cleanAttr(n,v)
24674         {
24675             
24676             if (v.match(/^\./) || v.match(/^\//)) {
24677                 return;
24678             }
24679             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24680                 return;
24681             }
24682             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24683             node.removeAttribute(n);
24684             
24685         }
24686         
24687         function cleanStyle(n,v)
24688         {
24689             if (v.match(/expression/)) { //XSS?? should we even bother..
24690                 node.removeAttribute(n);
24691                 return;
24692             }
24693             
24694             
24695             var parts = v.split(/;/);
24696             Roo.each(parts, function(p) {
24697                 p = p.replace(/\s+/g,'');
24698                 if (!p.length) {
24699                     return;
24700                 }
24701                 var l = p.split(':').shift().replace(/\s+/g,'');
24702                 
24703                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24704                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24705                     node.removeAttribute(n);
24706                     return false;
24707                 }
24708             });
24709             
24710             
24711         }
24712         
24713         
24714         for (var i = node.attributes.length-1; i > -1 ; i--) {
24715             var a = node.attributes[i];
24716             //console.log(a);
24717             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24718                 node.removeAttribute(a.name);
24719                 return;
24720             }
24721             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24722                 cleanAttr(a.name,a.value); // fixme..
24723                 return;
24724             }
24725             if (a.name == 'style') {
24726                 cleanStyle(a.name,a.value);
24727             }
24728             /// clean up MS crap..
24729             if (a.name == 'class') {
24730                 if (a.value.match(/^Mso/)) {
24731                     node.className = '';
24732                 }
24733             }
24734             
24735             // style cleanup!?
24736             // class cleanup?
24737             
24738         }
24739         
24740         
24741         this.cleanUpChildren(node);
24742         
24743         
24744     }
24745     
24746     
24747     // hide stuff that is not compatible
24748     /**
24749      * @event blur
24750      * @hide
24751      */
24752     /**
24753      * @event change
24754      * @hide
24755      */
24756     /**
24757      * @event focus
24758      * @hide
24759      */
24760     /**
24761      * @event specialkey
24762      * @hide
24763      */
24764     /**
24765      * @cfg {String} fieldClass @hide
24766      */
24767     /**
24768      * @cfg {String} focusClass @hide
24769      */
24770     /**
24771      * @cfg {String} autoCreate @hide
24772      */
24773     /**
24774      * @cfg {String} inputType @hide
24775      */
24776     /**
24777      * @cfg {String} invalidClass @hide
24778      */
24779     /**
24780      * @cfg {String} invalidText @hide
24781      */
24782     /**
24783      * @cfg {String} msgFx @hide
24784      */
24785     /**
24786      * @cfg {String} validateOnBlur @hide
24787      */
24788 });
24789
24790 Roo.form.HtmlEditor.white = [
24791         'area', 'br', 'img', 'input', 'hr', 'wbr',
24792         
24793        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24794        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24795        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24796        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24797        'table',   'ul',         'xmp', 
24798        
24799        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24800       'thead',   'tr', 
24801      
24802       'dir', 'menu', 'ol', 'ul', 'dl',
24803        
24804       'embed',  'object'
24805 ];
24806
24807
24808 Roo.form.HtmlEditor.black = [
24809     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24810         'applet', // 
24811         'base',   'basefont', 'bgsound', 'blink',  'body', 
24812         'frame',  'frameset', 'head',    'html',   'ilayer', 
24813         'iframe', 'layer',  'link',     'meta',    'object',   
24814         'script', 'style' ,'title',  'xml' // clean later..
24815 ];
24816 Roo.form.HtmlEditor.clean = [
24817     'script', 'style', 'title', 'xml'
24818 ];
24819
24820 // attributes..
24821
24822 Roo.form.HtmlEditor.ablack = [
24823     'on'
24824 ];
24825     
24826 Roo.form.HtmlEditor.aclean = [ 
24827     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24828 ];
24829
24830 // protocols..
24831 Roo.form.HtmlEditor.pwhite= [
24832         'http',  'https',  'mailto'
24833 ];
24834
24835 Roo.form.HtmlEditor.cwhite= [
24836         'text-align',
24837         'font-size'
24838 ];
24839
24840 // <script type="text/javascript">
24841 /*
24842  * Based on
24843  * Ext JS Library 1.1.1
24844  * Copyright(c) 2006-2007, Ext JS, LLC.
24845  *  
24846  
24847  */
24848
24849 /**
24850  * @class Roo.form.HtmlEditorToolbar1
24851  * Basic Toolbar
24852  * 
24853  * Usage:
24854  *
24855  new Roo.form.HtmlEditor({
24856     ....
24857     toolbars : [
24858         new Roo.form.HtmlEditorToolbar1({
24859             disable : { fonts: 1 , format: 1, ..., ... , ...],
24860             btns : [ .... ]
24861         })
24862     }
24863      
24864  * 
24865  * @cfg {Object} disable List of elements to disable..
24866  * @cfg {Array} btns List of additional buttons.
24867  * 
24868  * 
24869  * NEEDS Extra CSS? 
24870  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24871  */
24872  
24873 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24874 {
24875     
24876     Roo.apply(this, config);
24877     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24878     // dont call parent... till later.
24879 }
24880
24881 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24882     
24883     tb: false,
24884     
24885     rendered: false,
24886     
24887     editor : false,
24888     /**
24889      * @cfg {Object} disable  List of toolbar elements to disable
24890          
24891      */
24892     disable : false,
24893       /**
24894      * @cfg {Array} fontFamilies An array of available font families
24895      */
24896     fontFamilies : [
24897         'Arial',
24898         'Courier New',
24899         'Tahoma',
24900         'Times New Roman',
24901         'Verdana'
24902     ],
24903     
24904     specialChars : [
24905            "&#169;",
24906           "&#174;",     
24907           "&#8482;",    
24908           "&#163;" ,    
24909          // "&#8212;",    
24910           "&#8230;",    
24911           "&#247;" ,    
24912         //  "&#225;" ,     ?? a acute?
24913            "&#8364;"    , //Euro
24914        //   "&#8220;"    ,
24915         //  "&#8221;"    ,
24916         //  "&#8226;"    ,
24917           "&#176;"  //   , // degrees
24918
24919          // "&#233;"     , // e ecute
24920          // "&#250;"     , // u ecute?
24921     ],
24922     inputElements : [ 
24923             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24924             "input:submit", "input:button", "select", "textarea", "label" ],
24925     formats : [
24926         ["p"] ,  
24927         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24928         ["pre"],[ "code"], 
24929         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24930     ],
24931      /**
24932      * @cfg {String} defaultFont default font to use.
24933      */
24934     defaultFont: 'tahoma',
24935    
24936     fontSelect : false,
24937     
24938     
24939     formatCombo : false,
24940     
24941     init : function(editor)
24942     {
24943         this.editor = editor;
24944         
24945         
24946         var fid = editor.frameId;
24947         var etb = this;
24948         function btn(id, toggle, handler){
24949             var xid = fid + '-'+ id ;
24950             return {
24951                 id : xid,
24952                 cmd : id,
24953                 cls : 'x-btn-icon x-edit-'+id,
24954                 enableToggle:toggle !== false,
24955                 scope: editor, // was editor...
24956                 handler:handler||editor.relayBtnCmd,
24957                 clickEvent:'mousedown',
24958                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24959                 tabIndex:-1
24960             };
24961         }
24962         
24963         
24964         
24965         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
24966         this.tb = tb;
24967          // stop form submits
24968         tb.el.on('click', function(e){
24969             e.preventDefault(); // what does this do?
24970         });
24971
24972         if(!this.disable.font && !Roo.isSafari){
24973             /* why no safari for fonts
24974             editor.fontSelect = tb.el.createChild({
24975                 tag:'select',
24976                 tabIndex: -1,
24977                 cls:'x-font-select',
24978                 html: editor.createFontOptions()
24979             });
24980             editor.fontSelect.on('change', function(){
24981                 var font = editor.fontSelect.dom.value;
24982                 editor.relayCmd('fontname', font);
24983                 editor.deferFocus();
24984             }, editor);
24985             tb.add(
24986                 editor.fontSelect.dom,
24987                 '-'
24988             );
24989             */
24990         };
24991         if(!this.disable.formats){
24992             this.formatCombo = new Roo.form.ComboBox({
24993                 store: new Roo.data.SimpleStore({
24994                     id : 'tag',
24995                     fields: ['tag'],
24996                     data : this.formats // from states.js
24997                 }),
24998                 blockFocus : true,
24999                 //autoCreate : {tag: "div",  size: "20"},
25000                 displayField:'tag',
25001                 typeAhead: false,
25002                 mode: 'local',
25003                 editable : false,
25004                 triggerAction: 'all',
25005                 emptyText:'Add tag',
25006                 selectOnFocus:true,
25007                 width:135,
25008                 listeners : {
25009                     'select': function(c, r, i) {
25010                         editor.insertTag(r.get('tag'));
25011                         editor.focus();
25012                     }
25013                 }
25014
25015             });
25016             tb.addField(this.formatCombo);
25017             
25018         }
25019         
25020         if(!this.disable.format){
25021             tb.add(
25022                 btn('bold'),
25023                 btn('italic'),
25024                 btn('underline')
25025             );
25026         };
25027         if(!this.disable.fontSize){
25028             tb.add(
25029                 '-',
25030                 
25031                 
25032                 btn('increasefontsize', false, editor.adjustFont),
25033                 btn('decreasefontsize', false, editor.adjustFont)
25034             );
25035         };
25036         
25037         
25038         if(this.disable.colors){
25039             tb.add(
25040                 '-', {
25041                     id:editor.frameId +'-forecolor',
25042                     cls:'x-btn-icon x-edit-forecolor',
25043                     clickEvent:'mousedown',
25044                     tooltip: this.buttonTips['forecolor'] || undefined,
25045                     tabIndex:-1,
25046                     menu : new Roo.menu.ColorMenu({
25047                         allowReselect: true,
25048                         focus: Roo.emptyFn,
25049                         value:'000000',
25050                         plain:true,
25051                         selectHandler: function(cp, color){
25052                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25053                             editor.deferFocus();
25054                         },
25055                         scope: editor,
25056                         clickEvent:'mousedown'
25057                     })
25058                 }, {
25059                     id:editor.frameId +'backcolor',
25060                     cls:'x-btn-icon x-edit-backcolor',
25061                     clickEvent:'mousedown',
25062                     tooltip: this.buttonTips['backcolor'] || undefined,
25063                     tabIndex:-1,
25064                     menu : new Roo.menu.ColorMenu({
25065                         focus: Roo.emptyFn,
25066                         value:'FFFFFF',
25067                         plain:true,
25068                         allowReselect: true,
25069                         selectHandler: function(cp, color){
25070                             if(Roo.isGecko){
25071                                 editor.execCmd('useCSS', false);
25072                                 editor.execCmd('hilitecolor', color);
25073                                 editor.execCmd('useCSS', true);
25074                                 editor.deferFocus();
25075                             }else{
25076                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25077                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25078                                 editor.deferFocus();
25079                             }
25080                         },
25081                         scope:editor,
25082                         clickEvent:'mousedown'
25083                     })
25084                 }
25085             );
25086         };
25087         // now add all the items...
25088         
25089
25090         if(!this.disable.alignments){
25091             tb.add(
25092                 '-',
25093                 btn('justifyleft'),
25094                 btn('justifycenter'),
25095                 btn('justifyright')
25096             );
25097         };
25098
25099         //if(!Roo.isSafari){
25100             if(!this.disable.links){
25101                 tb.add(
25102                     '-',
25103                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25104                 );
25105             };
25106
25107             if(!this.disable.lists){
25108                 tb.add(
25109                     '-',
25110                     btn('insertorderedlist'),
25111                     btn('insertunorderedlist')
25112                 );
25113             }
25114             if(!this.disable.sourceEdit){
25115                 tb.add(
25116                     '-',
25117                     btn('sourceedit', true, function(btn){
25118                         this.toggleSourceEdit(btn.pressed);
25119                     })
25120                 );
25121             }
25122         //}
25123         
25124         var smenu = { };
25125         // special menu.. - needs to be tidied up..
25126         if (!this.disable.special) {
25127             smenu = {
25128                 text: "&#169;",
25129                 cls: 'x-edit-none',
25130                 menu : {
25131                     items : []
25132                    }
25133             };
25134             for (var i =0; i < this.specialChars.length; i++) {
25135                 smenu.menu.items.push({
25136                     
25137                     html: this.specialChars[i],
25138                     handler: function(a,b) {
25139                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25140                         
25141                     },
25142                     tabIndex:-1
25143                 });
25144             }
25145             
25146             
25147             tb.add(smenu);
25148             
25149             
25150         }
25151         if (this.btns) {
25152             for(var i =0; i< this.btns.length;i++) {
25153                 var b = this.btns[i];
25154                 b.cls =  'x-edit-none';
25155                 b.scope = editor;
25156                 tb.add(b);
25157             }
25158         
25159         }
25160         
25161         
25162         
25163         // disable everything...
25164         
25165         this.tb.items.each(function(item){
25166            if(item.id != editor.frameId+ '-sourceedit'){
25167                 item.disable();
25168             }
25169         });
25170         this.rendered = true;
25171         
25172         // the all the btns;
25173         editor.on('editorevent', this.updateToolbar, this);
25174         // other toolbars need to implement this..
25175         //editor.on('editmodechange', this.updateToolbar, this);
25176     },
25177     
25178     
25179     
25180     /**
25181      * Protected method that will not generally be called directly. It triggers
25182      * a toolbar update by reading the markup state of the current selection in the editor.
25183      */
25184     updateToolbar: function(){
25185
25186         if(!this.editor.activated){
25187             this.editor.onFirstFocus();
25188             return;
25189         }
25190
25191         var btns = this.tb.items.map, 
25192             doc = this.editor.doc,
25193             frameId = this.editor.frameId;
25194
25195         if(!this.disable.font && !Roo.isSafari){
25196             /*
25197             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25198             if(name != this.fontSelect.dom.value){
25199                 this.fontSelect.dom.value = name;
25200             }
25201             */
25202         }
25203         if(!this.disable.format){
25204             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25205             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25206             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25207         }
25208         if(!this.disable.alignments){
25209             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25210             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25211             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25212         }
25213         if(!Roo.isSafari && !this.disable.lists){
25214             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25215             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25216         }
25217         
25218         var ans = this.editor.getAllAncestors();
25219         if (this.formatCombo) {
25220             
25221             
25222             var store = this.formatCombo.store;
25223             this.formatCombo.setValue("");
25224             for (var i =0; i < ans.length;i++) {
25225                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25226                     // select it..
25227                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25228                     break;
25229                 }
25230             }
25231         }
25232         
25233         
25234         
25235         // hides menus... - so this cant be on a menu...
25236         Roo.menu.MenuMgr.hideAll();
25237
25238         //this.editorsyncValue();
25239     },
25240    
25241     
25242     createFontOptions : function(){
25243         var buf = [], fs = this.fontFamilies, ff, lc;
25244         for(var i = 0, len = fs.length; i< len; i++){
25245             ff = fs[i];
25246             lc = ff.toLowerCase();
25247             buf.push(
25248                 '<option value="',lc,'" style="font-family:',ff,';"',
25249                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25250                     ff,
25251                 '</option>'
25252             );
25253         }
25254         return buf.join('');
25255     },
25256     
25257     toggleSourceEdit : function(sourceEditMode){
25258         if(sourceEditMode === undefined){
25259             sourceEditMode = !this.sourceEditMode;
25260         }
25261         this.sourceEditMode = sourceEditMode === true;
25262         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25263         // just toggle the button?
25264         if(btn.pressed !== this.editor.sourceEditMode){
25265             btn.toggle(this.editor.sourceEditMode);
25266             return;
25267         }
25268         
25269         if(this.sourceEditMode){
25270             this.tb.items.each(function(item){
25271                 if(item.cmd != 'sourceedit'){
25272                     item.disable();
25273                 }
25274             });
25275           
25276         }else{
25277             if(this.initialized){
25278                 this.tb.items.each(function(item){
25279                     item.enable();
25280                 });
25281             }
25282             
25283         }
25284         // tell the editor that it's been pressed..
25285         this.editor.toggleSourceEdit(sourceEditMode);
25286        
25287     },
25288      /**
25289      * Object collection of toolbar tooltips for the buttons in the editor. The key
25290      * is the command id associated with that button and the value is a valid QuickTips object.
25291      * For example:
25292 <pre><code>
25293 {
25294     bold : {
25295         title: 'Bold (Ctrl+B)',
25296         text: 'Make the selected text bold.',
25297         cls: 'x-html-editor-tip'
25298     },
25299     italic : {
25300         title: 'Italic (Ctrl+I)',
25301         text: 'Make the selected text italic.',
25302         cls: 'x-html-editor-tip'
25303     },
25304     ...
25305 </code></pre>
25306     * @type Object
25307      */
25308     buttonTips : {
25309         bold : {
25310             title: 'Bold (Ctrl+B)',
25311             text: 'Make the selected text bold.',
25312             cls: 'x-html-editor-tip'
25313         },
25314         italic : {
25315             title: 'Italic (Ctrl+I)',
25316             text: 'Make the selected text italic.',
25317             cls: 'x-html-editor-tip'
25318         },
25319         underline : {
25320             title: 'Underline (Ctrl+U)',
25321             text: 'Underline the selected text.',
25322             cls: 'x-html-editor-tip'
25323         },
25324         increasefontsize : {
25325             title: 'Grow Text',
25326             text: 'Increase the font size.',
25327             cls: 'x-html-editor-tip'
25328         },
25329         decreasefontsize : {
25330             title: 'Shrink Text',
25331             text: 'Decrease the font size.',
25332             cls: 'x-html-editor-tip'
25333         },
25334         backcolor : {
25335             title: 'Text Highlight Color',
25336             text: 'Change the background color of the selected text.',
25337             cls: 'x-html-editor-tip'
25338         },
25339         forecolor : {
25340             title: 'Font Color',
25341             text: 'Change the color of the selected text.',
25342             cls: 'x-html-editor-tip'
25343         },
25344         justifyleft : {
25345             title: 'Align Text Left',
25346             text: 'Align text to the left.',
25347             cls: 'x-html-editor-tip'
25348         },
25349         justifycenter : {
25350             title: 'Center Text',
25351             text: 'Center text in the editor.',
25352             cls: 'x-html-editor-tip'
25353         },
25354         justifyright : {
25355             title: 'Align Text Right',
25356             text: 'Align text to the right.',
25357             cls: 'x-html-editor-tip'
25358         },
25359         insertunorderedlist : {
25360             title: 'Bullet List',
25361             text: 'Start a bulleted list.',
25362             cls: 'x-html-editor-tip'
25363         },
25364         insertorderedlist : {
25365             title: 'Numbered List',
25366             text: 'Start a numbered list.',
25367             cls: 'x-html-editor-tip'
25368         },
25369         createlink : {
25370             title: 'Hyperlink',
25371             text: 'Make the selected text a hyperlink.',
25372             cls: 'x-html-editor-tip'
25373         },
25374         sourceedit : {
25375             title: 'Source Edit',
25376             text: 'Switch to source editing mode.',
25377             cls: 'x-html-editor-tip'
25378         }
25379     },
25380     // private
25381     onDestroy : function(){
25382         if(this.rendered){
25383             
25384             this.tb.items.each(function(item){
25385                 if(item.menu){
25386                     item.menu.removeAll();
25387                     if(item.menu.el){
25388                         item.menu.el.destroy();
25389                     }
25390                 }
25391                 item.destroy();
25392             });
25393              
25394         }
25395     },
25396     onFirstFocus: function() {
25397         this.tb.items.each(function(item){
25398            item.enable();
25399         });
25400     }
25401 });
25402
25403
25404
25405
25406 // <script type="text/javascript">
25407 /*
25408  * Based on
25409  * Ext JS Library 1.1.1
25410  * Copyright(c) 2006-2007, Ext JS, LLC.
25411  *  
25412  
25413  */
25414
25415  
25416 /**
25417  * @class Roo.form.HtmlEditor.ToolbarContext
25418  * Context Toolbar
25419  * 
25420  * Usage:
25421  *
25422  new Roo.form.HtmlEditor({
25423     ....
25424     toolbars : [
25425         new Roo.form.HtmlEditor.ToolbarStandard(),
25426         new Roo.form.HtmlEditor.ToolbarContext()
25427         })
25428     }
25429      
25430  * 
25431  * @config : {Object} disable List of elements to disable.. (not done yet.)
25432  * 
25433  * 
25434  */
25435
25436 Roo.form.HtmlEditor.ToolbarContext = function(config)
25437 {
25438     
25439     Roo.apply(this, config);
25440     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25441     // dont call parent... till later.
25442 }
25443 Roo.form.HtmlEditor.ToolbarContext.types = {
25444     'IMG' : {
25445         width : {
25446             title: "Width",
25447             width: 40
25448         },
25449         height:  {
25450             title: "Height",
25451             width: 40
25452         },
25453         align: {
25454             title: "Align",
25455             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25456             width : 80
25457             
25458         },
25459         border: {
25460             title: "Border",
25461             width: 40
25462         },
25463         alt: {
25464             title: "Alt",
25465             width: 120
25466         },
25467         src : {
25468             title: "Src",
25469             width: 220
25470         }
25471         
25472     },
25473     'A' : {
25474         name : {
25475             title: "Name",
25476             width: 50
25477         },
25478         href:  {
25479             title: "Href",
25480             width: 220
25481         } // border?
25482         
25483     },
25484     'TABLE' : {
25485         rows : {
25486             title: "Rows",
25487             width: 20
25488         },
25489         cols : {
25490             title: "Cols",
25491             width: 20
25492         },
25493         width : {
25494             title: "Width",
25495             width: 40
25496         },
25497         height : {
25498             title: "Height",
25499             width: 40
25500         },
25501         border : {
25502             title: "Border",
25503             width: 20
25504         }
25505     },
25506     'TD' : {
25507         width : {
25508             title: "Width",
25509             width: 40
25510         },
25511         height : {
25512             title: "Height",
25513             width: 40
25514         },   
25515         align: {
25516             title: "Align",
25517             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25518             width: 40
25519         },
25520         valign: {
25521             title: "Valign",
25522             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25523             width: 40
25524         },
25525         colspan: {
25526             title: "Colspan",
25527             width: 20
25528             
25529         }
25530     },
25531     'INPUT' : {
25532         name : {
25533             title: "name",
25534             width: 120
25535         },
25536         value : {
25537             title: "Value",
25538             width: 120
25539         },
25540         width : {
25541             title: "Width",
25542             width: 40
25543         }
25544     },
25545     'LABEL' : {
25546         'for' : {
25547             title: "For",
25548             width: 120
25549         }
25550     },
25551     'TEXTAREA' : {
25552           name : {
25553             title: "name",
25554             width: 120
25555         },
25556         rows : {
25557             title: "Rows",
25558             width: 20
25559         },
25560         cols : {
25561             title: "Cols",
25562             width: 20
25563         }
25564     },
25565     'SELECT' : {
25566         name : {
25567             title: "name",
25568             width: 120
25569         },
25570         selectoptions : {
25571             title: "Options",
25572             width: 200
25573         }
25574     },
25575     'BODY' : {
25576         title : {
25577             title: "title",
25578             width: 120,
25579             disabled : true
25580         }
25581     }
25582 };
25583
25584
25585
25586 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25587     
25588     tb: false,
25589     
25590     rendered: false,
25591     
25592     editor : false,
25593     /**
25594      * @cfg {Object} disable  List of toolbar elements to disable
25595          
25596      */
25597     disable : false,
25598     
25599     
25600     
25601     toolbars : false,
25602     
25603     init : function(editor)
25604     {
25605         this.editor = editor;
25606         
25607         
25608         var fid = editor.frameId;
25609         var etb = this;
25610         function btn(id, toggle, handler){
25611             var xid = fid + '-'+ id ;
25612             return {
25613                 id : xid,
25614                 cmd : id,
25615                 cls : 'x-btn-icon x-edit-'+id,
25616                 enableToggle:toggle !== false,
25617                 scope: editor, // was editor...
25618                 handler:handler||editor.relayBtnCmd,
25619                 clickEvent:'mousedown',
25620                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25621                 tabIndex:-1
25622             };
25623         }
25624         // create a new element.
25625         var wdiv = editor.wrap.createChild({
25626                 tag: 'div'
25627             }, editor.wrap.dom.firstChild.nextSibling, true);
25628         
25629         // can we do this more than once??
25630         
25631          // stop form submits
25632       
25633  
25634         // disable everything...
25635         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25636         this.toolbars = {};
25637            
25638         for (var i in  ty) {
25639           
25640             this.toolbars[i] = this.buildToolbar(ty[i],i);
25641         }
25642         this.tb = this.toolbars.BODY;
25643         this.tb.el.show();
25644         
25645          
25646         this.rendered = true;
25647         
25648         // the all the btns;
25649         editor.on('editorevent', this.updateToolbar, this);
25650         // other toolbars need to implement this..
25651         //editor.on('editmodechange', this.updateToolbar, this);
25652     },
25653     
25654     
25655     
25656     /**
25657      * Protected method that will not generally be called directly. It triggers
25658      * a toolbar update by reading the markup state of the current selection in the editor.
25659      */
25660     updateToolbar: function(){
25661
25662         if(!this.editor.activated){
25663             this.editor.onFirstFocus();
25664             return;
25665         }
25666
25667         
25668         var ans = this.editor.getAllAncestors();
25669         
25670         // pick
25671         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25672         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25673         sel = sel ? sel : this.editor.doc.body;
25674         sel = sel.tagName.length ? sel : this.editor.doc.body;
25675         var tn = sel.tagName.toUpperCase();
25676         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25677         tn = sel.tagName.toUpperCase();
25678         if (this.tb.name  == tn) {
25679             return; // no change
25680         }
25681         this.tb.el.hide();
25682         ///console.log("show: " + tn);
25683         this.tb =  this.toolbars[tn];
25684         this.tb.el.show();
25685         this.tb.fields.each(function(e) {
25686             e.setValue(sel.getAttribute(e.name));
25687         });
25688         this.tb.selectedNode = sel;
25689         
25690         
25691         Roo.menu.MenuMgr.hideAll();
25692
25693         //this.editorsyncValue();
25694     },
25695    
25696        
25697     // private
25698     onDestroy : function(){
25699         if(this.rendered){
25700             
25701             this.tb.items.each(function(item){
25702                 if(item.menu){
25703                     item.menu.removeAll();
25704                     if(item.menu.el){
25705                         item.menu.el.destroy();
25706                     }
25707                 }
25708                 item.destroy();
25709             });
25710              
25711         }
25712     },
25713     onFirstFocus: function() {
25714         // need to do this for all the toolbars..
25715         this.tb.items.each(function(item){
25716            item.enable();
25717         });
25718     },
25719     buildToolbar: function(tlist, nm)
25720     {
25721         var editor = this.editor;
25722          // create a new element.
25723         var wdiv = editor.wrap.createChild({
25724                 tag: 'div'
25725             }, editor.wrap.dom.firstChild.nextSibling, true);
25726         
25727        
25728         var tb = new Roo.Toolbar(wdiv);
25729         tb.add(nm+ ":&nbsp;");
25730         for (var i in tlist) {
25731             var item = tlist[i];
25732             tb.add(item.title + ":&nbsp;");
25733             if (item.opts) {
25734                 // fixme
25735                 
25736               
25737                 tb.addField( new Roo.form.ComboBox({
25738                     store: new Roo.data.SimpleStore({
25739                         id : 'val',
25740                         fields: ['val'],
25741                         data : item.opts // from states.js
25742                     }),
25743                     name : i,
25744                     displayField:'val',
25745                     typeAhead: false,
25746                     mode: 'local',
25747                     editable : false,
25748                     triggerAction: 'all',
25749                     emptyText:'Select',
25750                     selectOnFocus:true,
25751                     width: item.width ? item.width  : 130,
25752                     listeners : {
25753                         'select': function(c, r, i) {
25754                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25755                         }
25756                     }
25757
25758                 }));
25759                 continue;
25760                     
25761                 
25762                 
25763                 
25764                 
25765                 tb.addField( new Roo.form.TextField({
25766                     name: i,
25767                     width: 100,
25768                     //allowBlank:false,
25769                     value: ''
25770                 }));
25771                 continue;
25772             }
25773             tb.addField( new Roo.form.TextField({
25774                 name: i,
25775                 width: item.width,
25776                 //allowBlank:true,
25777                 value: '',
25778                 listeners: {
25779                     'change' : function(f, nv, ov) {
25780                         tb.selectedNode.setAttribute(f.name, nv);
25781                     }
25782                 }
25783             }));
25784              
25785         }
25786         tb.el.on('click', function(e){
25787             e.preventDefault(); // what does this do?
25788         });
25789         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25790         tb.el.hide();
25791         tb.name = nm;
25792         // dont need to disable them... as they will get hidden
25793         return tb;
25794          
25795         
25796     }
25797     
25798     
25799     
25800     
25801 });
25802
25803
25804
25805
25806
25807 /*
25808  * Based on:
25809  * Ext JS Library 1.1.1
25810  * Copyright(c) 2006-2007, Ext JS, LLC.
25811  *
25812  * Originally Released Under LGPL - original licence link has changed is not relivant.
25813  *
25814  * Fork - LGPL
25815  * <script type="text/javascript">
25816  */
25817  
25818 /**
25819  * @class Roo.form.BasicForm
25820  * @extends Roo.util.Observable
25821  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25822  * @constructor
25823  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25824  * @param {Object} config Configuration options
25825  */
25826 Roo.form.BasicForm = function(el, config){
25827     this.allItems = [];
25828     this.childForms = [];
25829     Roo.apply(this, config);
25830     /*
25831      * The Roo.form.Field items in this form.
25832      * @type MixedCollection
25833      */
25834      
25835      
25836     this.items = new Roo.util.MixedCollection(false, function(o){
25837         return o.id || (o.id = Roo.id());
25838     });
25839     this.addEvents({
25840         /**
25841          * @event beforeaction
25842          * Fires before any action is performed. Return false to cancel the action.
25843          * @param {Form} this
25844          * @param {Action} action The action to be performed
25845          */
25846         beforeaction: true,
25847         /**
25848          * @event actionfailed
25849          * Fires when an action fails.
25850          * @param {Form} this
25851          * @param {Action} action The action that failed
25852          */
25853         actionfailed : true,
25854         /**
25855          * @event actioncomplete
25856          * Fires when an action is completed.
25857          * @param {Form} this
25858          * @param {Action} action The action that completed
25859          */
25860         actioncomplete : true
25861     });
25862     if(el){
25863         this.initEl(el);
25864     }
25865     Roo.form.BasicForm.superclass.constructor.call(this);
25866 };
25867
25868 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25869     /**
25870      * @cfg {String} method
25871      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25872      */
25873     /**
25874      * @cfg {DataReader} reader
25875      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25876      * This is optional as there is built-in support for processing JSON.
25877      */
25878     /**
25879      * @cfg {DataReader} errorReader
25880      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25881      * This is completely optional as there is built-in support for processing JSON.
25882      */
25883     /**
25884      * @cfg {String} url
25885      * The URL to use for form actions if one isn't supplied in the action options.
25886      */
25887     /**
25888      * @cfg {Boolean} fileUpload
25889      * Set to true if this form is a file upload.
25890      */
25891     /**
25892      * @cfg {Object} baseParams
25893      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25894      */
25895     /**
25896      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25897      */
25898     timeout: 30,
25899
25900     // private
25901     activeAction : null,
25902
25903     /**
25904      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25905      * or setValues() data instead of when the form was first created.
25906      */
25907     trackResetOnLoad : false,
25908     
25909     
25910     /**
25911      * childForms - used for multi-tab forms
25912      * @type {Array}
25913      */
25914     childForms : false,
25915     
25916     /**
25917      * allItems - full list of fields.
25918      * @type {Array}
25919      */
25920     allItems : false,
25921     
25922     /**
25923      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
25924      * element by passing it or its id or mask the form itself by passing in true.
25925      * @type Mixed
25926      */
25927     waitMsgTarget : undefined,
25928
25929     // private
25930     initEl : function(el){
25931         this.el = Roo.get(el);
25932         this.id = this.el.id || Roo.id();
25933         this.el.on('submit', this.onSubmit, this);
25934         this.el.addClass('x-form');
25935     },
25936
25937     // private
25938     onSubmit : function(e){
25939         e.stopEvent();
25940     },
25941
25942     /**
25943      * Returns true if client-side validation on the form is successful.
25944      * @return Boolean
25945      */
25946     isValid : function(){
25947         var valid = true;
25948         this.items.each(function(f){
25949            if(!f.validate()){
25950                valid = false;
25951            }
25952         });
25953         return valid;
25954     },
25955
25956     /**
25957      * Returns true if any fields in this form have changed since their original load.
25958      * @return Boolean
25959      */
25960     isDirty : function(){
25961         var dirty = false;
25962         this.items.each(function(f){
25963            if(f.isDirty()){
25964                dirty = true;
25965                return false;
25966            }
25967         });
25968         return dirty;
25969     },
25970
25971     /**
25972      * Performs a predefined action (submit or load) or custom actions you define on this form.
25973      * @param {String} actionName The name of the action type
25974      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
25975      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
25976      * accept other config options):
25977      * <pre>
25978 Property          Type             Description
25979 ----------------  ---------------  ----------------------------------------------------------------------------------
25980 url               String           The url for the action (defaults to the form's url)
25981 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25982 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25983 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25984                                    validate the form on the client (defaults to false)
25985      * </pre>
25986      * @return {BasicForm} this
25987      */
25988     doAction : function(action, options){
25989         if(typeof action == 'string'){
25990             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25991         }
25992         if(this.fireEvent('beforeaction', this, action) !== false){
25993             this.beforeAction(action);
25994             action.run.defer(100, action);
25995         }
25996         return this;
25997     },
25998
25999     /**
26000      * Shortcut to do a submit action.
26001      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26002      * @return {BasicForm} this
26003      */
26004     submit : function(options){
26005         this.doAction('submit', options);
26006         return this;
26007     },
26008
26009     /**
26010      * Shortcut to do a load action.
26011      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26012      * @return {BasicForm} this
26013      */
26014     load : function(options){
26015         this.doAction('load', options);
26016         return this;
26017     },
26018
26019     /**
26020      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26021      * @param {Record} record The record to edit
26022      * @return {BasicForm} this
26023      */
26024     updateRecord : function(record){
26025         record.beginEdit();
26026         var fs = record.fields;
26027         fs.each(function(f){
26028             var field = this.findField(f.name);
26029             if(field){
26030                 record.set(f.name, field.getValue());
26031             }
26032         }, this);
26033         record.endEdit();
26034         return this;
26035     },
26036
26037     /**
26038      * Loads an Roo.data.Record into this form.
26039      * @param {Record} record The record to load
26040      * @return {BasicForm} this
26041      */
26042     loadRecord : function(record){
26043         this.setValues(record.data);
26044         return this;
26045     },
26046
26047     // private
26048     beforeAction : function(action){
26049         var o = action.options;
26050         if(o.waitMsg){
26051             if(this.waitMsgTarget === true){
26052                 this.el.mask(o.waitMsg, 'x-mask-loading');
26053             }else if(this.waitMsgTarget){
26054                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26055                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26056             }else{
26057                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26058             }
26059         }
26060     },
26061
26062     // private
26063     afterAction : function(action, success){
26064         this.activeAction = null;
26065         var o = action.options;
26066         if(o.waitMsg){
26067             if(this.waitMsgTarget === true){
26068                 this.el.unmask();
26069             }else if(this.waitMsgTarget){
26070                 this.waitMsgTarget.unmask();
26071             }else{
26072                 Roo.MessageBox.updateProgress(1);
26073                 Roo.MessageBox.hide();
26074             }
26075         }
26076         if(success){
26077             if(o.reset){
26078                 this.reset();
26079             }
26080             Roo.callback(o.success, o.scope, [this, action]);
26081             this.fireEvent('actioncomplete', this, action);
26082         }else{
26083             Roo.callback(o.failure, o.scope, [this, action]);
26084             this.fireEvent('actionfailed', this, action);
26085         }
26086     },
26087
26088     /**
26089      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26090      * @param {String} id The value to search for
26091      * @return Field
26092      */
26093     findField : function(id){
26094         var field = this.items.get(id);
26095         if(!field){
26096             this.items.each(function(f){
26097                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26098                     field = f;
26099                     return false;
26100                 }
26101             });
26102         }
26103         return field || null;
26104     },
26105
26106     /**
26107      * Add a secondary form to this one, 
26108      * Used to provide tabbed forms. One form is primary, with hidden values 
26109      * which mirror the elements from the other forms.
26110      * 
26111      * @param {Roo.form.Form} form to add.
26112      * 
26113      */
26114     addForm : function(form)
26115     {
26116        
26117         if (this.childForms.indexOf(form) > -1) {
26118             // already added..
26119             return;
26120         }
26121         this.childForms.push(form);
26122         var n = '';
26123         Roo.each(form.allItems, function (fe) {
26124             
26125             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26126             if (this.findField(n)) { // already added..
26127                 return;
26128             }
26129             var add = new Roo.form.Hidden({
26130                 name : n
26131             });
26132             add.render(this.el);
26133             
26134             this.add( add );
26135         }, this);
26136         
26137     },
26138     /**
26139      * Mark fields in this form invalid in bulk.
26140      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26141      * @return {BasicForm} this
26142      */
26143     markInvalid : function(errors){
26144         if(errors instanceof Array){
26145             for(var i = 0, len = errors.length; i < len; i++){
26146                 var fieldError = errors[i];
26147                 var f = this.findField(fieldError.id);
26148                 if(f){
26149                     f.markInvalid(fieldError.msg);
26150                 }
26151             }
26152         }else{
26153             var field, id;
26154             for(id in errors){
26155                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26156                     field.markInvalid(errors[id]);
26157                 }
26158             }
26159         }
26160         Roo.each(this.childForms || [], function (f) {
26161             f.markInvalid(errors);
26162         });
26163         
26164         return this;
26165     },
26166
26167     /**
26168      * Set values for fields in this form in bulk.
26169      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26170      * @return {BasicForm} this
26171      */
26172     setValues : function(values){
26173         if(values instanceof Array){ // array of objects
26174             for(var i = 0, len = values.length; i < len; i++){
26175                 var v = values[i];
26176                 var f = this.findField(v.id);
26177                 if(f){
26178                     f.setValue(v.value);
26179                     if(this.trackResetOnLoad){
26180                         f.originalValue = f.getValue();
26181                     }
26182                 }
26183             }
26184         }else{ // object hash
26185             var field, id;
26186             for(id in values){
26187                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26188                     
26189                     if (field.setFromData && 
26190                         field.valueField && 
26191                         field.displayField &&
26192                         // combos' with local stores can 
26193                         // be queried via setValue()
26194                         // to set their value..
26195                         (field.store && !field.store.isLocal)
26196                         ) {
26197                         // it's a combo
26198                         var sd = { };
26199                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26200                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26201                         field.setFromData(sd);
26202                         
26203                     } else {
26204                         field.setValue(values[id]);
26205                     }
26206                     
26207                     
26208                     if(this.trackResetOnLoad){
26209                         field.originalValue = field.getValue();
26210                     }
26211                 }
26212             }
26213         }
26214          
26215         Roo.each(this.childForms || [], function (f) {
26216             f.setValues(values);
26217         });
26218                 
26219         return this;
26220     },
26221
26222     /**
26223      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26224      * they are returned as an array.
26225      * @param {Boolean} asString
26226      * @return {Object}
26227      */
26228     getValues : function(asString){
26229         if (this.childForms) {
26230             // copy values from the child forms
26231             Roo.each(this.childForms, function (f) {
26232                 this.setValues(f.getValues());
26233             }, this);
26234         }
26235         
26236         
26237         
26238         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26239         if(asString === true){
26240             return fs;
26241         }
26242         return Roo.urlDecode(fs);
26243     },
26244     
26245     /**
26246      * Returns the fields in this form as an object with key/value pairs. 
26247      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26248      * @return {Object}
26249      */
26250     getFieldValues : function()
26251     {
26252         if (this.childForms) {
26253             // copy values from the child forms
26254             Roo.each(this.childForms, function (f) {
26255                 this.setValues(f.getValues());
26256             }, this);
26257         }
26258         
26259         var ret = {};
26260         this.items.each(function(f){
26261             if (!f.getName()) {
26262                 return;
26263             }
26264             var v = f.getValue();
26265             if ((typeof(v) == 'object') && f.getRawValue) {
26266                 v = f.getRawValue() ; // dates..
26267             }
26268             ret[f.getName()] = v;
26269         });
26270         
26271         return ret;
26272     },
26273
26274     /**
26275      * Clears all invalid messages in this form.
26276      * @return {BasicForm} this
26277      */
26278     clearInvalid : function(){
26279         this.items.each(function(f){
26280            f.clearInvalid();
26281         });
26282         
26283         Roo.each(this.childForms || [], function (f) {
26284             f.clearInvalid();
26285         });
26286         
26287         
26288         return this;
26289     },
26290
26291     /**
26292      * Resets this form.
26293      * @return {BasicForm} this
26294      */
26295     reset : function(){
26296         this.items.each(function(f){
26297             f.reset();
26298         });
26299         
26300         Roo.each(this.childForms || [], function (f) {
26301             f.reset();
26302         });
26303        
26304         
26305         return this;
26306     },
26307
26308     /**
26309      * Add Roo.form components to this form.
26310      * @param {Field} field1
26311      * @param {Field} field2 (optional)
26312      * @param {Field} etc (optional)
26313      * @return {BasicForm} this
26314      */
26315     add : function(){
26316         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26317         return this;
26318     },
26319
26320
26321     /**
26322      * Removes a field from the items collection (does NOT remove its markup).
26323      * @param {Field} field
26324      * @return {BasicForm} this
26325      */
26326     remove : function(field){
26327         this.items.remove(field);
26328         return this;
26329     },
26330
26331     /**
26332      * Looks at the fields in this form, checks them for an id attribute,
26333      * and calls applyTo on the existing dom element with that id.
26334      * @return {BasicForm} this
26335      */
26336     render : function(){
26337         this.items.each(function(f){
26338             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26339                 f.applyTo(f.id);
26340             }
26341         });
26342         return this;
26343     },
26344
26345     /**
26346      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26347      * @param {Object} values
26348      * @return {BasicForm} this
26349      */
26350     applyToFields : function(o){
26351         this.items.each(function(f){
26352            Roo.apply(f, o);
26353         });
26354         return this;
26355     },
26356
26357     /**
26358      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26359      * @param {Object} values
26360      * @return {BasicForm} this
26361      */
26362     applyIfToFields : function(o){
26363         this.items.each(function(f){
26364            Roo.applyIf(f, o);
26365         });
26366         return this;
26367     }
26368 });
26369
26370 // back compat
26371 Roo.BasicForm = Roo.form.BasicForm;/*
26372  * Based on:
26373  * Ext JS Library 1.1.1
26374  * Copyright(c) 2006-2007, Ext JS, LLC.
26375  *
26376  * Originally Released Under LGPL - original licence link has changed is not relivant.
26377  *
26378  * Fork - LGPL
26379  * <script type="text/javascript">
26380  */
26381
26382 /**
26383  * @class Roo.form.Form
26384  * @extends Roo.form.BasicForm
26385  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26386  * @constructor
26387  * @param {Object} config Configuration options
26388  */
26389 Roo.form.Form = function(config){
26390     var xitems =  [];
26391     if (config.items) {
26392         xitems = config.items;
26393         delete config.items;
26394     }
26395    
26396     
26397     Roo.form.Form.superclass.constructor.call(this, null, config);
26398     this.url = this.url || this.action;
26399     if(!this.root){
26400         this.root = new Roo.form.Layout(Roo.applyIf({
26401             id: Roo.id()
26402         }, config));
26403     }
26404     this.active = this.root;
26405     /**
26406      * Array of all the buttons that have been added to this form via {@link addButton}
26407      * @type Array
26408      */
26409     this.buttons = [];
26410     this.allItems = [];
26411     this.addEvents({
26412         /**
26413          * @event clientvalidation
26414          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26415          * @param {Form} this
26416          * @param {Boolean} valid true if the form has passed client-side validation
26417          */
26418         clientvalidation: true,
26419         /**
26420          * @event rendered
26421          * Fires when the form is rendered
26422          * @param {Roo.form.Form} form
26423          */
26424         rendered : true
26425     });
26426     
26427     if (this.progressUrl) {
26428             // push a hidden field onto the list of fields..
26429             this.items.addxtype( {
26430                     xns: Roo.form, 
26431                     xtype : 'Hidden', 
26432                     name : 'UPLOAD_IDENTIFIER' 
26433             });
26434         }
26435         
26436     
26437     Roo.each(xitems, this.addxtype, this);
26438     
26439     
26440     
26441 };
26442
26443 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26444     /**
26445      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26446      */
26447     /**
26448      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26449      */
26450     /**
26451      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26452      */
26453     buttonAlign:'center',
26454
26455     /**
26456      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26457      */
26458     minButtonWidth:75,
26459
26460     /**
26461      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26462      * This property cascades to child containers if not set.
26463      */
26464     labelAlign:'left',
26465
26466     /**
26467      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26468      * fires a looping event with that state. This is required to bind buttons to the valid
26469      * state using the config value formBind:true on the button.
26470      */
26471     monitorValid : false,
26472
26473     /**
26474      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26475      */
26476     monitorPoll : 200,
26477     
26478     /**
26479      * @cfg {String} progressUrl - Url to return progress data 
26480      */
26481     
26482     progressUrl : false,
26483   
26484     /**
26485      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26486      * fields are added and the column is closed. If no fields are passed the column remains open
26487      * until end() is called.
26488      * @param {Object} config The config to pass to the column
26489      * @param {Field} field1 (optional)
26490      * @param {Field} field2 (optional)
26491      * @param {Field} etc (optional)
26492      * @return Column The column container object
26493      */
26494     column : function(c){
26495         var col = new Roo.form.Column(c);
26496         this.start(col);
26497         if(arguments.length > 1){ // duplicate code required because of Opera
26498             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26499             this.end();
26500         }
26501         return col;
26502     },
26503
26504     /**
26505      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26506      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26507      * until end() is called.
26508      * @param {Object} config The config to pass to the fieldset
26509      * @param {Field} field1 (optional)
26510      * @param {Field} field2 (optional)
26511      * @param {Field} etc (optional)
26512      * @return FieldSet The fieldset container object
26513      */
26514     fieldset : function(c){
26515         var fs = new Roo.form.FieldSet(c);
26516         this.start(fs);
26517         if(arguments.length > 1){ // duplicate code required because of Opera
26518             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26519             this.end();
26520         }
26521         return fs;
26522     },
26523
26524     /**
26525      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26526      * fields are added and the container is closed. If no fields are passed the container remains open
26527      * until end() is called.
26528      * @param {Object} config The config to pass to the Layout
26529      * @param {Field} field1 (optional)
26530      * @param {Field} field2 (optional)
26531      * @param {Field} etc (optional)
26532      * @return Layout The container object
26533      */
26534     container : function(c){
26535         var l = new Roo.form.Layout(c);
26536         this.start(l);
26537         if(arguments.length > 1){ // duplicate code required because of Opera
26538             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26539             this.end();
26540         }
26541         return l;
26542     },
26543
26544     /**
26545      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26546      * @param {Object} container A Roo.form.Layout or subclass of Layout
26547      * @return {Form} this
26548      */
26549     start : function(c){
26550         // cascade label info
26551         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26552         this.active.stack.push(c);
26553         c.ownerCt = this.active;
26554         this.active = c;
26555         return this;
26556     },
26557
26558     /**
26559      * Closes the current open container
26560      * @return {Form} this
26561      */
26562     end : function(){
26563         if(this.active == this.root){
26564             return this;
26565         }
26566         this.active = this.active.ownerCt;
26567         return this;
26568     },
26569
26570     /**
26571      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26572      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26573      * as the label of the field.
26574      * @param {Field} field1
26575      * @param {Field} field2 (optional)
26576      * @param {Field} etc. (optional)
26577      * @return {Form} this
26578      */
26579     add : function(){
26580         this.active.stack.push.apply(this.active.stack, arguments);
26581         this.allItems.push.apply(this.allItems,arguments);
26582         var r = [];
26583         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26584             if(a[i].isFormField){
26585                 r.push(a[i]);
26586             }
26587         }
26588         if(r.length > 0){
26589             Roo.form.Form.superclass.add.apply(this, r);
26590         }
26591         return this;
26592     },
26593     
26594
26595     
26596     
26597     
26598      /**
26599      * Find any element that has been added to a form, using it's ID or name
26600      * This can include framesets, columns etc. along with regular fields..
26601      * @param {String} id - id or name to find.
26602      
26603      * @return {Element} e - or false if nothing found.
26604      */
26605     findbyId : function(id)
26606     {
26607         var ret = false;
26608         if (!id) {
26609             return ret;
26610         }
26611         Ext.each(this.allItems, function(f){
26612             if (f.id == id || f.name == id ){
26613                 ret = f;
26614                 return false;
26615             }
26616         });
26617         return ret;
26618     },
26619
26620     
26621     
26622     /**
26623      * Render this form into the passed container. This should only be called once!
26624      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26625      * @return {Form} this
26626      */
26627     render : function(ct)
26628     {
26629         
26630         
26631         
26632         ct = Roo.get(ct);
26633         var o = this.autoCreate || {
26634             tag: 'form',
26635             method : this.method || 'POST',
26636             id : this.id || Roo.id()
26637         };
26638         this.initEl(ct.createChild(o));
26639
26640         this.root.render(this.el);
26641         
26642        
26643              
26644         this.items.each(function(f){
26645             f.render('x-form-el-'+f.id);
26646         });
26647
26648         if(this.buttons.length > 0){
26649             // tables are required to maintain order and for correct IE layout
26650             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26651                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26652                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26653             }}, null, true);
26654             var tr = tb.getElementsByTagName('tr')[0];
26655             for(var i = 0, len = this.buttons.length; i < len; i++) {
26656                 var b = this.buttons[i];
26657                 var td = document.createElement('td');
26658                 td.className = 'x-form-btn-td';
26659                 b.render(tr.appendChild(td));
26660             }
26661         }
26662         if(this.monitorValid){ // initialize after render
26663             this.startMonitoring();
26664         }
26665         this.fireEvent('rendered', this);
26666         return this;
26667     },
26668
26669     /**
26670      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26671      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26672      * object or a valid Roo.DomHelper element config
26673      * @param {Function} handler The function called when the button is clicked
26674      * @param {Object} scope (optional) The scope of the handler function
26675      * @return {Roo.Button}
26676      */
26677     addButton : function(config, handler, scope){
26678         var bc = {
26679             handler: handler,
26680             scope: scope,
26681             minWidth: this.minButtonWidth,
26682             hideParent:true
26683         };
26684         if(typeof config == "string"){
26685             bc.text = config;
26686         }else{
26687             Roo.apply(bc, config);
26688         }
26689         var btn = new Roo.Button(null, bc);
26690         this.buttons.push(btn);
26691         return btn;
26692     },
26693
26694      /**
26695      * Adds a series of form elements (using the xtype property as the factory method.
26696      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26697      * @param {Object} config 
26698      */
26699     
26700     addxtype : function()
26701     {
26702         var ar = Array.prototype.slice.call(arguments, 0);
26703         var ret = false;
26704         for(var i = 0; i < ar.length; i++) {
26705             if (!ar[i]) {
26706                 continue; // skip -- if this happends something invalid got sent, we 
26707                 // should ignore it, as basically that interface element will not show up
26708                 // and that should be pretty obvious!!
26709             }
26710             
26711             if (Roo.form[ar[i].xtype]) {
26712                 ar[i].form = this;
26713                 var fe = Roo.factory(ar[i], Roo.form);
26714                 if (!ret) {
26715                     ret = fe;
26716                 }
26717                 fe.form = this;
26718                 if (fe.store) {
26719                     fe.store.form = this;
26720                 }
26721                 if (fe.isLayout) {  
26722                          
26723                     this.start(fe);
26724                     this.allItems.push(fe);
26725                     if (fe.items && fe.addxtype) {
26726                         fe.addxtype.apply(fe, fe.items);
26727                         delete fe.items;
26728                     }
26729                      this.end();
26730                     continue;
26731                 }
26732                 
26733                 
26734                  
26735                 this.add(fe);
26736               //  console.log('adding ' + ar[i].xtype);
26737             }
26738             if (ar[i].xtype == 'Button') {  
26739                 //console.log('adding button');
26740                 //console.log(ar[i]);
26741                 this.addButton(ar[i]);
26742                 this.allItems.push(fe);
26743                 continue;
26744             }
26745             
26746             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26747                 alert('end is not supported on xtype any more, use items');
26748             //    this.end();
26749             //    //console.log('adding end');
26750             }
26751             
26752         }
26753         return ret;
26754     },
26755     
26756     /**
26757      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26758      * option "monitorValid"
26759      */
26760     startMonitoring : function(){
26761         if(!this.bound){
26762             this.bound = true;
26763             Roo.TaskMgr.start({
26764                 run : this.bindHandler,
26765                 interval : this.monitorPoll || 200,
26766                 scope: this
26767             });
26768         }
26769     },
26770
26771     /**
26772      * Stops monitoring of the valid state of this form
26773      */
26774     stopMonitoring : function(){
26775         this.bound = false;
26776     },
26777
26778     // private
26779     bindHandler : function(){
26780         if(!this.bound){
26781             return false; // stops binding
26782         }
26783         var valid = true;
26784         this.items.each(function(f){
26785             if(!f.isValid(true)){
26786                 valid = false;
26787                 return false;
26788             }
26789         });
26790         for(var i = 0, len = this.buttons.length; i < len; i++){
26791             var btn = this.buttons[i];
26792             if(btn.formBind === true && btn.disabled === valid){
26793                 btn.setDisabled(!valid);
26794             }
26795         }
26796         this.fireEvent('clientvalidation', this, valid);
26797     }
26798     
26799     
26800     
26801     
26802     
26803     
26804     
26805     
26806 });
26807
26808
26809 // back compat
26810 Roo.Form = Roo.form.Form;
26811 /*
26812  * Based on:
26813  * Ext JS Library 1.1.1
26814  * Copyright(c) 2006-2007, Ext JS, LLC.
26815  *
26816  * Originally Released Under LGPL - original licence link has changed is not relivant.
26817  *
26818  * Fork - LGPL
26819  * <script type="text/javascript">
26820  */
26821  
26822  /**
26823  * @class Roo.form.Action
26824  * Internal Class used to handle form actions
26825  * @constructor
26826  * @param {Roo.form.BasicForm} el The form element or its id
26827  * @param {Object} config Configuration options
26828  */
26829  
26830  
26831 // define the action interface
26832 Roo.form.Action = function(form, options){
26833     this.form = form;
26834     this.options = options || {};
26835 };
26836 /**
26837  * Client Validation Failed
26838  * @const 
26839  */
26840 Roo.form.Action.CLIENT_INVALID = 'client';
26841 /**
26842  * Server Validation Failed
26843  * @const 
26844  */
26845  Roo.form.Action.SERVER_INVALID = 'server';
26846  /**
26847  * Connect to Server Failed
26848  * @const 
26849  */
26850 Roo.form.Action.CONNECT_FAILURE = 'connect';
26851 /**
26852  * Reading Data from Server Failed
26853  * @const 
26854  */
26855 Roo.form.Action.LOAD_FAILURE = 'load';
26856
26857 Roo.form.Action.prototype = {
26858     type : 'default',
26859     failureType : undefined,
26860     response : undefined,
26861     result : undefined,
26862
26863     // interface method
26864     run : function(options){
26865
26866     },
26867
26868     // interface method
26869     success : function(response){
26870
26871     },
26872
26873     // interface method
26874     handleResponse : function(response){
26875
26876     },
26877
26878     // default connection failure
26879     failure : function(response){
26880         this.response = response;
26881         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26882         this.form.afterAction(this, false);
26883     },
26884
26885     processResponse : function(response){
26886         this.response = response;
26887         if(!response.responseText){
26888             return true;
26889         }
26890         this.result = this.handleResponse(response);
26891         return this.result;
26892     },
26893
26894     // utility functions used internally
26895     getUrl : function(appendParams){
26896         var url = this.options.url || this.form.url || this.form.el.dom.action;
26897         if(appendParams){
26898             var p = this.getParams();
26899             if(p){
26900                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26901             }
26902         }
26903         return url;
26904     },
26905
26906     getMethod : function(){
26907         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26908     },
26909
26910     getParams : function(){
26911         var bp = this.form.baseParams;
26912         var p = this.options.params;
26913         if(p){
26914             if(typeof p == "object"){
26915                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26916             }else if(typeof p == 'string' && bp){
26917                 p += '&' + Roo.urlEncode(bp);
26918             }
26919         }else if(bp){
26920             p = Roo.urlEncode(bp);
26921         }
26922         return p;
26923     },
26924
26925     createCallback : function(){
26926         return {
26927             success: this.success,
26928             failure: this.failure,
26929             scope: this,
26930             timeout: (this.form.timeout*1000),
26931             upload: this.form.fileUpload ? this.success : undefined
26932         };
26933     }
26934 };
26935
26936 Roo.form.Action.Submit = function(form, options){
26937     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26938 };
26939
26940 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26941     type : 'submit',
26942
26943     haveProgress : false,
26944     uploadComplete : false,
26945     
26946     // uploadProgress indicator.
26947     uploadProgress : function()
26948     {
26949         if (!this.form.progressUrl) {
26950             return;
26951         }
26952         
26953         if (!this.haveProgress) {
26954             Roo.MessageBox.progress("Uploading", "Uploading");
26955         }
26956         if (this.uploadComplete) {
26957            Roo.MessageBox.hide();
26958            return;
26959         }
26960         
26961         this.haveProgress = true;
26962    
26963         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26964         
26965         var c = new Roo.data.Connection();
26966         c.request({
26967             url : this.form.progressUrl,
26968             params: {
26969                 id : uid
26970             },
26971             method: 'GET',
26972             success : function(data){
26973                //console.log(data);
26974                 if (this.uploadComplete) {
26975                    Roo.MessageBox.hide();
26976                    return;
26977                 }
26978                    
26979                 if (data){
26980                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26981                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26982                     );
26983                 }
26984                 this.uploadProgress.defer(2000,this);
26985             },
26986        
26987             failure: function(data) {
26988                 Roo.log('progress url failed ');
26989                 Roo.log(data);
26990             },
26991             scope : this
26992         });
26993            
26994     },
26995     
26996     
26997     run : function()
26998     {
26999         // run get Values on the form, so it syncs any secondary forms.
27000         this.form.getValues();
27001         
27002         var o = this.options;
27003         var method = this.getMethod();
27004         var isPost = method == 'POST';
27005         if(o.clientValidation === false || this.form.isValid()){
27006             
27007             if (this.form.progressUrl) {
27008                 this.findField('UPLOAD_IDENTIFIER').setValue(
27009                     (new Date() * 1) + '' + Math.random());
27010                     
27011             } 
27012             
27013             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27014                 form:this.form.el.dom,
27015                 url:this.getUrl(!isPost),
27016                 method: method,
27017                 params:isPost ? this.getParams() : null,
27018                 isUpload: this.form.fileUpload
27019             }));
27020             
27021             this.uploadProgress();
27022
27023         }else if (o.clientValidation !== false){ // client validation failed
27024             this.failureType = Roo.form.Action.CLIENT_INVALID;
27025             this.form.afterAction(this, false);
27026         }
27027     },
27028
27029     success : function(response)
27030     {
27031         this.uploadComplete= true;
27032         if (this.haveProgress) {
27033             Roo.MessageBox.hide();
27034         }
27035         
27036         var result = this.processResponse(response);
27037         if(result === true || result.success){
27038             this.form.afterAction(this, true);
27039             return;
27040         }
27041         if(result.errors){
27042             this.form.markInvalid(result.errors);
27043             this.failureType = Roo.form.Action.SERVER_INVALID;
27044         }
27045         this.form.afterAction(this, false);
27046     },
27047     failure : function(response)
27048     {
27049         this.uploadComplete= true;
27050         if (this.haveProgress) {
27051             Roo.MessageBox.hide();
27052         }
27053         
27054         this.response = response;
27055         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27056         this.form.afterAction(this, false);
27057     },
27058     
27059     handleResponse : function(response){
27060         if(this.form.errorReader){
27061             var rs = this.form.errorReader.read(response);
27062             var errors = [];
27063             if(rs.records){
27064                 for(var i = 0, len = rs.records.length; i < len; i++) {
27065                     var r = rs.records[i];
27066                     errors[i] = r.data;
27067                 }
27068             }
27069             if(errors.length < 1){
27070                 errors = null;
27071             }
27072             return {
27073                 success : rs.success,
27074                 errors : errors
27075             };
27076         }
27077         var ret = false;
27078         try {
27079             ret = Roo.decode(response.responseText);
27080         } catch (e) {
27081             ret = {
27082                 success: false,
27083                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27084                 errors : []
27085             };
27086         }
27087         return ret;
27088         
27089     }
27090 });
27091
27092
27093 Roo.form.Action.Load = function(form, options){
27094     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27095     this.reader = this.form.reader;
27096 };
27097
27098 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27099     type : 'load',
27100
27101     run : function(){
27102         Roo.Ajax.request(Roo.apply(
27103                 this.createCallback(), {
27104                     method:this.getMethod(),
27105                     url:this.getUrl(false),
27106                     params:this.getParams()
27107         }));
27108     },
27109
27110     success : function(response){
27111         var result = this.processResponse(response);
27112         if(result === true || !result.success || !result.data){
27113             this.failureType = Roo.form.Action.LOAD_FAILURE;
27114             this.form.afterAction(this, false);
27115             return;
27116         }
27117         this.form.clearInvalid();
27118         this.form.setValues(result.data);
27119         this.form.afterAction(this, true);
27120     },
27121
27122     handleResponse : function(response){
27123         if(this.form.reader){
27124             var rs = this.form.reader.read(response);
27125             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27126             return {
27127                 success : rs.success,
27128                 data : data
27129             };
27130         }
27131         return Roo.decode(response.responseText);
27132     }
27133 });
27134
27135 Roo.form.Action.ACTION_TYPES = {
27136     'load' : Roo.form.Action.Load,
27137     'submit' : Roo.form.Action.Submit
27138 };/*
27139  * Based on:
27140  * Ext JS Library 1.1.1
27141  * Copyright(c) 2006-2007, Ext JS, LLC.
27142  *
27143  * Originally Released Under LGPL - original licence link has changed is not relivant.
27144  *
27145  * Fork - LGPL
27146  * <script type="text/javascript">
27147  */
27148  
27149 /**
27150  * @class Roo.form.Layout
27151  * @extends Roo.Component
27152  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27153  * @constructor
27154  * @param {Object} config Configuration options
27155  */
27156 Roo.form.Layout = function(config){
27157     var xitems = [];
27158     if (config.items) {
27159         xitems = config.items;
27160         delete config.items;
27161     }
27162     Roo.form.Layout.superclass.constructor.call(this, config);
27163     this.stack = [];
27164     Roo.each(xitems, this.addxtype, this);
27165      
27166 };
27167
27168 Roo.extend(Roo.form.Layout, Roo.Component, {
27169     /**
27170      * @cfg {String/Object} autoCreate
27171      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27172      */
27173     /**
27174      * @cfg {String/Object/Function} style
27175      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27176      * a function which returns such a specification.
27177      */
27178     /**
27179      * @cfg {String} labelAlign
27180      * Valid values are "left," "top" and "right" (defaults to "left")
27181      */
27182     /**
27183      * @cfg {Number} labelWidth
27184      * Fixed width in pixels of all field labels (defaults to undefined)
27185      */
27186     /**
27187      * @cfg {Boolean} clear
27188      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27189      */
27190     clear : true,
27191     /**
27192      * @cfg {String} labelSeparator
27193      * The separator to use after field labels (defaults to ':')
27194      */
27195     labelSeparator : ':',
27196     /**
27197      * @cfg {Boolean} hideLabels
27198      * True to suppress the display of field labels in this layout (defaults to false)
27199      */
27200     hideLabels : false,
27201
27202     // private
27203     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27204     
27205     isLayout : true,
27206     
27207     // private
27208     onRender : function(ct, position){
27209         if(this.el){ // from markup
27210             this.el = Roo.get(this.el);
27211         }else {  // generate
27212             var cfg = this.getAutoCreate();
27213             this.el = ct.createChild(cfg, position);
27214         }
27215         if(this.style){
27216             this.el.applyStyles(this.style);
27217         }
27218         if(this.labelAlign){
27219             this.el.addClass('x-form-label-'+this.labelAlign);
27220         }
27221         if(this.hideLabels){
27222             this.labelStyle = "display:none";
27223             this.elementStyle = "padding-left:0;";
27224         }else{
27225             if(typeof this.labelWidth == 'number'){
27226                 this.labelStyle = "width:"+this.labelWidth+"px;";
27227                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27228             }
27229             if(this.labelAlign == 'top'){
27230                 this.labelStyle = "width:auto;";
27231                 this.elementStyle = "padding-left:0;";
27232             }
27233         }
27234         var stack = this.stack;
27235         var slen = stack.length;
27236         if(slen > 0){
27237             if(!this.fieldTpl){
27238                 var t = new Roo.Template(
27239                     '<div class="x-form-item {5}">',
27240                         '<label for="{0}" style="{2}">{1}{4}</label>',
27241                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27242                         '</div>',
27243                     '</div><div class="x-form-clear-left"></div>'
27244                 );
27245                 t.disableFormats = true;
27246                 t.compile();
27247                 Roo.form.Layout.prototype.fieldTpl = t;
27248             }
27249             for(var i = 0; i < slen; i++) {
27250                 if(stack[i].isFormField){
27251                     this.renderField(stack[i]);
27252                 }else{
27253                     this.renderComponent(stack[i]);
27254                 }
27255             }
27256         }
27257         if(this.clear){
27258             this.el.createChild({cls:'x-form-clear'});
27259         }
27260     },
27261
27262     // private
27263     renderField : function(f){
27264         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27265                f.id, //0
27266                f.fieldLabel, //1
27267                f.labelStyle||this.labelStyle||'', //2
27268                this.elementStyle||'', //3
27269                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27270                f.itemCls||this.itemCls||''  //5
27271        ], true).getPrevSibling());
27272     },
27273
27274     // private
27275     renderComponent : function(c){
27276         c.render(c.isLayout ? this.el : this.el.createChild());    
27277     },
27278     /**
27279      * Adds a object form elements (using the xtype property as the factory method.)
27280      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27281      * @param {Object} config 
27282      */
27283     addxtype : function(o)
27284     {
27285         // create the lement.
27286         o.form = this.form;
27287         var fe = Roo.factory(o, Roo.form);
27288         this.form.allItems.push(fe);
27289         this.stack.push(fe);
27290         
27291         if (fe.isFormField) {
27292             this.form.items.add(fe);
27293         }
27294          
27295         return fe;
27296     }
27297 });
27298
27299 /**
27300  * @class Roo.form.Column
27301  * @extends Roo.form.Layout
27302  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27303  * @constructor
27304  * @param {Object} config Configuration options
27305  */
27306 Roo.form.Column = function(config){
27307     Roo.form.Column.superclass.constructor.call(this, config);
27308 };
27309
27310 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27311     /**
27312      * @cfg {Number/String} width
27313      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27314      */
27315     /**
27316      * @cfg {String/Object} autoCreate
27317      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27318      */
27319
27320     // private
27321     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27322
27323     // private
27324     onRender : function(ct, position){
27325         Roo.form.Column.superclass.onRender.call(this, ct, position);
27326         if(this.width){
27327             this.el.setWidth(this.width);
27328         }
27329     }
27330 });
27331
27332
27333 /**
27334  * @class Roo.form.Row
27335  * @extends Roo.form.Layout
27336  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27337  * @constructor
27338  * @param {Object} config Configuration options
27339  */
27340
27341  
27342 Roo.form.Row = function(config){
27343     Roo.form.Row.superclass.constructor.call(this, config);
27344 };
27345  
27346 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27347       /**
27348      * @cfg {Number/String} width
27349      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27350      */
27351     /**
27352      * @cfg {Number/String} height
27353      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27354      */
27355     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27356     
27357     padWidth : 20,
27358     // private
27359     onRender : function(ct, position){
27360         //console.log('row render');
27361         if(!this.rowTpl){
27362             var t = new Roo.Template(
27363                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27364                     '<label for="{0}" style="{2}">{1}{4}</label>',
27365                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27366                     '</div>',
27367                 '</div>'
27368             );
27369             t.disableFormats = true;
27370             t.compile();
27371             Roo.form.Layout.prototype.rowTpl = t;
27372         }
27373         this.fieldTpl = this.rowTpl;
27374         
27375         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27376         var labelWidth = 100;
27377         
27378         if ((this.labelAlign != 'top')) {
27379             if (typeof this.labelWidth == 'number') {
27380                 labelWidth = this.labelWidth
27381             }
27382             this.padWidth =  20 + labelWidth;
27383             
27384         }
27385         
27386         Roo.form.Column.superclass.onRender.call(this, ct, position);
27387         if(this.width){
27388             this.el.setWidth(this.width);
27389         }
27390         if(this.height){
27391             this.el.setHeight(this.height);
27392         }
27393     },
27394     
27395     // private
27396     renderField : function(f){
27397         f.fieldEl = this.fieldTpl.append(this.el, [
27398                f.id, f.fieldLabel,
27399                f.labelStyle||this.labelStyle||'',
27400                this.elementStyle||'',
27401                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27402                f.itemCls||this.itemCls||'',
27403                f.width ? f.width + this.padWidth : 160 + this.padWidth
27404        ],true);
27405     }
27406 });
27407  
27408
27409 /**
27410  * @class Roo.form.FieldSet
27411  * @extends Roo.form.Layout
27412  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27413  * @constructor
27414  * @param {Object} config Configuration options
27415  */
27416 Roo.form.FieldSet = function(config){
27417     Roo.form.FieldSet.superclass.constructor.call(this, config);
27418 };
27419
27420 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27421     /**
27422      * @cfg {String} legend
27423      * The text to display as the legend for the FieldSet (defaults to '')
27424      */
27425     /**
27426      * @cfg {String/Object} autoCreate
27427      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27428      */
27429
27430     // private
27431     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27432
27433     // private
27434     onRender : function(ct, position){
27435         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27436         if(this.legend){
27437             this.setLegend(this.legend);
27438         }
27439     },
27440
27441     // private
27442     setLegend : function(text){
27443         if(this.rendered){
27444             this.el.child('legend').update(text);
27445         }
27446     }
27447 });/*
27448  * Based on:
27449  * Ext JS Library 1.1.1
27450  * Copyright(c) 2006-2007, Ext JS, LLC.
27451  *
27452  * Originally Released Under LGPL - original licence link has changed is not relivant.
27453  *
27454  * Fork - LGPL
27455  * <script type="text/javascript">
27456  */
27457 /**
27458  * @class Roo.form.VTypes
27459  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27460  * @singleton
27461  */
27462 Roo.form.VTypes = function(){
27463     // closure these in so they are only created once.
27464     var alpha = /^[a-zA-Z_]+$/;
27465     var alphanum = /^[a-zA-Z0-9_]+$/;
27466     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27467     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27468
27469     // All these messages and functions are configurable
27470     return {
27471         /**
27472          * The function used to validate email addresses
27473          * @param {String} value The email address
27474          */
27475         'email' : function(v){
27476             return email.test(v);
27477         },
27478         /**
27479          * The error text to display when the email validation function returns false
27480          * @type String
27481          */
27482         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27483         /**
27484          * The keystroke filter mask to be applied on email input
27485          * @type RegExp
27486          */
27487         'emailMask' : /[a-z0-9_\.\-@]/i,
27488
27489         /**
27490          * The function used to validate URLs
27491          * @param {String} value The URL
27492          */
27493         'url' : function(v){
27494             return url.test(v);
27495         },
27496         /**
27497          * The error text to display when the url validation function returns false
27498          * @type String
27499          */
27500         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27501         
27502         /**
27503          * The function used to validate alpha values
27504          * @param {String} value The value
27505          */
27506         'alpha' : function(v){
27507             return alpha.test(v);
27508         },
27509         /**
27510          * The error text to display when the alpha validation function returns false
27511          * @type String
27512          */
27513         'alphaText' : 'This field should only contain letters and _',
27514         /**
27515          * The keystroke filter mask to be applied on alpha input
27516          * @type RegExp
27517          */
27518         'alphaMask' : /[a-z_]/i,
27519
27520         /**
27521          * The function used to validate alphanumeric values
27522          * @param {String} value The value
27523          */
27524         'alphanum' : function(v){
27525             return alphanum.test(v);
27526         },
27527         /**
27528          * The error text to display when the alphanumeric validation function returns false
27529          * @type String
27530          */
27531         'alphanumText' : 'This field should only contain letters, numbers and _',
27532         /**
27533          * The keystroke filter mask to be applied on alphanumeric input
27534          * @type RegExp
27535          */
27536         'alphanumMask' : /[a-z0-9_]/i
27537     };
27538 }();//<script type="text/javascript">
27539
27540 /**
27541  * @class Roo.form.FCKeditor
27542  * @extends Roo.form.TextArea
27543  * Wrapper around the FCKEditor http://www.fckeditor.net
27544  * @constructor
27545  * Creates a new FCKeditor
27546  * @param {Object} config Configuration options
27547  */
27548 Roo.form.FCKeditor = function(config){
27549     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27550     this.addEvents({
27551          /**
27552          * @event editorinit
27553          * Fired when the editor is initialized - you can add extra handlers here..
27554          * @param {FCKeditor} this
27555          * @param {Object} the FCK object.
27556          */
27557         editorinit : true
27558     });
27559     
27560     
27561 };
27562 Roo.form.FCKeditor.editors = { };
27563 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27564 {
27565     //defaultAutoCreate : {
27566     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27567     //},
27568     // private
27569     /**
27570      * @cfg {Object} fck options - see fck manual for details.
27571      */
27572     fckconfig : false,
27573     
27574     /**
27575      * @cfg {Object} fck toolbar set (Basic or Default)
27576      */
27577     toolbarSet : 'Basic',
27578     /**
27579      * @cfg {Object} fck BasePath
27580      */ 
27581     basePath : '/fckeditor/',
27582     
27583     
27584     frame : false,
27585     
27586     value : '',
27587     
27588    
27589     onRender : function(ct, position)
27590     {
27591         if(!this.el){
27592             this.defaultAutoCreate = {
27593                 tag: "textarea",
27594                 style:"width:300px;height:60px;",
27595                 autocomplete: "off"
27596             };
27597         }
27598         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27599         /*
27600         if(this.grow){
27601             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27602             if(this.preventScrollbars){
27603                 this.el.setStyle("overflow", "hidden");
27604             }
27605             this.el.setHeight(this.growMin);
27606         }
27607         */
27608         //console.log('onrender' + this.getId() );
27609         Roo.form.FCKeditor.editors[this.getId()] = this;
27610          
27611
27612         this.replaceTextarea() ;
27613         
27614     },
27615     
27616     getEditor : function() {
27617         return this.fckEditor;
27618     },
27619     /**
27620      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27621      * @param {Mixed} value The value to set
27622      */
27623     
27624     
27625     setValue : function(value)
27626     {
27627         //console.log('setValue: ' + value);
27628         
27629         if(typeof(value) == 'undefined') { // not sure why this is happending...
27630             return;
27631         }
27632         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27633         
27634         //if(!this.el || !this.getEditor()) {
27635         //    this.value = value;
27636             //this.setValue.defer(100,this,[value]);    
27637         //    return;
27638         //} 
27639         
27640         if(!this.getEditor()) {
27641             return;
27642         }
27643         
27644         this.getEditor().SetData(value);
27645         
27646         //
27647
27648     },
27649
27650     /**
27651      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27652      * @return {Mixed} value The field value
27653      */
27654     getValue : function()
27655     {
27656         
27657         if (this.frame && this.frame.dom.style.display == 'none') {
27658             return Roo.form.FCKeditor.superclass.getValue.call(this);
27659         }
27660         
27661         if(!this.el || !this.getEditor()) {
27662            
27663            // this.getValue.defer(100,this); 
27664             return this.value;
27665         }
27666        
27667         
27668         var value=this.getEditor().GetData();
27669         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27670         return Roo.form.FCKeditor.superclass.getValue.call(this);
27671         
27672
27673     },
27674
27675     /**
27676      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27677      * @return {Mixed} value The field value
27678      */
27679     getRawValue : function()
27680     {
27681         if (this.frame && this.frame.dom.style.display == 'none') {
27682             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27683         }
27684         
27685         if(!this.el || !this.getEditor()) {
27686             //this.getRawValue.defer(100,this); 
27687             return this.value;
27688             return;
27689         }
27690         
27691         
27692         
27693         var value=this.getEditor().GetData();
27694         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27695         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27696          
27697     },
27698     
27699     setSize : function(w,h) {
27700         
27701         
27702         
27703         //if (this.frame && this.frame.dom.style.display == 'none') {
27704         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27705         //    return;
27706         //}
27707         //if(!this.el || !this.getEditor()) {
27708         //    this.setSize.defer(100,this, [w,h]); 
27709         //    return;
27710         //}
27711         
27712         
27713         
27714         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27715         
27716         this.frame.dom.setAttribute('width', w);
27717         this.frame.dom.setAttribute('height', h);
27718         this.frame.setSize(w,h);
27719         
27720     },
27721     
27722     toggleSourceEdit : function(value) {
27723         
27724       
27725          
27726         this.el.dom.style.display = value ? '' : 'none';
27727         this.frame.dom.style.display = value ?  'none' : '';
27728         
27729     },
27730     
27731     
27732     focus: function(tag)
27733     {
27734         if (this.frame.dom.style.display == 'none') {
27735             return Roo.form.FCKeditor.superclass.focus.call(this);
27736         }
27737         if(!this.el || !this.getEditor()) {
27738             this.focus.defer(100,this, [tag]); 
27739             return;
27740         }
27741         
27742         
27743         
27744         
27745         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27746         this.getEditor().Focus();
27747         if (tgs.length) {
27748             if (!this.getEditor().Selection.GetSelection()) {
27749                 this.focus.defer(100,this, [tag]); 
27750                 return;
27751             }
27752             
27753             
27754             var r = this.getEditor().EditorDocument.createRange();
27755             r.setStart(tgs[0],0);
27756             r.setEnd(tgs[0],0);
27757             this.getEditor().Selection.GetSelection().removeAllRanges();
27758             this.getEditor().Selection.GetSelection().addRange(r);
27759             this.getEditor().Focus();
27760         }
27761         
27762     },
27763     
27764     
27765     
27766     replaceTextarea : function()
27767     {
27768         if ( document.getElementById( this.getId() + '___Frame' ) )
27769             return ;
27770         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27771         //{
27772             // We must check the elements firstly using the Id and then the name.
27773         var oTextarea = document.getElementById( this.getId() );
27774         
27775         var colElementsByName = document.getElementsByName( this.getId() ) ;
27776          
27777         oTextarea.style.display = 'none' ;
27778
27779         if ( oTextarea.tabIndex ) {            
27780             this.TabIndex = oTextarea.tabIndex ;
27781         }
27782         
27783         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27784         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27785         this.frame = Roo.get(this.getId() + '___Frame')
27786     },
27787     
27788     _getConfigHtml : function()
27789     {
27790         var sConfig = '' ;
27791
27792         for ( var o in this.fckconfig ) {
27793             sConfig += sConfig.length > 0  ? '&amp;' : '';
27794             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27795         }
27796
27797         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27798     },
27799     
27800     
27801     _getIFrameHtml : function()
27802     {
27803         var sFile = 'fckeditor.html' ;
27804         /* no idea what this is about..
27805         try
27806         {
27807             if ( (/fcksource=true/i).test( window.top.location.search ) )
27808                 sFile = 'fckeditor.original.html' ;
27809         }
27810         catch (e) { 
27811         */
27812
27813         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27814         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27815         
27816         
27817         var html = '<iframe id="' + this.getId() +
27818             '___Frame" src="' + sLink +
27819             '" width="' + this.width +
27820             '" height="' + this.height + '"' +
27821             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27822             ' frameborder="0" scrolling="no"></iframe>' ;
27823
27824         return html ;
27825     },
27826     
27827     _insertHtmlBefore : function( html, element )
27828     {
27829         if ( element.insertAdjacentHTML )       {
27830             // IE
27831             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27832         } else { // Gecko
27833             var oRange = document.createRange() ;
27834             oRange.setStartBefore( element ) ;
27835             var oFragment = oRange.createContextualFragment( html );
27836             element.parentNode.insertBefore( oFragment, element ) ;
27837         }
27838     }
27839     
27840     
27841   
27842     
27843     
27844     
27845     
27846
27847 });
27848
27849 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27850
27851 function FCKeditor_OnComplete(editorInstance){
27852     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27853     f.fckEditor = editorInstance;
27854     //console.log("loaded");
27855     f.fireEvent('editorinit', f, editorInstance);
27856
27857   
27858
27859  
27860
27861
27862
27863
27864
27865
27866
27867
27868
27869
27870
27871
27872
27873
27874
27875 //<script type="text/javascript">
27876 /**
27877  * @class Roo.form.GridField
27878  * @extends Roo.form.Field
27879  * Embed a grid (or editable grid into a form)
27880  * STATUS ALPHA
27881  * 
27882  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27883  * it needs 
27884  * xgrid.store = Roo.data.Store
27885  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27886  * xgrid.store.reader = Roo.data.JsonReader 
27887  * 
27888  * 
27889  * @constructor
27890  * Creates a new GridField
27891  * @param {Object} config Configuration options
27892  */
27893 Roo.form.GridField = function(config){
27894     Roo.form.GridField.superclass.constructor.call(this, config);
27895      
27896 };
27897
27898 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27899     /**
27900      * @cfg {Number} width  - used to restrict width of grid..
27901      */
27902     width : 100,
27903     /**
27904      * @cfg {Number} height - used to restrict height of grid..
27905      */
27906     height : 50,
27907      /**
27908      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27909          * 
27910          *}
27911      */
27912     xgrid : false, 
27913     /**
27914      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27915      * {tag: "input", type: "checkbox", autocomplete: "off"})
27916      */
27917    // defaultAutoCreate : { tag: 'div' },
27918     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27919     /**
27920      * @cfg {String} addTitle Text to include for adding a title.
27921      */
27922     addTitle : false,
27923     //
27924     onResize : function(){
27925         Roo.form.Field.superclass.onResize.apply(this, arguments);
27926     },
27927
27928     initEvents : function(){
27929         // Roo.form.Checkbox.superclass.initEvents.call(this);
27930         // has no events...
27931        
27932     },
27933
27934
27935     getResizeEl : function(){
27936         return this.wrap;
27937     },
27938
27939     getPositionEl : function(){
27940         return this.wrap;
27941     },
27942
27943     // private
27944     onRender : function(ct, position){
27945         
27946         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27947         var style = this.style;
27948         delete this.style;
27949         
27950         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27951         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27952         this.viewEl = this.wrap.createChild({ tag: 'div' });
27953         if (style) {
27954             this.viewEl.applyStyles(style);
27955         }
27956         if (this.width) {
27957             this.viewEl.setWidth(this.width);
27958         }
27959         if (this.height) {
27960             this.viewEl.setHeight(this.height);
27961         }
27962         //if(this.inputValue !== undefined){
27963         //this.setValue(this.value);
27964         
27965         
27966         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27967         
27968         
27969         this.grid.render();
27970         this.grid.getDataSource().on('remove', this.refreshValue, this);
27971         this.grid.getDataSource().on('update', this.refreshValue, this);
27972         this.grid.on('afteredit', this.refreshValue, this);
27973  
27974     },
27975      
27976     
27977     /**
27978      * Sets the value of the item. 
27979      * @param {String} either an object  or a string..
27980      */
27981     setValue : function(v){
27982         //this.value = v;
27983         v = v || []; // empty set..
27984         // this does not seem smart - it really only affects memoryproxy grids..
27985         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27986             var ds = this.grid.getDataSource();
27987             // assumes a json reader..
27988             var data = {}
27989             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27990             ds.loadData( data);
27991         }
27992         Roo.form.GridField.superclass.setValue.call(this, v);
27993         this.refreshValue();
27994         // should load data in the grid really....
27995     },
27996     
27997     // private
27998     refreshValue: function() {
27999          var val = [];
28000         this.grid.getDataSource().each(function(r) {
28001             val.push(r.data);
28002         });
28003         this.el.dom.value = Roo.encode(val);
28004     }
28005     
28006      
28007     
28008     
28009 });/*
28010  * Based on:
28011  * Ext JS Library 1.1.1
28012  * Copyright(c) 2006-2007, Ext JS, LLC.
28013  *
28014  * Originally Released Under LGPL - original licence link has changed is not relivant.
28015  *
28016  * Fork - LGPL
28017  * <script type="text/javascript">
28018  */
28019 /**
28020  * @class Roo.form.DisplayField
28021  * @extends Roo.form.Field
28022  * A generic Field to display non-editable data.
28023  * @constructor
28024  * Creates a new Display Field item.
28025  * @param {Object} config Configuration options
28026  */
28027 Roo.form.DisplayField = function(config){
28028     Roo.form.DisplayField.superclass.constructor.call(this, config);
28029     
28030 };
28031
28032 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28033     inputType:      'hidden',
28034     allowBlank:     true,
28035     readOnly:         true,
28036     
28037  
28038     /**
28039      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28040      */
28041     focusClass : undefined,
28042     /**
28043      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28044      */
28045     fieldClass: 'x-form-field',
28046     
28047      /**
28048      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28049      */
28050     valueRenderer: undefined,
28051     
28052     width: 100,
28053     /**
28054      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28055      * {tag: "input", type: "checkbox", autocomplete: "off"})
28056      */
28057      
28058  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28059
28060     onResize : function(){
28061         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28062         
28063     },
28064
28065     initEvents : function(){
28066         // Roo.form.Checkbox.superclass.initEvents.call(this);
28067         // has no events...
28068        
28069     },
28070
28071
28072     getResizeEl : function(){
28073         return this.wrap;
28074     },
28075
28076     getPositionEl : function(){
28077         return this.wrap;
28078     },
28079
28080     // private
28081     onRender : function(ct, position){
28082         
28083         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28084         //if(this.inputValue !== undefined){
28085         this.wrap = this.el.wrap();
28086         
28087         this.viewEl = this.wrap.createChild({ tag: 'div'});
28088         
28089         if (this.bodyStyle) {
28090             this.viewEl.applyStyles(this.bodyStyle);
28091         }
28092         //this.viewEl.setStyle('padding', '2px');
28093         
28094         this.setValue(this.value);
28095         
28096     },
28097 /*
28098     // private
28099     initValue : Roo.emptyFn,
28100
28101   */
28102
28103         // private
28104     onClick : function(){
28105         
28106     },
28107
28108     /**
28109      * Sets the checked state of the checkbox.
28110      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28111      */
28112     setValue : function(v){
28113         this.value = v;
28114         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28115         // this might be called before we have a dom element..
28116         if (!this.viewEl) {
28117             return;
28118         }
28119         this.viewEl.dom.innerHTML = html;
28120         Roo.form.DisplayField.superclass.setValue.call(this, v);
28121
28122     }
28123 });//<script type="text/javasscript">
28124  
28125
28126 /**
28127  * @class Roo.DDView
28128  * A DnD enabled version of Roo.View.
28129  * @param {Element/String} container The Element in which to create the View.
28130  * @param {String} tpl The template string used to create the markup for each element of the View
28131  * @param {Object} config The configuration properties. These include all the config options of
28132  * {@link Roo.View} plus some specific to this class.<br>
28133  * <p>
28134  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28135  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28136  * <p>
28137  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28138 .x-view-drag-insert-above {
28139         border-top:1px dotted #3366cc;
28140 }
28141 .x-view-drag-insert-below {
28142         border-bottom:1px dotted #3366cc;
28143 }
28144 </code></pre>
28145  * 
28146  */
28147  
28148 Roo.DDView = function(container, tpl, config) {
28149     Roo.DDView.superclass.constructor.apply(this, arguments);
28150     this.getEl().setStyle("outline", "0px none");
28151     this.getEl().unselectable();
28152     if (this.dragGroup) {
28153                 this.setDraggable(this.dragGroup.split(","));
28154     }
28155     if (this.dropGroup) {
28156                 this.setDroppable(this.dropGroup.split(","));
28157     }
28158     if (this.deletable) {
28159         this.setDeletable();
28160     }
28161     this.isDirtyFlag = false;
28162         this.addEvents({
28163                 "drop" : true
28164         });
28165 };
28166
28167 Roo.extend(Roo.DDView, Roo.View, {
28168 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28169 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28170 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28171 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28172
28173         isFormField: true,
28174
28175         reset: Roo.emptyFn,
28176         
28177         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28178
28179         validate: function() {
28180                 return true;
28181         },
28182         
28183         destroy: function() {
28184                 this.purgeListeners();
28185                 this.getEl.removeAllListeners();
28186                 this.getEl().remove();
28187                 if (this.dragZone) {
28188                         if (this.dragZone.destroy) {
28189                                 this.dragZone.destroy();
28190                         }
28191                 }
28192                 if (this.dropZone) {
28193                         if (this.dropZone.destroy) {
28194                                 this.dropZone.destroy();
28195                         }
28196                 }
28197         },
28198
28199 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28200         getName: function() {
28201                 return this.name;
28202         },
28203
28204 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28205         setValue: function(v) {
28206                 if (!this.store) {
28207                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28208                 }
28209                 var data = {};
28210                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28211                 this.store.proxy = new Roo.data.MemoryProxy(data);
28212                 this.store.load();
28213         },
28214
28215 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28216         getValue: function() {
28217                 var result = '(';
28218                 this.store.each(function(rec) {
28219                         result += rec.id + ',';
28220                 });
28221                 return result.substr(0, result.length - 1) + ')';
28222         },
28223         
28224         getIds: function() {
28225                 var i = 0, result = new Array(this.store.getCount());
28226                 this.store.each(function(rec) {
28227                         result[i++] = rec.id;
28228                 });
28229                 return result;
28230         },
28231         
28232         isDirty: function() {
28233                 return this.isDirtyFlag;
28234         },
28235
28236 /**
28237  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28238  *      whole Element becomes the target, and this causes the drop gesture to append.
28239  */
28240     getTargetFromEvent : function(e) {
28241                 var target = e.getTarget();
28242                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28243                 target = target.parentNode;
28244                 }
28245                 if (!target) {
28246                         target = this.el.dom.lastChild || this.el.dom;
28247                 }
28248                 return target;
28249     },
28250
28251 /**
28252  *      Create the drag data which consists of an object which has the property "ddel" as
28253  *      the drag proxy element. 
28254  */
28255     getDragData : function(e) {
28256         var target = this.findItemFromChild(e.getTarget());
28257                 if(target) {
28258                         this.handleSelection(e);
28259                         var selNodes = this.getSelectedNodes();
28260             var dragData = {
28261                 source: this,
28262                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28263                 nodes: selNodes,
28264                 records: []
28265                         };
28266                         var selectedIndices = this.getSelectedIndexes();
28267                         for (var i = 0; i < selectedIndices.length; i++) {
28268                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28269                         }
28270                         if (selNodes.length == 1) {
28271                                 dragData.ddel = target.cloneNode(true); // the div element
28272                         } else {
28273                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28274                                 div.className = 'multi-proxy';
28275                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28276                                         div.appendChild(selNodes[i].cloneNode(true));
28277                                 }
28278                                 dragData.ddel = div;
28279                         }
28280             //console.log(dragData)
28281             //console.log(dragData.ddel.innerHTML)
28282                         return dragData;
28283                 }
28284         //console.log('nodragData')
28285                 return false;
28286     },
28287     
28288 /**     Specify to which ddGroup items in this DDView may be dragged. */
28289     setDraggable: function(ddGroup) {
28290         if (ddGroup instanceof Array) {
28291                 Roo.each(ddGroup, this.setDraggable, this);
28292                 return;
28293         }
28294         if (this.dragZone) {
28295                 this.dragZone.addToGroup(ddGroup);
28296         } else {
28297                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28298                                 containerScroll: true,
28299                                 ddGroup: ddGroup 
28300
28301                         });
28302 //                      Draggability implies selection. DragZone's mousedown selects the element.
28303                         if (!this.multiSelect) { this.singleSelect = true; }
28304
28305 //                      Wire the DragZone's handlers up to methods in *this*
28306                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28307                 }
28308     },
28309
28310 /**     Specify from which ddGroup this DDView accepts drops. */
28311     setDroppable: function(ddGroup) {
28312         if (ddGroup instanceof Array) {
28313                 Roo.each(ddGroup, this.setDroppable, this);
28314                 return;
28315         }
28316         if (this.dropZone) {
28317                 this.dropZone.addToGroup(ddGroup);
28318         } else {
28319                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28320                                 containerScroll: true,
28321                                 ddGroup: ddGroup
28322                         });
28323
28324 //                      Wire the DropZone's handlers up to methods in *this*
28325                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28326                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28327                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28328                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28329                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28330                 }
28331     },
28332
28333 /**     Decide whether to drop above or below a View node. */
28334     getDropPoint : function(e, n, dd){
28335         if (n == this.el.dom) { return "above"; }
28336                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28337                 var c = t + (b - t) / 2;
28338                 var y = Roo.lib.Event.getPageY(e);
28339                 if(y <= c) {
28340                         return "above";
28341                 }else{
28342                         return "below";
28343                 }
28344     },
28345
28346     onNodeEnter : function(n, dd, e, data){
28347                 return false;
28348     },
28349     
28350     onNodeOver : function(n, dd, e, data){
28351                 var pt = this.getDropPoint(e, n, dd);
28352                 // set the insert point style on the target node
28353                 var dragElClass = this.dropNotAllowed;
28354                 if (pt) {
28355                         var targetElClass;
28356                         if (pt == "above"){
28357                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28358                                 targetElClass = "x-view-drag-insert-above";
28359                         } else {
28360                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28361                                 targetElClass = "x-view-drag-insert-below";
28362                         }
28363                         if (this.lastInsertClass != targetElClass){
28364                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28365                                 this.lastInsertClass = targetElClass;
28366                         }
28367                 }
28368                 return dragElClass;
28369         },
28370
28371     onNodeOut : function(n, dd, e, data){
28372                 this.removeDropIndicators(n);
28373     },
28374
28375     onNodeDrop : function(n, dd, e, data){
28376         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28377                 return false;
28378         }
28379         var pt = this.getDropPoint(e, n, dd);
28380                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28381                 if (pt == "below") { insertAt++; }
28382                 for (var i = 0; i < data.records.length; i++) {
28383                         var r = data.records[i];
28384                         var dup = this.store.getById(r.id);
28385                         if (dup && (dd != this.dragZone)) {
28386                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28387                         } else {
28388                                 if (data.copy) {
28389                                         this.store.insert(insertAt++, r.copy());
28390                                 } else {
28391                                         data.source.isDirtyFlag = true;
28392                                         r.store.remove(r);
28393                                         this.store.insert(insertAt++, r);
28394                                 }
28395                                 this.isDirtyFlag = true;
28396                         }
28397                 }
28398                 this.dragZone.cachedTarget = null;
28399                 return true;
28400     },
28401
28402     removeDropIndicators : function(n){
28403                 if(n){
28404                         Roo.fly(n).removeClass([
28405                                 "x-view-drag-insert-above",
28406                                 "x-view-drag-insert-below"]);
28407                         this.lastInsertClass = "_noclass";
28408                 }
28409     },
28410
28411 /**
28412  *      Utility method. Add a delete option to the DDView's context menu.
28413  *      @param {String} imageUrl The URL of the "delete" icon image.
28414  */
28415         setDeletable: function(imageUrl) {
28416                 if (!this.singleSelect && !this.multiSelect) {
28417                         this.singleSelect = true;
28418                 }
28419                 var c = this.getContextMenu();
28420                 this.contextMenu.on("itemclick", function(item) {
28421                         switch (item.id) {
28422                                 case "delete":
28423                                         this.remove(this.getSelectedIndexes());
28424                                         break;
28425                         }
28426                 }, this);
28427                 this.contextMenu.add({
28428                         icon: imageUrl,
28429                         id: "delete",
28430                         text: 'Delete'
28431                 });
28432         },
28433         
28434 /**     Return the context menu for this DDView. */
28435         getContextMenu: function() {
28436                 if (!this.contextMenu) {
28437 //                      Create the View's context menu
28438                         this.contextMenu = new Roo.menu.Menu({
28439                                 id: this.id + "-contextmenu"
28440                         });
28441                         this.el.on("contextmenu", this.showContextMenu, this);
28442                 }
28443                 return this.contextMenu;
28444         },
28445         
28446         disableContextMenu: function() {
28447                 if (this.contextMenu) {
28448                         this.el.un("contextmenu", this.showContextMenu, this);
28449                 }
28450         },
28451
28452         showContextMenu: function(e, item) {
28453         item = this.findItemFromChild(e.getTarget());
28454                 if (item) {
28455                         e.stopEvent();
28456                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28457                         this.contextMenu.showAt(e.getXY());
28458             }
28459     },
28460
28461 /**
28462  *      Remove {@link Roo.data.Record}s at the specified indices.
28463  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28464  */
28465     remove: function(selectedIndices) {
28466                 selectedIndices = [].concat(selectedIndices);
28467                 for (var i = 0; i < selectedIndices.length; i++) {
28468                         var rec = this.store.getAt(selectedIndices[i]);
28469                         this.store.remove(rec);
28470                 }
28471     },
28472
28473 /**
28474  *      Double click fires the event, but also, if this is draggable, and there is only one other
28475  *      related DropZone, it transfers the selected node.
28476  */
28477     onDblClick : function(e){
28478         var item = this.findItemFromChild(e.getTarget());
28479         if(item){
28480             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28481                 return false;
28482             }
28483             if (this.dragGroup) {
28484                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28485                     while (targets.indexOf(this.dropZone) > -1) {
28486                             targets.remove(this.dropZone);
28487                                 }
28488                     if (targets.length == 1) {
28489                                         this.dragZone.cachedTarget = null;
28490                         var el = Roo.get(targets[0].getEl());
28491                         var box = el.getBox(true);
28492                         targets[0].onNodeDrop(el.dom, {
28493                                 target: el.dom,
28494                                 xy: [box.x, box.y + box.height - 1]
28495                         }, null, this.getDragData(e));
28496                     }
28497                 }
28498         }
28499     },
28500     
28501     handleSelection: function(e) {
28502                 this.dragZone.cachedTarget = null;
28503         var item = this.findItemFromChild(e.getTarget());
28504         if (!item) {
28505                 this.clearSelections(true);
28506                 return;
28507         }
28508                 if (item && (this.multiSelect || this.singleSelect)){
28509                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28510                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28511                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28512                                 this.unselect(item);
28513                         } else {
28514                                 this.select(item, this.multiSelect && e.ctrlKey);
28515                                 this.lastSelection = item;
28516                         }
28517                 }
28518     },
28519
28520     onItemClick : function(item, index, e){
28521                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28522                         return false;
28523                 }
28524                 return true;
28525     },
28526
28527     unselect : function(nodeInfo, suppressEvent){
28528                 var node = this.getNode(nodeInfo);
28529                 if(node && this.isSelected(node)){
28530                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28531                                 Roo.fly(node).removeClass(this.selectedClass);
28532                                 this.selections.remove(node);
28533                                 if(!suppressEvent){
28534                                         this.fireEvent("selectionchange", this, this.selections);
28535                                 }
28536                         }
28537                 }
28538     }
28539 });
28540 /*
28541  * Based on:
28542  * Ext JS Library 1.1.1
28543  * Copyright(c) 2006-2007, Ext JS, LLC.
28544  *
28545  * Originally Released Under LGPL - original licence link has changed is not relivant.
28546  *
28547  * Fork - LGPL
28548  * <script type="text/javascript">
28549  */
28550  
28551 /**
28552  * @class Roo.LayoutManager
28553  * @extends Roo.util.Observable
28554  * Base class for layout managers.
28555  */
28556 Roo.LayoutManager = function(container, config){
28557     Roo.LayoutManager.superclass.constructor.call(this);
28558     this.el = Roo.get(container);
28559     // ie scrollbar fix
28560     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28561         document.body.scroll = "no";
28562     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28563         this.el.position('relative');
28564     }
28565     this.id = this.el.id;
28566     this.el.addClass("x-layout-container");
28567     /** false to disable window resize monitoring @type Boolean */
28568     this.monitorWindowResize = true;
28569     this.regions = {};
28570     this.addEvents({
28571         /**
28572          * @event layout
28573          * Fires when a layout is performed. 
28574          * @param {Roo.LayoutManager} this
28575          */
28576         "layout" : true,
28577         /**
28578          * @event regionresized
28579          * Fires when the user resizes a region. 
28580          * @param {Roo.LayoutRegion} region The resized region
28581          * @param {Number} newSize The new size (width for east/west, height for north/south)
28582          */
28583         "regionresized" : true,
28584         /**
28585          * @event regioncollapsed
28586          * Fires when a region is collapsed. 
28587          * @param {Roo.LayoutRegion} region The collapsed region
28588          */
28589         "regioncollapsed" : true,
28590         /**
28591          * @event regionexpanded
28592          * Fires when a region is expanded.  
28593          * @param {Roo.LayoutRegion} region The expanded region
28594          */
28595         "regionexpanded" : true
28596     });
28597     this.updating = false;
28598     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28599 };
28600
28601 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28602     /**
28603      * Returns true if this layout is currently being updated
28604      * @return {Boolean}
28605      */
28606     isUpdating : function(){
28607         return this.updating; 
28608     },
28609     
28610     /**
28611      * Suspend the LayoutManager from doing auto-layouts while
28612      * making multiple add or remove calls
28613      */
28614     beginUpdate : function(){
28615         this.updating = true;    
28616     },
28617     
28618     /**
28619      * Restore auto-layouts and optionally disable the manager from performing a layout
28620      * @param {Boolean} noLayout true to disable a layout update 
28621      */
28622     endUpdate : function(noLayout){
28623         this.updating = false;
28624         if(!noLayout){
28625             this.layout();
28626         }    
28627     },
28628     
28629     layout: function(){
28630         
28631     },
28632     
28633     onRegionResized : function(region, newSize){
28634         this.fireEvent("regionresized", region, newSize);
28635         this.layout();
28636     },
28637     
28638     onRegionCollapsed : function(region){
28639         this.fireEvent("regioncollapsed", region);
28640     },
28641     
28642     onRegionExpanded : function(region){
28643         this.fireEvent("regionexpanded", region);
28644     },
28645         
28646     /**
28647      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28648      * performs box-model adjustments.
28649      * @return {Object} The size as an object {width: (the width), height: (the height)}
28650      */
28651     getViewSize : function(){
28652         var size;
28653         if(this.el.dom != document.body){
28654             size = this.el.getSize();
28655         }else{
28656             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28657         }
28658         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28659         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28660         return size;
28661     },
28662     
28663     /**
28664      * Returns the Element this layout is bound to.
28665      * @return {Roo.Element}
28666      */
28667     getEl : function(){
28668         return this.el;
28669     },
28670     
28671     /**
28672      * Returns the specified region.
28673      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28674      * @return {Roo.LayoutRegion}
28675      */
28676     getRegion : function(target){
28677         return this.regions[target.toLowerCase()];
28678     },
28679     
28680     onWindowResize : function(){
28681         if(this.monitorWindowResize){
28682             this.layout();
28683         }
28684     }
28685 });/*
28686  * Based on:
28687  * Ext JS Library 1.1.1
28688  * Copyright(c) 2006-2007, Ext JS, LLC.
28689  *
28690  * Originally Released Under LGPL - original licence link has changed is not relivant.
28691  *
28692  * Fork - LGPL
28693  * <script type="text/javascript">
28694  */
28695 /**
28696  * @class Roo.BorderLayout
28697  * @extends Roo.LayoutManager
28698  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28699  * please see: <br><br>
28700  * <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>
28701  * <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>
28702  * Example:
28703  <pre><code>
28704  var layout = new Roo.BorderLayout(document.body, {
28705     north: {
28706         initialSize: 25,
28707         titlebar: false
28708     },
28709     west: {
28710         split:true,
28711         initialSize: 200,
28712         minSize: 175,
28713         maxSize: 400,
28714         titlebar: true,
28715         collapsible: true
28716     },
28717     east: {
28718         split:true,
28719         initialSize: 202,
28720         minSize: 175,
28721         maxSize: 400,
28722         titlebar: true,
28723         collapsible: true
28724     },
28725     south: {
28726         split:true,
28727         initialSize: 100,
28728         minSize: 100,
28729         maxSize: 200,
28730         titlebar: true,
28731         collapsible: true
28732     },
28733     center: {
28734         titlebar: true,
28735         autoScroll:true,
28736         resizeTabs: true,
28737         minTabWidth: 50,
28738         preferredTabWidth: 150
28739     }
28740 });
28741
28742 // shorthand
28743 var CP = Roo.ContentPanel;
28744
28745 layout.beginUpdate();
28746 layout.add("north", new CP("north", "North"));
28747 layout.add("south", new CP("south", {title: "South", closable: true}));
28748 layout.add("west", new CP("west", {title: "West"}));
28749 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28750 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28751 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28752 layout.getRegion("center").showPanel("center1");
28753 layout.endUpdate();
28754 </code></pre>
28755
28756 <b>The container the layout is rendered into can be either the body element or any other element.
28757 If it is not the body element, the container needs to either be an absolute positioned element,
28758 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28759 the container size if it is not the body element.</b>
28760
28761 * @constructor
28762 * Create a new BorderLayout
28763 * @param {String/HTMLElement/Element} container The container this layout is bound to
28764 * @param {Object} config Configuration options
28765  */
28766 Roo.BorderLayout = function(container, config){
28767     config = config || {};
28768     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28769     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28770     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28771         var target = this.factory.validRegions[i];
28772         if(config[target]){
28773             this.addRegion(target, config[target]);
28774         }
28775     }
28776 };
28777
28778 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28779     /**
28780      * Creates and adds a new region if it doesn't already exist.
28781      * @param {String} target The target region key (north, south, east, west or center).
28782      * @param {Object} config The regions config object
28783      * @return {BorderLayoutRegion} The new region
28784      */
28785     addRegion : function(target, config){
28786         if(!this.regions[target]){
28787             var r = this.factory.create(target, this, config);
28788             this.bindRegion(target, r);
28789         }
28790         return this.regions[target];
28791     },
28792
28793     // private (kinda)
28794     bindRegion : function(name, r){
28795         this.regions[name] = r;
28796         r.on("visibilitychange", this.layout, this);
28797         r.on("paneladded", this.layout, this);
28798         r.on("panelremoved", this.layout, this);
28799         r.on("invalidated", this.layout, this);
28800         r.on("resized", this.onRegionResized, this);
28801         r.on("collapsed", this.onRegionCollapsed, this);
28802         r.on("expanded", this.onRegionExpanded, this);
28803     },
28804
28805     /**
28806      * Performs a layout update.
28807      */
28808     layout : function(){
28809         if(this.updating) return;
28810         var size = this.getViewSize();
28811         var w = size.width;
28812         var h = size.height;
28813         var centerW = w;
28814         var centerH = h;
28815         var centerY = 0;
28816         var centerX = 0;
28817         //var x = 0, y = 0;
28818
28819         var rs = this.regions;
28820         var north = rs["north"];
28821         var south = rs["south"]; 
28822         var west = rs["west"];
28823         var east = rs["east"];
28824         var center = rs["center"];
28825         //if(this.hideOnLayout){ // not supported anymore
28826             //c.el.setStyle("display", "none");
28827         //}
28828         if(north && north.isVisible()){
28829             var b = north.getBox();
28830             var m = north.getMargins();
28831             b.width = w - (m.left+m.right);
28832             b.x = m.left;
28833             b.y = m.top;
28834             centerY = b.height + b.y + m.bottom;
28835             centerH -= centerY;
28836             north.updateBox(this.safeBox(b));
28837         }
28838         if(south && south.isVisible()){
28839             var b = south.getBox();
28840             var m = south.getMargins();
28841             b.width = w - (m.left+m.right);
28842             b.x = m.left;
28843             var totalHeight = (b.height + m.top + m.bottom);
28844             b.y = h - totalHeight + m.top;
28845             centerH -= totalHeight;
28846             south.updateBox(this.safeBox(b));
28847         }
28848         if(west && west.isVisible()){
28849             var b = west.getBox();
28850             var m = west.getMargins();
28851             b.height = centerH - (m.top+m.bottom);
28852             b.x = m.left;
28853             b.y = centerY + m.top;
28854             var totalWidth = (b.width + m.left + m.right);
28855             centerX += totalWidth;
28856             centerW -= totalWidth;
28857             west.updateBox(this.safeBox(b));
28858         }
28859         if(east && east.isVisible()){
28860             var b = east.getBox();
28861             var m = east.getMargins();
28862             b.height = centerH - (m.top+m.bottom);
28863             var totalWidth = (b.width + m.left + m.right);
28864             b.x = w - totalWidth + m.left;
28865             b.y = centerY + m.top;
28866             centerW -= totalWidth;
28867             east.updateBox(this.safeBox(b));
28868         }
28869         if(center){
28870             var m = center.getMargins();
28871             var centerBox = {
28872                 x: centerX + m.left,
28873                 y: centerY + m.top,
28874                 width: centerW - (m.left+m.right),
28875                 height: centerH - (m.top+m.bottom)
28876             };
28877             //if(this.hideOnLayout){
28878                 //center.el.setStyle("display", "block");
28879             //}
28880             center.updateBox(this.safeBox(centerBox));
28881         }
28882         this.el.repaint();
28883         this.fireEvent("layout", this);
28884     },
28885
28886     // private
28887     safeBox : function(box){
28888         box.width = Math.max(0, box.width);
28889         box.height = Math.max(0, box.height);
28890         return box;
28891     },
28892
28893     /**
28894      * Adds a ContentPanel (or subclass) to this layout.
28895      * @param {String} target The target region key (north, south, east, west or center).
28896      * @param {Roo.ContentPanel} panel The panel to add
28897      * @return {Roo.ContentPanel} The added panel
28898      */
28899     add : function(target, panel){
28900          
28901         target = target.toLowerCase();
28902         return this.regions[target].add(panel);
28903     },
28904
28905     /**
28906      * Remove a ContentPanel (or subclass) to this layout.
28907      * @param {String} target The target region key (north, south, east, west or center).
28908      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
28909      * @return {Roo.ContentPanel} The removed panel
28910      */
28911     remove : function(target, panel){
28912         target = target.toLowerCase();
28913         return this.regions[target].remove(panel);
28914     },
28915
28916     /**
28917      * Searches all regions for a panel with the specified id
28918      * @param {String} panelId
28919      * @return {Roo.ContentPanel} The panel or null if it wasn't found
28920      */
28921     findPanel : function(panelId){
28922         var rs = this.regions;
28923         for(var target in rs){
28924             if(typeof rs[target] != "function"){
28925                 var p = rs[target].getPanel(panelId);
28926                 if(p){
28927                     return p;
28928                 }
28929             }
28930         }
28931         return null;
28932     },
28933
28934     /**
28935      * Searches all regions for a panel with the specified id and activates (shows) it.
28936      * @param {String/ContentPanel} panelId The panels id or the panel itself
28937      * @return {Roo.ContentPanel} The shown panel or null
28938      */
28939     showPanel : function(panelId) {
28940       var rs = this.regions;
28941       for(var target in rs){
28942          var r = rs[target];
28943          if(typeof r != "function"){
28944             if(r.hasPanel(panelId)){
28945                return r.showPanel(panelId);
28946             }
28947          }
28948       }
28949       return null;
28950    },
28951
28952    /**
28953      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
28954      * @param {Roo.state.Provider} provider (optional) An alternate state provider
28955      */
28956     restoreState : function(provider){
28957         if(!provider){
28958             provider = Roo.state.Manager;
28959         }
28960         var sm = new Roo.LayoutStateManager();
28961         sm.init(this, provider);
28962     },
28963
28964     /**
28965      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
28966      * object should contain properties for each region to add ContentPanels to, and each property's value should be
28967      * a valid ContentPanel config object.  Example:
28968      * <pre><code>
28969 // Create the main layout
28970 var layout = new Roo.BorderLayout('main-ct', {
28971     west: {
28972         split:true,
28973         minSize: 175,
28974         titlebar: true
28975     },
28976     center: {
28977         title:'Components'
28978     }
28979 }, 'main-ct');
28980
28981 // Create and add multiple ContentPanels at once via configs
28982 layout.batchAdd({
28983    west: {
28984        id: 'source-files',
28985        autoCreate:true,
28986        title:'Ext Source Files',
28987        autoScroll:true,
28988        fitToFrame:true
28989    },
28990    center : {
28991        el: cview,
28992        autoScroll:true,
28993        fitToFrame:true,
28994        toolbar: tb,
28995        resizeEl:'cbody'
28996    }
28997 });
28998 </code></pre>
28999      * @param {Object} regions An object containing ContentPanel configs by region name
29000      */
29001     batchAdd : function(regions){
29002         this.beginUpdate();
29003         for(var rname in regions){
29004             var lr = this.regions[rname];
29005             if(lr){
29006                 this.addTypedPanels(lr, regions[rname]);
29007             }
29008         }
29009         this.endUpdate();
29010     },
29011
29012     // private
29013     addTypedPanels : function(lr, ps){
29014         if(typeof ps == 'string'){
29015             lr.add(new Roo.ContentPanel(ps));
29016         }
29017         else if(ps instanceof Array){
29018             for(var i =0, len = ps.length; i < len; i++){
29019                 this.addTypedPanels(lr, ps[i]);
29020             }
29021         }
29022         else if(!ps.events){ // raw config?
29023             var el = ps.el;
29024             delete ps.el; // prevent conflict
29025             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29026         }
29027         else {  // panel object assumed!
29028             lr.add(ps);
29029         }
29030     },
29031     /**
29032      * Adds a xtype elements to the layout.
29033      * <pre><code>
29034
29035 layout.addxtype({
29036        xtype : 'ContentPanel',
29037        region: 'west',
29038        items: [ .... ]
29039    }
29040 );
29041
29042 layout.addxtype({
29043         xtype : 'NestedLayoutPanel',
29044         region: 'west',
29045         layout: {
29046            center: { },
29047            west: { }   
29048         },
29049         items : [ ... list of content panels or nested layout panels.. ]
29050    }
29051 );
29052 </code></pre>
29053      * @param {Object} cfg Xtype definition of item to add.
29054      */
29055     addxtype : function(cfg)
29056     {
29057         // basically accepts a pannel...
29058         // can accept a layout region..!?!?
29059        // console.log('BorderLayout add ' + cfg.xtype)
29060         
29061         if (!cfg.xtype.match(/Panel$/)) {
29062             return false;
29063         }
29064         var ret = false;
29065         var region = cfg.region;
29066         delete cfg.region;
29067         
29068           
29069         var xitems = [];
29070         if (cfg.items) {
29071             xitems = cfg.items;
29072             delete cfg.items;
29073         }
29074         
29075         
29076         switch(cfg.xtype) 
29077         {
29078             case 'ContentPanel':  // ContentPanel (el, cfg)
29079             case 'ScrollPanel':  // ContentPanel (el, cfg)
29080                 if(cfg.autoCreate) {
29081                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29082                 } else {
29083                     var el = this.el.createChild();
29084                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29085                 }
29086                 
29087                 this.add(region, ret);
29088                 break;
29089             
29090             
29091             case 'TreePanel': // our new panel!
29092                 cfg.el = this.el.createChild();
29093                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29094                 this.add(region, ret);
29095                 break;
29096             
29097             case 'NestedLayoutPanel': 
29098                 // create a new Layout (which is  a Border Layout...
29099                 var el = this.el.createChild();
29100                 var clayout = cfg.layout;
29101                 delete cfg.layout;
29102                 clayout.items   = clayout.items  || [];
29103                 // replace this exitems with the clayout ones..
29104                 xitems = clayout.items;
29105                  
29106                 
29107                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29108                     cfg.background = false;
29109                 }
29110                 var layout = new Roo.BorderLayout(el, clayout);
29111                 
29112                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29113                 //console.log('adding nested layout panel '  + cfg.toSource());
29114                 this.add(region, ret);
29115                 
29116                 break;
29117                 
29118             case 'GridPanel': 
29119             
29120                 // needs grid and region
29121                 
29122                 //var el = this.getRegion(region).el.createChild();
29123                 var el = this.el.createChild();
29124                 // create the grid first...
29125                 
29126                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29127                 delete cfg.grid;
29128                 if (region == 'center' && this.active ) {
29129                     cfg.background = false;
29130                 }
29131                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29132                 
29133                 this.add(region, ret);
29134                 if (cfg.background) {
29135                     ret.on('activate', function(gp) {
29136                         if (!gp.grid.rendered) {
29137                             gp.grid.render();
29138                         }
29139                     });
29140                 } else {
29141                     grid.render();
29142                 }
29143                 break;
29144            
29145                
29146                 
29147                 
29148             default: 
29149                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29150                 return;
29151              // GridPanel (grid, cfg)
29152             
29153         }
29154         this.beginUpdate();
29155         // add children..
29156         Roo.each(xitems, function(i)  {
29157             ret.addxtype(i);
29158         });
29159         this.endUpdate();
29160         return ret;
29161         
29162     }
29163 });
29164
29165 /**
29166  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29167  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29168  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29169  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29170  * <pre><code>
29171 // shorthand
29172 var CP = Roo.ContentPanel;
29173
29174 var layout = Roo.BorderLayout.create({
29175     north: {
29176         initialSize: 25,
29177         titlebar: false,
29178         panels: [new CP("north", "North")]
29179     },
29180     west: {
29181         split:true,
29182         initialSize: 200,
29183         minSize: 175,
29184         maxSize: 400,
29185         titlebar: true,
29186         collapsible: true,
29187         panels: [new CP("west", {title: "West"})]
29188     },
29189     east: {
29190         split:true,
29191         initialSize: 202,
29192         minSize: 175,
29193         maxSize: 400,
29194         titlebar: true,
29195         collapsible: true,
29196         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29197     },
29198     south: {
29199         split:true,
29200         initialSize: 100,
29201         minSize: 100,
29202         maxSize: 200,
29203         titlebar: true,
29204         collapsible: true,
29205         panels: [new CP("south", {title: "South", closable: true})]
29206     },
29207     center: {
29208         titlebar: true,
29209         autoScroll:true,
29210         resizeTabs: true,
29211         minTabWidth: 50,
29212         preferredTabWidth: 150,
29213         panels: [
29214             new CP("center1", {title: "Close Me", closable: true}),
29215             new CP("center2", {title: "Center Panel", closable: false})
29216         ]
29217     }
29218 }, document.body);
29219
29220 layout.getRegion("center").showPanel("center1");
29221 </code></pre>
29222  * @param config
29223  * @param targetEl
29224  */
29225 Roo.BorderLayout.create = function(config, targetEl){
29226     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29227     layout.beginUpdate();
29228     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29229     for(var j = 0, jlen = regions.length; j < jlen; j++){
29230         var lr = regions[j];
29231         if(layout.regions[lr] && config[lr].panels){
29232             var r = layout.regions[lr];
29233             var ps = config[lr].panels;
29234             layout.addTypedPanels(r, ps);
29235         }
29236     }
29237     layout.endUpdate();
29238     return layout;
29239 };
29240
29241 // private
29242 Roo.BorderLayout.RegionFactory = {
29243     // private
29244     validRegions : ["north","south","east","west","center"],
29245
29246     // private
29247     create : function(target, mgr, config){
29248         target = target.toLowerCase();
29249         if(config.lightweight || config.basic){
29250             return new Roo.BasicLayoutRegion(mgr, config, target);
29251         }
29252         switch(target){
29253             case "north":
29254                 return new Roo.NorthLayoutRegion(mgr, config);
29255             case "south":
29256                 return new Roo.SouthLayoutRegion(mgr, config);
29257             case "east":
29258                 return new Roo.EastLayoutRegion(mgr, config);
29259             case "west":
29260                 return new Roo.WestLayoutRegion(mgr, config);
29261             case "center":
29262                 return new Roo.CenterLayoutRegion(mgr, config);
29263         }
29264         throw 'Layout region "'+target+'" not supported.';
29265     }
29266 };/*
29267  * Based on:
29268  * Ext JS Library 1.1.1
29269  * Copyright(c) 2006-2007, Ext JS, LLC.
29270  *
29271  * Originally Released Under LGPL - original licence link has changed is not relivant.
29272  *
29273  * Fork - LGPL
29274  * <script type="text/javascript">
29275  */
29276  
29277 /**
29278  * @class Roo.BasicLayoutRegion
29279  * @extends Roo.util.Observable
29280  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29281  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29282  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29283  */
29284 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29285     this.mgr = mgr;
29286     this.position  = pos;
29287     this.events = {
29288         /**
29289          * @scope Roo.BasicLayoutRegion
29290          */
29291         
29292         /**
29293          * @event beforeremove
29294          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29295          * @param {Roo.LayoutRegion} this
29296          * @param {Roo.ContentPanel} panel The panel
29297          * @param {Object} e The cancel event object
29298          */
29299         "beforeremove" : true,
29300         /**
29301          * @event invalidated
29302          * Fires when the layout for this region is changed.
29303          * @param {Roo.LayoutRegion} this
29304          */
29305         "invalidated" : true,
29306         /**
29307          * @event visibilitychange
29308          * Fires when this region is shown or hidden 
29309          * @param {Roo.LayoutRegion} this
29310          * @param {Boolean} visibility true or false
29311          */
29312         "visibilitychange" : true,
29313         /**
29314          * @event paneladded
29315          * Fires when a panel is added. 
29316          * @param {Roo.LayoutRegion} this
29317          * @param {Roo.ContentPanel} panel The panel
29318          */
29319         "paneladded" : true,
29320         /**
29321          * @event panelremoved
29322          * Fires when a panel is removed. 
29323          * @param {Roo.LayoutRegion} this
29324          * @param {Roo.ContentPanel} panel The panel
29325          */
29326         "panelremoved" : true,
29327         /**
29328          * @event collapsed
29329          * Fires when this region is collapsed.
29330          * @param {Roo.LayoutRegion} this
29331          */
29332         "collapsed" : true,
29333         /**
29334          * @event expanded
29335          * Fires when this region is expanded.
29336          * @param {Roo.LayoutRegion} this
29337          */
29338         "expanded" : true,
29339         /**
29340          * @event slideshow
29341          * Fires when this region is slid into view.
29342          * @param {Roo.LayoutRegion} this
29343          */
29344         "slideshow" : true,
29345         /**
29346          * @event slidehide
29347          * Fires when this region slides out of view. 
29348          * @param {Roo.LayoutRegion} this
29349          */
29350         "slidehide" : true,
29351         /**
29352          * @event panelactivated
29353          * Fires when a panel is activated. 
29354          * @param {Roo.LayoutRegion} this
29355          * @param {Roo.ContentPanel} panel The activated panel
29356          */
29357         "panelactivated" : true,
29358         /**
29359          * @event resized
29360          * Fires when the user resizes this region. 
29361          * @param {Roo.LayoutRegion} this
29362          * @param {Number} newSize The new size (width for east/west, height for north/south)
29363          */
29364         "resized" : true
29365     };
29366     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29367     this.panels = new Roo.util.MixedCollection();
29368     this.panels.getKey = this.getPanelId.createDelegate(this);
29369     this.box = null;
29370     this.activePanel = null;
29371     // ensure listeners are added...
29372     
29373     if (config.listeners || config.events) {
29374         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29375             listeners : config.listeners || {},
29376             events : config.events || {}
29377         });
29378     }
29379     
29380     if(skipConfig !== true){
29381         this.applyConfig(config);
29382     }
29383 };
29384
29385 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29386     getPanelId : function(p){
29387         return p.getId();
29388     },
29389     
29390     applyConfig : function(config){
29391         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29392         this.config = config;
29393         
29394     },
29395     
29396     /**
29397      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29398      * the width, for horizontal (north, south) the height.
29399      * @param {Number} newSize The new width or height
29400      */
29401     resizeTo : function(newSize){
29402         var el = this.el ? this.el :
29403                  (this.activePanel ? this.activePanel.getEl() : null);
29404         if(el){
29405             switch(this.position){
29406                 case "east":
29407                 case "west":
29408                     el.setWidth(newSize);
29409                     this.fireEvent("resized", this, newSize);
29410                 break;
29411                 case "north":
29412                 case "south":
29413                     el.setHeight(newSize);
29414                     this.fireEvent("resized", this, newSize);
29415                 break;                
29416             }
29417         }
29418     },
29419     
29420     getBox : function(){
29421         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29422     },
29423     
29424     getMargins : function(){
29425         return this.margins;
29426     },
29427     
29428     updateBox : function(box){
29429         this.box = box;
29430         var el = this.activePanel.getEl();
29431         el.dom.style.left = box.x + "px";
29432         el.dom.style.top = box.y + "px";
29433         this.activePanel.setSize(box.width, box.height);
29434     },
29435     
29436     /**
29437      * Returns the container element for this region.
29438      * @return {Roo.Element}
29439      */
29440     getEl : function(){
29441         return this.activePanel;
29442     },
29443     
29444     /**
29445      * Returns true if this region is currently visible.
29446      * @return {Boolean}
29447      */
29448     isVisible : function(){
29449         return this.activePanel ? true : false;
29450     },
29451     
29452     setActivePanel : function(panel){
29453         panel = this.getPanel(panel);
29454         if(this.activePanel && this.activePanel != panel){
29455             this.activePanel.setActiveState(false);
29456             this.activePanel.getEl().setLeftTop(-10000,-10000);
29457         }
29458         this.activePanel = panel;
29459         panel.setActiveState(true);
29460         if(this.box){
29461             panel.setSize(this.box.width, this.box.height);
29462         }
29463         this.fireEvent("panelactivated", this, panel);
29464         this.fireEvent("invalidated");
29465     },
29466     
29467     /**
29468      * Show the specified panel.
29469      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29470      * @return {Roo.ContentPanel} The shown panel or null
29471      */
29472     showPanel : function(panel){
29473         if(panel = this.getPanel(panel)){
29474             this.setActivePanel(panel);
29475         }
29476         return panel;
29477     },
29478     
29479     /**
29480      * Get the active panel for this region.
29481      * @return {Roo.ContentPanel} The active panel or null
29482      */
29483     getActivePanel : function(){
29484         return this.activePanel;
29485     },
29486     
29487     /**
29488      * Add the passed ContentPanel(s)
29489      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29490      * @return {Roo.ContentPanel} The panel added (if only one was added)
29491      */
29492     add : function(panel){
29493         if(arguments.length > 1){
29494             for(var i = 0, len = arguments.length; i < len; i++) {
29495                 this.add(arguments[i]);
29496             }
29497             return null;
29498         }
29499         if(this.hasPanel(panel)){
29500             this.showPanel(panel);
29501             return panel;
29502         }
29503         var el = panel.getEl();
29504         if(el.dom.parentNode != this.mgr.el.dom){
29505             this.mgr.el.dom.appendChild(el.dom);
29506         }
29507         if(panel.setRegion){
29508             panel.setRegion(this);
29509         }
29510         this.panels.add(panel);
29511         el.setStyle("position", "absolute");
29512         if(!panel.background){
29513             this.setActivePanel(panel);
29514             if(this.config.initialSize && this.panels.getCount()==1){
29515                 this.resizeTo(this.config.initialSize);
29516             }
29517         }
29518         this.fireEvent("paneladded", this, panel);
29519         return panel;
29520     },
29521     
29522     /**
29523      * Returns true if the panel is in this region.
29524      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29525      * @return {Boolean}
29526      */
29527     hasPanel : function(panel){
29528         if(typeof panel == "object"){ // must be panel obj
29529             panel = panel.getId();
29530         }
29531         return this.getPanel(panel) ? true : false;
29532     },
29533     
29534     /**
29535      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29536      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29537      * @param {Boolean} preservePanel Overrides the config preservePanel option
29538      * @return {Roo.ContentPanel} The panel that was removed
29539      */
29540     remove : function(panel, preservePanel){
29541         panel = this.getPanel(panel);
29542         if(!panel){
29543             return null;
29544         }
29545         var e = {};
29546         this.fireEvent("beforeremove", this, panel, e);
29547         if(e.cancel === true){
29548             return null;
29549         }
29550         var panelId = panel.getId();
29551         this.panels.removeKey(panelId);
29552         return panel;
29553     },
29554     
29555     /**
29556      * Returns the panel specified or null if it's not in this region.
29557      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29558      * @return {Roo.ContentPanel}
29559      */
29560     getPanel : function(id){
29561         if(typeof id == "object"){ // must be panel obj
29562             return id;
29563         }
29564         return this.panels.get(id);
29565     },
29566     
29567     /**
29568      * Returns this regions position (north/south/east/west/center).
29569      * @return {String} 
29570      */
29571     getPosition: function(){
29572         return this.position;    
29573     }
29574 });/*
29575  * Based on:
29576  * Ext JS Library 1.1.1
29577  * Copyright(c) 2006-2007, Ext JS, LLC.
29578  *
29579  * Originally Released Under LGPL - original licence link has changed is not relivant.
29580  *
29581  * Fork - LGPL
29582  * <script type="text/javascript">
29583  */
29584  
29585 /**
29586  * @class Roo.LayoutRegion
29587  * @extends Roo.BasicLayoutRegion
29588  * This class represents a region in a layout manager.
29589  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29590  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29591  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29592  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29593  * @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})
29594  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29595  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29596  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29597  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29598  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29599  * @cfg {String} title The title for the region (overrides panel titles)
29600  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29601  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29602  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29603  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29604  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29605  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29606  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29607  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29608  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29609  * @cfg {Boolean} showPin True to show a pin button
29610 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29611 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29612 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29613 * @cfg {Number} width  For East/West panels
29614 * @cfg {Number} height For North/South panels
29615 * @cfg {Boolean} split To show the splitter
29616  */
29617 Roo.LayoutRegion = function(mgr, config, pos){
29618     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29619     var dh = Roo.DomHelper;
29620     /** This region's container element 
29621     * @type Roo.Element */
29622     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29623     /** This region's title element 
29624     * @type Roo.Element */
29625
29626     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29627         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29628         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29629     ]}, true);
29630     this.titleEl.enableDisplayMode();
29631     /** This region's title text element 
29632     * @type HTMLElement */
29633     this.titleTextEl = this.titleEl.dom.firstChild;
29634     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29635     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29636     this.closeBtn.enableDisplayMode();
29637     this.closeBtn.on("click", this.closeClicked, this);
29638     this.closeBtn.hide();
29639
29640     this.createBody(config);
29641     this.visible = true;
29642     this.collapsed = false;
29643
29644     if(config.hideWhenEmpty){
29645         this.hide();
29646         this.on("paneladded", this.validateVisibility, this);
29647         this.on("panelremoved", this.validateVisibility, this);
29648     }
29649     this.applyConfig(config);
29650 };
29651
29652 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29653
29654     createBody : function(){
29655         /** This region's body element 
29656         * @type Roo.Element */
29657         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29658     },
29659
29660     applyConfig : function(c){
29661         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29662             var dh = Roo.DomHelper;
29663             if(c.titlebar !== false){
29664                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29665                 this.collapseBtn.on("click", this.collapse, this);
29666                 this.collapseBtn.enableDisplayMode();
29667
29668                 if(c.showPin === true || this.showPin){
29669                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29670                     this.stickBtn.enableDisplayMode();
29671                     this.stickBtn.on("click", this.expand, this);
29672                     this.stickBtn.hide();
29673                 }
29674             }
29675             /** This region's collapsed element
29676             * @type Roo.Element */
29677             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29678                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29679             ]}, true);
29680             if(c.floatable !== false){
29681                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29682                this.collapsedEl.on("click", this.collapseClick, this);
29683             }
29684
29685             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29686                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29687                    id: "message", unselectable: "on", style:{"float":"left"}});
29688                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29689              }
29690             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29691             this.expandBtn.on("click", this.expand, this);
29692         }
29693         if(this.collapseBtn){
29694             this.collapseBtn.setVisible(c.collapsible == true);
29695         }
29696         this.cmargins = c.cmargins || this.cmargins ||
29697                          (this.position == "west" || this.position == "east" ?
29698                              {top: 0, left: 2, right:2, bottom: 0} :
29699                              {top: 2, left: 0, right:0, bottom: 2});
29700         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29701         this.bottomTabs = c.tabPosition != "top";
29702         this.autoScroll = c.autoScroll || false;
29703         if(this.autoScroll){
29704             this.bodyEl.setStyle("overflow", "auto");
29705         }else{
29706             this.bodyEl.setStyle("overflow", "hidden");
29707         }
29708         //if(c.titlebar !== false){
29709             if((!c.titlebar && !c.title) || c.titlebar === false){
29710                 this.titleEl.hide();
29711             }else{
29712                 this.titleEl.show();
29713                 if(c.title){
29714                     this.titleTextEl.innerHTML = c.title;
29715                 }
29716             }
29717         //}
29718         this.duration = c.duration || .30;
29719         this.slideDuration = c.slideDuration || .45;
29720         this.config = c;
29721         if(c.collapsed){
29722             this.collapse(true);
29723         }
29724         if(c.hidden){
29725             this.hide();
29726         }
29727     },
29728     /**
29729      * Returns true if this region is currently visible.
29730      * @return {Boolean}
29731      */
29732     isVisible : function(){
29733         return this.visible;
29734     },
29735
29736     /**
29737      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29738      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29739      */
29740     setCollapsedTitle : function(title){
29741         title = title || "&#160;";
29742         if(this.collapsedTitleTextEl){
29743             this.collapsedTitleTextEl.innerHTML = title;
29744         }
29745     },
29746
29747     getBox : function(){
29748         var b;
29749         if(!this.collapsed){
29750             b = this.el.getBox(false, true);
29751         }else{
29752             b = this.collapsedEl.getBox(false, true);
29753         }
29754         return b;
29755     },
29756
29757     getMargins : function(){
29758         return this.collapsed ? this.cmargins : this.margins;
29759     },
29760
29761     highlight : function(){
29762         this.el.addClass("x-layout-panel-dragover");
29763     },
29764
29765     unhighlight : function(){
29766         this.el.removeClass("x-layout-panel-dragover");
29767     },
29768
29769     updateBox : function(box){
29770         this.box = box;
29771         if(!this.collapsed){
29772             this.el.dom.style.left = box.x + "px";
29773             this.el.dom.style.top = box.y + "px";
29774             this.updateBody(box.width, box.height);
29775         }else{
29776             this.collapsedEl.dom.style.left = box.x + "px";
29777             this.collapsedEl.dom.style.top = box.y + "px";
29778             this.collapsedEl.setSize(box.width, box.height);
29779         }
29780         if(this.tabs){
29781             this.tabs.autoSizeTabs();
29782         }
29783     },
29784
29785     updateBody : function(w, h){
29786         if(w !== null){
29787             this.el.setWidth(w);
29788             w -= this.el.getBorderWidth("rl");
29789             if(this.config.adjustments){
29790                 w += this.config.adjustments[0];
29791             }
29792         }
29793         if(h !== null){
29794             this.el.setHeight(h);
29795             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29796             h -= this.el.getBorderWidth("tb");
29797             if(this.config.adjustments){
29798                 h += this.config.adjustments[1];
29799             }
29800             this.bodyEl.setHeight(h);
29801             if(this.tabs){
29802                 h = this.tabs.syncHeight(h);
29803             }
29804         }
29805         if(this.panelSize){
29806             w = w !== null ? w : this.panelSize.width;
29807             h = h !== null ? h : this.panelSize.height;
29808         }
29809         if(this.activePanel){
29810             var el = this.activePanel.getEl();
29811             w = w !== null ? w : el.getWidth();
29812             h = h !== null ? h : el.getHeight();
29813             this.panelSize = {width: w, height: h};
29814             this.activePanel.setSize(w, h);
29815         }
29816         if(Roo.isIE && this.tabs){
29817             this.tabs.el.repaint();
29818         }
29819     },
29820
29821     /**
29822      * Returns the container element for this region.
29823      * @return {Roo.Element}
29824      */
29825     getEl : function(){
29826         return this.el;
29827     },
29828
29829     /**
29830      * Hides this region.
29831      */
29832     hide : function(){
29833         if(!this.collapsed){
29834             this.el.dom.style.left = "-2000px";
29835             this.el.hide();
29836         }else{
29837             this.collapsedEl.dom.style.left = "-2000px";
29838             this.collapsedEl.hide();
29839         }
29840         this.visible = false;
29841         this.fireEvent("visibilitychange", this, false);
29842     },
29843
29844     /**
29845      * Shows this region if it was previously hidden.
29846      */
29847     show : function(){
29848         if(!this.collapsed){
29849             this.el.show();
29850         }else{
29851             this.collapsedEl.show();
29852         }
29853         this.visible = true;
29854         this.fireEvent("visibilitychange", this, true);
29855     },
29856
29857     closeClicked : function(){
29858         if(this.activePanel){
29859             this.remove(this.activePanel);
29860         }
29861     },
29862
29863     collapseClick : function(e){
29864         if(this.isSlid){
29865            e.stopPropagation();
29866            this.slideIn();
29867         }else{
29868            e.stopPropagation();
29869            this.slideOut();
29870         }
29871     },
29872
29873     /**
29874      * Collapses this region.
29875      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29876      */
29877     collapse : function(skipAnim){
29878         if(this.collapsed) return;
29879         this.collapsed = true;
29880         if(this.split){
29881             this.split.el.hide();
29882         }
29883         if(this.config.animate && skipAnim !== true){
29884             this.fireEvent("invalidated", this);
29885             this.animateCollapse();
29886         }else{
29887             this.el.setLocation(-20000,-20000);
29888             this.el.hide();
29889             this.collapsedEl.show();
29890             this.fireEvent("collapsed", this);
29891             this.fireEvent("invalidated", this);
29892         }
29893     },
29894
29895     animateCollapse : function(){
29896         // overridden
29897     },
29898
29899     /**
29900      * Expands this region if it was previously collapsed.
29901      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29902      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29903      */
29904     expand : function(e, skipAnim){
29905         if(e) e.stopPropagation();
29906         if(!this.collapsed || this.el.hasActiveFx()) return;
29907         if(this.isSlid){
29908             this.afterSlideIn();
29909             skipAnim = true;
29910         }
29911         this.collapsed = false;
29912         if(this.config.animate && skipAnim !== true){
29913             this.animateExpand();
29914         }else{
29915             this.el.show();
29916             if(this.split){
29917                 this.split.el.show();
29918             }
29919             this.collapsedEl.setLocation(-2000,-2000);
29920             this.collapsedEl.hide();
29921             this.fireEvent("invalidated", this);
29922             this.fireEvent("expanded", this);
29923         }
29924     },
29925
29926     animateExpand : function(){
29927         // overridden
29928     },
29929
29930     initTabs : function(){
29931         this.bodyEl.setStyle("overflow", "hidden");
29932         var ts = new Roo.TabPanel(this.bodyEl.dom, {
29933             tabPosition: this.bottomTabs ? 'bottom' : 'top',
29934             disableTooltips: this.config.disableTabTips
29935         });
29936         if(this.config.hideTabs){
29937             ts.stripWrap.setDisplayed(false);
29938         }
29939         this.tabs = ts;
29940         ts.resizeTabs = this.config.resizeTabs === true;
29941         ts.minTabWidth = this.config.minTabWidth || 40;
29942         ts.maxTabWidth = this.config.maxTabWidth || 250;
29943         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
29944         ts.monitorResize = false;
29945         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
29946         ts.bodyEl.addClass('x-layout-tabs-body');
29947         this.panels.each(this.initPanelAsTab, this);
29948     },
29949
29950     initPanelAsTab : function(panel){
29951         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
29952                     this.config.closeOnTab && panel.isClosable());
29953         if(panel.tabTip !== undefined){
29954             ti.setTooltip(panel.tabTip);
29955         }
29956         ti.on("activate", function(){
29957               this.setActivePanel(panel);
29958         }, this);
29959         if(this.config.closeOnTab){
29960             ti.on("beforeclose", function(t, e){
29961                 e.cancel = true;
29962                 this.remove(panel);
29963             }, this);
29964         }
29965         return ti;
29966     },
29967
29968     updatePanelTitle : function(panel, title){
29969         if(this.activePanel == panel){
29970             this.updateTitle(title);
29971         }
29972         if(this.tabs){
29973             var ti = this.tabs.getTab(panel.getEl().id);
29974             ti.setText(title);
29975             if(panel.tabTip !== undefined){
29976                 ti.setTooltip(panel.tabTip);
29977             }
29978         }
29979     },
29980
29981     updateTitle : function(title){
29982         if(this.titleTextEl && !this.config.title){
29983             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
29984         }
29985     },
29986
29987     setActivePanel : function(panel){
29988         panel = this.getPanel(panel);
29989         if(this.activePanel && this.activePanel != panel){
29990             this.activePanel.setActiveState(false);
29991         }
29992         this.activePanel = panel;
29993         panel.setActiveState(true);
29994         if(this.panelSize){
29995             panel.setSize(this.panelSize.width, this.panelSize.height);
29996         }
29997         if(this.closeBtn){
29998             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
29999         }
30000         this.updateTitle(panel.getTitle());
30001         if(this.tabs){
30002             this.fireEvent("invalidated", this);
30003         }
30004         this.fireEvent("panelactivated", this, panel);
30005     },
30006
30007     /**
30008      * Shows the specified panel.
30009      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30010      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30011      */
30012     showPanel : function(panel){
30013         if(panel = this.getPanel(panel)){
30014             if(this.tabs){
30015                 var tab = this.tabs.getTab(panel.getEl().id);
30016                 if(tab.isHidden()){
30017                     this.tabs.unhideTab(tab.id);
30018                 }
30019                 tab.activate();
30020             }else{
30021                 this.setActivePanel(panel);
30022             }
30023         }
30024         return panel;
30025     },
30026
30027     /**
30028      * Get the active panel for this region.
30029      * @return {Roo.ContentPanel} The active panel or null
30030      */
30031     getActivePanel : function(){
30032         return this.activePanel;
30033     },
30034
30035     validateVisibility : function(){
30036         if(this.panels.getCount() < 1){
30037             this.updateTitle("&#160;");
30038             this.closeBtn.hide();
30039             this.hide();
30040         }else{
30041             if(!this.isVisible()){
30042                 this.show();
30043             }
30044         }
30045     },
30046
30047     /**
30048      * Adds the passed ContentPanel(s) to this region.
30049      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30050      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30051      */
30052     add : function(panel){
30053         if(arguments.length > 1){
30054             for(var i = 0, len = arguments.length; i < len; i++) {
30055                 this.add(arguments[i]);
30056             }
30057             return null;
30058         }
30059         if(this.hasPanel(panel)){
30060             this.showPanel(panel);
30061             return panel;
30062         }
30063         panel.setRegion(this);
30064         this.panels.add(panel);
30065         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30066             this.bodyEl.dom.appendChild(panel.getEl().dom);
30067             if(panel.background !== true){
30068                 this.setActivePanel(panel);
30069             }
30070             this.fireEvent("paneladded", this, panel);
30071             return panel;
30072         }
30073         if(!this.tabs){
30074             this.initTabs();
30075         }else{
30076             this.initPanelAsTab(panel);
30077         }
30078         if(panel.background !== true){
30079             this.tabs.activate(panel.getEl().id);
30080         }
30081         this.fireEvent("paneladded", this, panel);
30082         return panel;
30083     },
30084
30085     /**
30086      * Hides the tab for the specified panel.
30087      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30088      */
30089     hidePanel : function(panel){
30090         if(this.tabs && (panel = this.getPanel(panel))){
30091             this.tabs.hideTab(panel.getEl().id);
30092         }
30093     },
30094
30095     /**
30096      * Unhides the tab for a previously hidden panel.
30097      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30098      */
30099     unhidePanel : function(panel){
30100         if(this.tabs && (panel = this.getPanel(panel))){
30101             this.tabs.unhideTab(panel.getEl().id);
30102         }
30103     },
30104
30105     clearPanels : function(){
30106         while(this.panels.getCount() > 0){
30107              this.remove(this.panels.first());
30108         }
30109     },
30110
30111     /**
30112      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30113      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30114      * @param {Boolean} preservePanel Overrides the config preservePanel option
30115      * @return {Roo.ContentPanel} The panel that was removed
30116      */
30117     remove : function(panel, preservePanel){
30118         panel = this.getPanel(panel);
30119         if(!panel){
30120             return null;
30121         }
30122         var e = {};
30123         this.fireEvent("beforeremove", this, panel, e);
30124         if(e.cancel === true){
30125             return null;
30126         }
30127         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30128         var panelId = panel.getId();
30129         this.panels.removeKey(panelId);
30130         if(preservePanel){
30131             document.body.appendChild(panel.getEl().dom);
30132         }
30133         if(this.tabs){
30134             this.tabs.removeTab(panel.getEl().id);
30135         }else if (!preservePanel){
30136             this.bodyEl.dom.removeChild(panel.getEl().dom);
30137         }
30138         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30139             var p = this.panels.first();
30140             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30141             tempEl.appendChild(p.getEl().dom);
30142             this.bodyEl.update("");
30143             this.bodyEl.dom.appendChild(p.getEl().dom);
30144             tempEl = null;
30145             this.updateTitle(p.getTitle());
30146             this.tabs = null;
30147             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30148             this.setActivePanel(p);
30149         }
30150         panel.setRegion(null);
30151         if(this.activePanel == panel){
30152             this.activePanel = null;
30153         }
30154         if(this.config.autoDestroy !== false && preservePanel !== true){
30155             try{panel.destroy();}catch(e){}
30156         }
30157         this.fireEvent("panelremoved", this, panel);
30158         return panel;
30159     },
30160
30161     /**
30162      * Returns the TabPanel component used by this region
30163      * @return {Roo.TabPanel}
30164      */
30165     getTabs : function(){
30166         return this.tabs;
30167     },
30168
30169     createTool : function(parentEl, className){
30170         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30171             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30172         btn.addClassOnOver("x-layout-tools-button-over");
30173         return btn;
30174     }
30175 });/*
30176  * Based on:
30177  * Ext JS Library 1.1.1
30178  * Copyright(c) 2006-2007, Ext JS, LLC.
30179  *
30180  * Originally Released Under LGPL - original licence link has changed is not relivant.
30181  *
30182  * Fork - LGPL
30183  * <script type="text/javascript">
30184  */
30185  
30186
30187
30188 /**
30189  * @class Roo.SplitLayoutRegion
30190  * @extends Roo.LayoutRegion
30191  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30192  */
30193 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30194     this.cursor = cursor;
30195     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30196 };
30197
30198 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30199     splitTip : "Drag to resize.",
30200     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30201     useSplitTips : false,
30202
30203     applyConfig : function(config){
30204         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30205         if(config.split){
30206             if(!this.split){
30207                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30208                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30209                 /** The SplitBar for this region 
30210                 * @type Roo.SplitBar */
30211                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30212                 this.split.on("moved", this.onSplitMove, this);
30213                 this.split.useShim = config.useShim === true;
30214                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30215                 if(this.useSplitTips){
30216                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30217                 }
30218                 if(config.collapsible){
30219                     this.split.el.on("dblclick", this.collapse,  this);
30220                 }
30221             }
30222             if(typeof config.minSize != "undefined"){
30223                 this.split.minSize = config.minSize;
30224             }
30225             if(typeof config.maxSize != "undefined"){
30226                 this.split.maxSize = config.maxSize;
30227             }
30228             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30229                 this.hideSplitter();
30230             }
30231         }
30232     },
30233
30234     getHMaxSize : function(){
30235          var cmax = this.config.maxSize || 10000;
30236          var center = this.mgr.getRegion("center");
30237          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30238     },
30239
30240     getVMaxSize : function(){
30241          var cmax = this.config.maxSize || 10000;
30242          var center = this.mgr.getRegion("center");
30243          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30244     },
30245
30246     onSplitMove : function(split, newSize){
30247         this.fireEvent("resized", this, newSize);
30248     },
30249     
30250     /** 
30251      * Returns the {@link Roo.SplitBar} for this region.
30252      * @return {Roo.SplitBar}
30253      */
30254     getSplitBar : function(){
30255         return this.split;
30256     },
30257     
30258     hide : function(){
30259         this.hideSplitter();
30260         Roo.SplitLayoutRegion.superclass.hide.call(this);
30261     },
30262
30263     hideSplitter : function(){
30264         if(this.split){
30265             this.split.el.setLocation(-2000,-2000);
30266             this.split.el.hide();
30267         }
30268     },
30269
30270     show : function(){
30271         if(this.split){
30272             this.split.el.show();
30273         }
30274         Roo.SplitLayoutRegion.superclass.show.call(this);
30275     },
30276     
30277     beforeSlide: function(){
30278         if(Roo.isGecko){// firefox overflow auto bug workaround
30279             this.bodyEl.clip();
30280             if(this.tabs) this.tabs.bodyEl.clip();
30281             if(this.activePanel){
30282                 this.activePanel.getEl().clip();
30283                 
30284                 if(this.activePanel.beforeSlide){
30285                     this.activePanel.beforeSlide();
30286                 }
30287             }
30288         }
30289     },
30290     
30291     afterSlide : function(){
30292         if(Roo.isGecko){// firefox overflow auto bug workaround
30293             this.bodyEl.unclip();
30294             if(this.tabs) this.tabs.bodyEl.unclip();
30295             if(this.activePanel){
30296                 this.activePanel.getEl().unclip();
30297                 if(this.activePanel.afterSlide){
30298                     this.activePanel.afterSlide();
30299                 }
30300             }
30301         }
30302     },
30303
30304     initAutoHide : function(){
30305         if(this.autoHide !== false){
30306             if(!this.autoHideHd){
30307                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30308                 this.autoHideHd = {
30309                     "mouseout": function(e){
30310                         if(!e.within(this.el, true)){
30311                             st.delay(500);
30312                         }
30313                     },
30314                     "mouseover" : function(e){
30315                         st.cancel();
30316                     },
30317                     scope : this
30318                 };
30319             }
30320             this.el.on(this.autoHideHd);
30321         }
30322     },
30323
30324     clearAutoHide : function(){
30325         if(this.autoHide !== false){
30326             this.el.un("mouseout", this.autoHideHd.mouseout);
30327             this.el.un("mouseover", this.autoHideHd.mouseover);
30328         }
30329     },
30330
30331     clearMonitor : function(){
30332         Roo.get(document).un("click", this.slideInIf, this);
30333     },
30334
30335     // these names are backwards but not changed for compat
30336     slideOut : function(){
30337         if(this.isSlid || this.el.hasActiveFx()){
30338             return;
30339         }
30340         this.isSlid = true;
30341         if(this.collapseBtn){
30342             this.collapseBtn.hide();
30343         }
30344         this.closeBtnState = this.closeBtn.getStyle('display');
30345         this.closeBtn.hide();
30346         if(this.stickBtn){
30347             this.stickBtn.show();
30348         }
30349         this.el.show();
30350         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30351         this.beforeSlide();
30352         this.el.setStyle("z-index", 10001);
30353         this.el.slideIn(this.getSlideAnchor(), {
30354             callback: function(){
30355                 this.afterSlide();
30356                 this.initAutoHide();
30357                 Roo.get(document).on("click", this.slideInIf, this);
30358                 this.fireEvent("slideshow", this);
30359             },
30360             scope: this,
30361             block: true
30362         });
30363     },
30364
30365     afterSlideIn : function(){
30366         this.clearAutoHide();
30367         this.isSlid = false;
30368         this.clearMonitor();
30369         this.el.setStyle("z-index", "");
30370         if(this.collapseBtn){
30371             this.collapseBtn.show();
30372         }
30373         this.closeBtn.setStyle('display', this.closeBtnState);
30374         if(this.stickBtn){
30375             this.stickBtn.hide();
30376         }
30377         this.fireEvent("slidehide", this);
30378     },
30379
30380     slideIn : function(cb){
30381         if(!this.isSlid || this.el.hasActiveFx()){
30382             Roo.callback(cb);
30383             return;
30384         }
30385         this.isSlid = false;
30386         this.beforeSlide();
30387         this.el.slideOut(this.getSlideAnchor(), {
30388             callback: function(){
30389                 this.el.setLeftTop(-10000, -10000);
30390                 this.afterSlide();
30391                 this.afterSlideIn();
30392                 Roo.callback(cb);
30393             },
30394             scope: this,
30395             block: true
30396         });
30397     },
30398     
30399     slideInIf : function(e){
30400         if(!e.within(this.el)){
30401             this.slideIn();
30402         }
30403     },
30404
30405     animateCollapse : function(){
30406         this.beforeSlide();
30407         this.el.setStyle("z-index", 20000);
30408         var anchor = this.getSlideAnchor();
30409         this.el.slideOut(anchor, {
30410             callback : function(){
30411                 this.el.setStyle("z-index", "");
30412                 this.collapsedEl.slideIn(anchor, {duration:.3});
30413                 this.afterSlide();
30414                 this.el.setLocation(-10000,-10000);
30415                 this.el.hide();
30416                 this.fireEvent("collapsed", this);
30417             },
30418             scope: this,
30419             block: true
30420         });
30421     },
30422
30423     animateExpand : function(){
30424         this.beforeSlide();
30425         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30426         this.el.setStyle("z-index", 20000);
30427         this.collapsedEl.hide({
30428             duration:.1
30429         });
30430         this.el.slideIn(this.getSlideAnchor(), {
30431             callback : function(){
30432                 this.el.setStyle("z-index", "");
30433                 this.afterSlide();
30434                 if(this.split){
30435                     this.split.el.show();
30436                 }
30437                 this.fireEvent("invalidated", this);
30438                 this.fireEvent("expanded", this);
30439             },
30440             scope: this,
30441             block: true
30442         });
30443     },
30444
30445     anchors : {
30446         "west" : "left",
30447         "east" : "right",
30448         "north" : "top",
30449         "south" : "bottom"
30450     },
30451
30452     sanchors : {
30453         "west" : "l",
30454         "east" : "r",
30455         "north" : "t",
30456         "south" : "b"
30457     },
30458
30459     canchors : {
30460         "west" : "tl-tr",
30461         "east" : "tr-tl",
30462         "north" : "tl-bl",
30463         "south" : "bl-tl"
30464     },
30465
30466     getAnchor : function(){
30467         return this.anchors[this.position];
30468     },
30469
30470     getCollapseAnchor : function(){
30471         return this.canchors[this.position];
30472     },
30473
30474     getSlideAnchor : function(){
30475         return this.sanchors[this.position];
30476     },
30477
30478     getAlignAdj : function(){
30479         var cm = this.cmargins;
30480         switch(this.position){
30481             case "west":
30482                 return [0, 0];
30483             break;
30484             case "east":
30485                 return [0, 0];
30486             break;
30487             case "north":
30488                 return [0, 0];
30489             break;
30490             case "south":
30491                 return [0, 0];
30492             break;
30493         }
30494     },
30495
30496     getExpandAdj : function(){
30497         var c = this.collapsedEl, cm = this.cmargins;
30498         switch(this.position){
30499             case "west":
30500                 return [-(cm.right+c.getWidth()+cm.left), 0];
30501             break;
30502             case "east":
30503                 return [cm.right+c.getWidth()+cm.left, 0];
30504             break;
30505             case "north":
30506                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30507             break;
30508             case "south":
30509                 return [0, cm.top+cm.bottom+c.getHeight()];
30510             break;
30511         }
30512     }
30513 });/*
30514  * Based on:
30515  * Ext JS Library 1.1.1
30516  * Copyright(c) 2006-2007, Ext JS, LLC.
30517  *
30518  * Originally Released Under LGPL - original licence link has changed is not relivant.
30519  *
30520  * Fork - LGPL
30521  * <script type="text/javascript">
30522  */
30523 /*
30524  * These classes are private internal classes
30525  */
30526 Roo.CenterLayoutRegion = function(mgr, config){
30527     Roo.LayoutRegion.call(this, mgr, config, "center");
30528     this.visible = true;
30529     this.minWidth = config.minWidth || 20;
30530     this.minHeight = config.minHeight || 20;
30531 };
30532
30533 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30534     hide : function(){
30535         // center panel can't be hidden
30536     },
30537     
30538     show : function(){
30539         // center panel can't be hidden
30540     },
30541     
30542     getMinWidth: function(){
30543         return this.minWidth;
30544     },
30545     
30546     getMinHeight: function(){
30547         return this.minHeight;
30548     }
30549 });
30550
30551
30552 Roo.NorthLayoutRegion = function(mgr, config){
30553     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30554     if(this.split){
30555         this.split.placement = Roo.SplitBar.TOP;
30556         this.split.orientation = Roo.SplitBar.VERTICAL;
30557         this.split.el.addClass("x-layout-split-v");
30558     }
30559     var size = config.initialSize || config.height;
30560     if(typeof size != "undefined"){
30561         this.el.setHeight(size);
30562     }
30563 };
30564 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30565     orientation: Roo.SplitBar.VERTICAL,
30566     getBox : function(){
30567         if(this.collapsed){
30568             return this.collapsedEl.getBox();
30569         }
30570         var box = this.el.getBox();
30571         if(this.split){
30572             box.height += this.split.el.getHeight();
30573         }
30574         return box;
30575     },
30576     
30577     updateBox : function(box){
30578         if(this.split && !this.collapsed){
30579             box.height -= this.split.el.getHeight();
30580             this.split.el.setLeft(box.x);
30581             this.split.el.setTop(box.y+box.height);
30582             this.split.el.setWidth(box.width);
30583         }
30584         if(this.collapsed){
30585             this.updateBody(box.width, null);
30586         }
30587         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30588     }
30589 });
30590
30591 Roo.SouthLayoutRegion = function(mgr, config){
30592     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30593     if(this.split){
30594         this.split.placement = Roo.SplitBar.BOTTOM;
30595         this.split.orientation = Roo.SplitBar.VERTICAL;
30596         this.split.el.addClass("x-layout-split-v");
30597     }
30598     var size = config.initialSize || config.height;
30599     if(typeof size != "undefined"){
30600         this.el.setHeight(size);
30601     }
30602 };
30603 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30604     orientation: Roo.SplitBar.VERTICAL,
30605     getBox : function(){
30606         if(this.collapsed){
30607             return this.collapsedEl.getBox();
30608         }
30609         var box = this.el.getBox();
30610         if(this.split){
30611             var sh = this.split.el.getHeight();
30612             box.height += sh;
30613             box.y -= sh;
30614         }
30615         return box;
30616     },
30617     
30618     updateBox : function(box){
30619         if(this.split && !this.collapsed){
30620             var sh = this.split.el.getHeight();
30621             box.height -= sh;
30622             box.y += sh;
30623             this.split.el.setLeft(box.x);
30624             this.split.el.setTop(box.y-sh);
30625             this.split.el.setWidth(box.width);
30626         }
30627         if(this.collapsed){
30628             this.updateBody(box.width, null);
30629         }
30630         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30631     }
30632 });
30633
30634 Roo.EastLayoutRegion = function(mgr, config){
30635     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30636     if(this.split){
30637         this.split.placement = Roo.SplitBar.RIGHT;
30638         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30639         this.split.el.addClass("x-layout-split-h");
30640     }
30641     var size = config.initialSize || config.width;
30642     if(typeof size != "undefined"){
30643         this.el.setWidth(size);
30644     }
30645 };
30646 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30647     orientation: Roo.SplitBar.HORIZONTAL,
30648     getBox : function(){
30649         if(this.collapsed){
30650             return this.collapsedEl.getBox();
30651         }
30652         var box = this.el.getBox();
30653         if(this.split){
30654             var sw = this.split.el.getWidth();
30655             box.width += sw;
30656             box.x -= sw;
30657         }
30658         return box;
30659     },
30660
30661     updateBox : function(box){
30662         if(this.split && !this.collapsed){
30663             var sw = this.split.el.getWidth();
30664             box.width -= sw;
30665             this.split.el.setLeft(box.x);
30666             this.split.el.setTop(box.y);
30667             this.split.el.setHeight(box.height);
30668             box.x += sw;
30669         }
30670         if(this.collapsed){
30671             this.updateBody(null, box.height);
30672         }
30673         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30674     }
30675 });
30676
30677 Roo.WestLayoutRegion = function(mgr, config){
30678     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30679     if(this.split){
30680         this.split.placement = Roo.SplitBar.LEFT;
30681         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30682         this.split.el.addClass("x-layout-split-h");
30683     }
30684     var size = config.initialSize || config.width;
30685     if(typeof size != "undefined"){
30686         this.el.setWidth(size);
30687     }
30688 };
30689 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30690     orientation: Roo.SplitBar.HORIZONTAL,
30691     getBox : function(){
30692         if(this.collapsed){
30693             return this.collapsedEl.getBox();
30694         }
30695         var box = this.el.getBox();
30696         if(this.split){
30697             box.width += this.split.el.getWidth();
30698         }
30699         return box;
30700     },
30701     
30702     updateBox : function(box){
30703         if(this.split && !this.collapsed){
30704             var sw = this.split.el.getWidth();
30705             box.width -= sw;
30706             this.split.el.setLeft(box.x+box.width);
30707             this.split.el.setTop(box.y);
30708             this.split.el.setHeight(box.height);
30709         }
30710         if(this.collapsed){
30711             this.updateBody(null, box.height);
30712         }
30713         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30714     }
30715 });
30716 /*
30717  * Based on:
30718  * Ext JS Library 1.1.1
30719  * Copyright(c) 2006-2007, Ext JS, LLC.
30720  *
30721  * Originally Released Under LGPL - original licence link has changed is not relivant.
30722  *
30723  * Fork - LGPL
30724  * <script type="text/javascript">
30725  */
30726  
30727  
30728 /*
30729  * Private internal class for reading and applying state
30730  */
30731 Roo.LayoutStateManager = function(layout){
30732      // default empty state
30733      this.state = {
30734         north: {},
30735         south: {},
30736         east: {},
30737         west: {}       
30738     };
30739 };
30740
30741 Roo.LayoutStateManager.prototype = {
30742     init : function(layout, provider){
30743         this.provider = provider;
30744         var state = provider.get(layout.id+"-layout-state");
30745         if(state){
30746             var wasUpdating = layout.isUpdating();
30747             if(!wasUpdating){
30748                 layout.beginUpdate();
30749             }
30750             for(var key in state){
30751                 if(typeof state[key] != "function"){
30752                     var rstate = state[key];
30753                     var r = layout.getRegion(key);
30754                     if(r && rstate){
30755                         if(rstate.size){
30756                             r.resizeTo(rstate.size);
30757                         }
30758                         if(rstate.collapsed == true){
30759                             r.collapse(true);
30760                         }else{
30761                             r.expand(null, true);
30762                         }
30763                     }
30764                 }
30765             }
30766             if(!wasUpdating){
30767                 layout.endUpdate();
30768             }
30769             this.state = state; 
30770         }
30771         this.layout = layout;
30772         layout.on("regionresized", this.onRegionResized, this);
30773         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30774         layout.on("regionexpanded", this.onRegionExpanded, this);
30775     },
30776     
30777     storeState : function(){
30778         this.provider.set(this.layout.id+"-layout-state", this.state);
30779     },
30780     
30781     onRegionResized : function(region, newSize){
30782         this.state[region.getPosition()].size = newSize;
30783         this.storeState();
30784     },
30785     
30786     onRegionCollapsed : function(region){
30787         this.state[region.getPosition()].collapsed = true;
30788         this.storeState();
30789     },
30790     
30791     onRegionExpanded : function(region){
30792         this.state[region.getPosition()].collapsed = false;
30793         this.storeState();
30794     }
30795 };/*
30796  * Based on:
30797  * Ext JS Library 1.1.1
30798  * Copyright(c) 2006-2007, Ext JS, LLC.
30799  *
30800  * Originally Released Under LGPL - original licence link has changed is not relivant.
30801  *
30802  * Fork - LGPL
30803  * <script type="text/javascript">
30804  */
30805 /**
30806  * @class Roo.ContentPanel
30807  * @extends Roo.util.Observable
30808  * A basic ContentPanel element.
30809  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30810  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30811  * @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
30812  * @cfg {Boolean} closable True if the panel can be closed/removed
30813  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30814  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30815  * @cfg {Toolbar} toolbar A toolbar for this panel
30816  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30817  * @cfg {String} title The title for this panel
30818  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30819  * @cfg {String} url Calls {@link #setUrl} with this value
30820  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30821  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30822  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30823  * @constructor
30824  * Create a new ContentPanel.
30825  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30826  * @param {String/Object} config A string to set only the title or a config object
30827  * @param {String} content (optional) Set the HTML content for this panel
30828  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30829  */
30830 Roo.ContentPanel = function(el, config, content){
30831     
30832      
30833     /*
30834     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30835         config = el;
30836         el = Roo.id();
30837     }
30838     if (config && config.parentLayout) { 
30839         el = config.parentLayout.el.createChild(); 
30840     }
30841     */
30842     if(el.autoCreate){ // xtype is available if this is called from factory
30843         config = el;
30844         el = Roo.id();
30845     }
30846     this.el = Roo.get(el);
30847     if(!this.el && config && config.autoCreate){
30848         if(typeof config.autoCreate == "object"){
30849             if(!config.autoCreate.id){
30850                 config.autoCreate.id = config.id||el;
30851             }
30852             this.el = Roo.DomHelper.append(document.body,
30853                         config.autoCreate, true);
30854         }else{
30855             this.el = Roo.DomHelper.append(document.body,
30856                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30857         }
30858     }
30859     this.closable = false;
30860     this.loaded = false;
30861     this.active = false;
30862     if(typeof config == "string"){
30863         this.title = config;
30864     }else{
30865         Roo.apply(this, config);
30866     }
30867     
30868     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30869         this.wrapEl = this.el.wrap();    
30870         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30871         
30872     }
30873     
30874     
30875     
30876     if(this.resizeEl){
30877         this.resizeEl = Roo.get(this.resizeEl, true);
30878     }else{
30879         this.resizeEl = this.el;
30880     }
30881     this.addEvents({
30882         /**
30883          * @event activate
30884          * Fires when this panel is activated. 
30885          * @param {Roo.ContentPanel} this
30886          */
30887         "activate" : true,
30888         /**
30889          * @event deactivate
30890          * Fires when this panel is activated. 
30891          * @param {Roo.ContentPanel} this
30892          */
30893         "deactivate" : true,
30894
30895         /**
30896          * @event resize
30897          * Fires when this panel is resized if fitToFrame is true.
30898          * @param {Roo.ContentPanel} this
30899          * @param {Number} width The width after any component adjustments
30900          * @param {Number} height The height after any component adjustments
30901          */
30902         "resize" : true
30903     });
30904     if(this.autoScroll){
30905         this.resizeEl.setStyle("overflow", "auto");
30906     }
30907     content = content || this.content;
30908     if(content){
30909         this.setContent(content);
30910     }
30911     if(config && config.url){
30912         this.setUrl(this.url, this.params, this.loadOnce);
30913     }
30914     
30915     
30916     
30917     Roo.ContentPanel.superclass.constructor.call(this);
30918 };
30919
30920 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
30921     tabTip:'',
30922     setRegion : function(region){
30923         this.region = region;
30924         if(region){
30925            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
30926         }else{
30927            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
30928         } 
30929     },
30930     
30931     /**
30932      * Returns the toolbar for this Panel if one was configured. 
30933      * @return {Roo.Toolbar} 
30934      */
30935     getToolbar : function(){
30936         return this.toolbar;
30937     },
30938     
30939     setActiveState : function(active){
30940         this.active = active;
30941         if(!active){
30942             this.fireEvent("deactivate", this);
30943         }else{
30944             this.fireEvent("activate", this);
30945         }
30946     },
30947     /**
30948      * Updates this panel's element
30949      * @param {String} content The new content
30950      * @param {Boolean} loadScripts (optional) true to look for and process scripts
30951     */
30952     setContent : function(content, loadScripts){
30953         this.el.update(content, loadScripts);
30954     },
30955
30956     ignoreResize : function(w, h){
30957         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
30958             return true;
30959         }else{
30960             this.lastSize = {width: w, height: h};
30961             return false;
30962         }
30963     },
30964     /**
30965      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
30966      * @return {Roo.UpdateManager} The UpdateManager
30967      */
30968     getUpdateManager : function(){
30969         return this.el.getUpdateManager();
30970     },
30971      /**
30972      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
30973      * @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:
30974 <pre><code>
30975 panel.load({
30976     url: "your-url.php",
30977     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
30978     callback: yourFunction,
30979     scope: yourObject, //(optional scope)
30980     discardUrl: false,
30981     nocache: false,
30982     text: "Loading...",
30983     timeout: 30,
30984     scripts: false
30985 });
30986 </code></pre>
30987      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
30988      * 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.
30989      * @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}
30990      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
30991      * @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.
30992      * @return {Roo.ContentPanel} this
30993      */
30994     load : function(){
30995         var um = this.el.getUpdateManager();
30996         um.update.apply(um, arguments);
30997         return this;
30998     },
30999
31000
31001     /**
31002      * 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.
31003      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31004      * @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)
31005      * @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)
31006      * @return {Roo.UpdateManager} The UpdateManager
31007      */
31008     setUrl : function(url, params, loadOnce){
31009         if(this.refreshDelegate){
31010             this.removeListener("activate", this.refreshDelegate);
31011         }
31012         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31013         this.on("activate", this.refreshDelegate);
31014         return this.el.getUpdateManager();
31015     },
31016     
31017     _handleRefresh : function(url, params, loadOnce){
31018         if(!loadOnce || !this.loaded){
31019             var updater = this.el.getUpdateManager();
31020             updater.update(url, params, this._setLoaded.createDelegate(this));
31021         }
31022     },
31023     
31024     _setLoaded : function(){
31025         this.loaded = true;
31026     }, 
31027     
31028     /**
31029      * Returns this panel's id
31030      * @return {String} 
31031      */
31032     getId : function(){
31033         return this.el.id;
31034     },
31035     
31036     /** 
31037      * Returns this panel's element - used by regiosn to add.
31038      * @return {Roo.Element} 
31039      */
31040     getEl : function(){
31041         return this.wrapEl || this.el;
31042     },
31043     
31044     adjustForComponents : function(width, height){
31045         if(this.resizeEl != this.el){
31046             width -= this.el.getFrameWidth('lr');
31047             height -= this.el.getFrameWidth('tb');
31048         }
31049         if(this.toolbar){
31050             var te = this.toolbar.getEl();
31051             height -= te.getHeight();
31052             te.setWidth(width);
31053         }
31054         if(this.adjustments){
31055             width += this.adjustments[0];
31056             height += this.adjustments[1];
31057         }
31058         return {"width": width, "height": height};
31059     },
31060     
31061     setSize : function(width, height){
31062         if(this.fitToFrame && !this.ignoreResize(width, height)){
31063             if(this.fitContainer && this.resizeEl != this.el){
31064                 this.el.setSize(width, height);
31065             }
31066             var size = this.adjustForComponents(width, height);
31067             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31068             this.fireEvent('resize', this, size.width, size.height);
31069         }
31070     },
31071     
31072     /**
31073      * Returns this panel's title
31074      * @return {String} 
31075      */
31076     getTitle : function(){
31077         return this.title;
31078     },
31079     
31080     /**
31081      * Set this panel's title
31082      * @param {String} title
31083      */
31084     setTitle : function(title){
31085         this.title = title;
31086         if(this.region){
31087             this.region.updatePanelTitle(this, title);
31088         }
31089     },
31090     
31091     /**
31092      * Returns true is this panel was configured to be closable
31093      * @return {Boolean} 
31094      */
31095     isClosable : function(){
31096         return this.closable;
31097     },
31098     
31099     beforeSlide : function(){
31100         this.el.clip();
31101         this.resizeEl.clip();
31102     },
31103     
31104     afterSlide : function(){
31105         this.el.unclip();
31106         this.resizeEl.unclip();
31107     },
31108     
31109     /**
31110      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31111      *   Will fail silently if the {@link #setUrl} method has not been called.
31112      *   This does not activate the panel, just updates its content.
31113      */
31114     refresh : function(){
31115         if(this.refreshDelegate){
31116            this.loaded = false;
31117            this.refreshDelegate();
31118         }
31119     },
31120     
31121     /**
31122      * Destroys this panel
31123      */
31124     destroy : function(){
31125         this.el.removeAllListeners();
31126         var tempEl = document.createElement("span");
31127         tempEl.appendChild(this.el.dom);
31128         tempEl.innerHTML = "";
31129         this.el.remove();
31130         this.el = null;
31131     },
31132     
31133       /**
31134      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31135      * <pre><code>
31136
31137 layout.addxtype({
31138        xtype : 'Form',
31139        items: [ .... ]
31140    }
31141 );
31142
31143 </code></pre>
31144      * @param {Object} cfg Xtype definition of item to add.
31145      */
31146     
31147     addxtype : function(cfg) {
31148         // add form..
31149         if (cfg.xtype.match(/^Form$/)) {
31150             var el = this.el.createChild();
31151
31152             this.form = new  Roo.form.Form(cfg);
31153             
31154             
31155             if ( this.form.allItems.length) this.form.render(el.dom);
31156             return this.form;
31157         }
31158         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31159             // views..
31160             cfg.el = this.el.appendChild(document.createElement("div"));
31161             // factory?
31162             var ret = new Roo[cfg.xtype](cfg);
31163             ret.render(false, ''); // render blank..
31164             return ret;
31165             
31166         }
31167         return false;
31168         
31169     }
31170 });
31171
31172 /**
31173  * @class Roo.GridPanel
31174  * @extends Roo.ContentPanel
31175  * @constructor
31176  * Create a new GridPanel.
31177  * @param {Roo.grid.Grid} grid The grid for this panel
31178  * @param {String/Object} config A string to set only the panel's title, or a config object
31179  */
31180 Roo.GridPanel = function(grid, config){
31181     
31182   
31183     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31184         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31185         
31186     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31187     
31188     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31189     
31190     if(this.toolbar){
31191         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31192     }
31193     // xtype created footer. - not sure if will work as we normally have to render first..
31194     if (this.footer && !this.footer.el && this.footer.xtype) {
31195         
31196         this.footer.container = this.grid.getView().getFooterPanel(true);
31197         this.footer.dataSource = this.grid.dataSource;
31198         this.footer = Roo.factory(this.footer, Roo);
31199         
31200     }
31201     
31202     grid.monitorWindowResize = false; // turn off autosizing
31203     grid.autoHeight = false;
31204     grid.autoWidth = false;
31205     this.grid = grid;
31206     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31207 };
31208
31209 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31210     getId : function(){
31211         return this.grid.id;
31212     },
31213     
31214     /**
31215      * Returns the grid for this panel
31216      * @return {Roo.grid.Grid} 
31217      */
31218     getGrid : function(){
31219         return this.grid;    
31220     },
31221     
31222     setSize : function(width, height){
31223         if(!this.ignoreResize(width, height)){
31224             var grid = this.grid;
31225             var size = this.adjustForComponents(width, height);
31226             grid.getGridEl().setSize(size.width, size.height);
31227             grid.autoSize();
31228         }
31229     },
31230     
31231     beforeSlide : function(){
31232         this.grid.getView().scroller.clip();
31233     },
31234     
31235     afterSlide : function(){
31236         this.grid.getView().scroller.unclip();
31237     },
31238     
31239     destroy : function(){
31240         this.grid.destroy();
31241         delete this.grid;
31242         Roo.GridPanel.superclass.destroy.call(this); 
31243     }
31244 });
31245
31246
31247 /**
31248  * @class Roo.NestedLayoutPanel
31249  * @extends Roo.ContentPanel
31250  * @constructor
31251  * Create a new NestedLayoutPanel.
31252  * 
31253  * 
31254  * @param {Roo.BorderLayout} layout The layout for this panel
31255  * @param {String/Object} config A string to set only the title or a config object
31256  */
31257 Roo.NestedLayoutPanel = function(layout, config)
31258 {
31259     // construct with only one argument..
31260     /* FIXME - implement nicer consturctors
31261     if (layout.layout) {
31262         config = layout;
31263         layout = config.layout;
31264         delete config.layout;
31265     }
31266     if (layout.xtype && !layout.getEl) {
31267         // then layout needs constructing..
31268         layout = Roo.factory(layout, Roo);
31269     }
31270     */
31271     
31272     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31273     
31274     layout.monitorWindowResize = false; // turn off autosizing
31275     this.layout = layout;
31276     this.layout.getEl().addClass("x-layout-nested-layout");
31277     
31278     
31279     
31280 };
31281
31282 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31283
31284     setSize : function(width, height){
31285         if(!this.ignoreResize(width, height)){
31286             var size = this.adjustForComponents(width, height);
31287             var el = this.layout.getEl();
31288             el.setSize(size.width, size.height);
31289             var touch = el.dom.offsetWidth;
31290             this.layout.layout();
31291             // ie requires a double layout on the first pass
31292             if(Roo.isIE && !this.initialized){
31293                 this.initialized = true;
31294                 this.layout.layout();
31295             }
31296         }
31297     },
31298     
31299     // activate all subpanels if not currently active..
31300     
31301     setActiveState : function(active){
31302         this.active = active;
31303         if(!active){
31304             this.fireEvent("deactivate", this);
31305             return;
31306         }
31307         
31308         this.fireEvent("activate", this);
31309         // not sure if this should happen before or after..
31310         if (!this.layout) {
31311             return; // should not happen..
31312         }
31313         var reg = false;
31314         for (var r in this.layout.regions) {
31315             reg = this.layout.getRegion(r);
31316             if (reg.getActivePanel()) {
31317                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31318                 reg.setActivePanel(reg.getActivePanel());
31319                 continue;
31320             }
31321             if (!reg.panels.length) {
31322                 continue;
31323             }
31324             reg.showPanel(reg.getPanel(0));
31325         }
31326         
31327         
31328         
31329         
31330     },
31331     
31332     /**
31333      * Returns the nested BorderLayout for this panel
31334      * @return {Roo.BorderLayout} 
31335      */
31336     getLayout : function(){
31337         return this.layout;
31338     },
31339     
31340      /**
31341      * Adds a xtype elements to the layout of the nested panel
31342      * <pre><code>
31343
31344 panel.addxtype({
31345        xtype : 'ContentPanel',
31346        region: 'west',
31347        items: [ .... ]
31348    }
31349 );
31350
31351 panel.addxtype({
31352         xtype : 'NestedLayoutPanel',
31353         region: 'west',
31354         layout: {
31355            center: { },
31356            west: { }   
31357         },
31358         items : [ ... list of content panels or nested layout panels.. ]
31359    }
31360 );
31361 </code></pre>
31362      * @param {Object} cfg Xtype definition of item to add.
31363      */
31364     addxtype : function(cfg) {
31365         return this.layout.addxtype(cfg);
31366     
31367     }
31368 });
31369
31370 Roo.ScrollPanel = function(el, config, content){
31371     config = config || {};
31372     config.fitToFrame = true;
31373     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31374     
31375     this.el.dom.style.overflow = "hidden";
31376     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31377     this.el.removeClass("x-layout-inactive-content");
31378     this.el.on("mousewheel", this.onWheel, this);
31379
31380     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31381     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31382     up.unselectable(); down.unselectable();
31383     up.on("click", this.scrollUp, this);
31384     down.on("click", this.scrollDown, this);
31385     up.addClassOnOver("x-scroller-btn-over");
31386     down.addClassOnOver("x-scroller-btn-over");
31387     up.addClassOnClick("x-scroller-btn-click");
31388     down.addClassOnClick("x-scroller-btn-click");
31389     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31390
31391     this.resizeEl = this.el;
31392     this.el = wrap; this.up = up; this.down = down;
31393 };
31394
31395 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31396     increment : 100,
31397     wheelIncrement : 5,
31398     scrollUp : function(){
31399         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31400     },
31401
31402     scrollDown : function(){
31403         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31404     },
31405
31406     afterScroll : function(){
31407         var el = this.resizeEl;
31408         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31409         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31410         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31411     },
31412
31413     setSize : function(){
31414         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31415         this.afterScroll();
31416     },
31417
31418     onWheel : function(e){
31419         var d = e.getWheelDelta();
31420         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31421         this.afterScroll();
31422         e.stopEvent();
31423     },
31424
31425     setContent : function(content, loadScripts){
31426         this.resizeEl.update(content, loadScripts);
31427     }
31428
31429 });
31430
31431
31432
31433
31434
31435
31436
31437
31438
31439 /**
31440  * @class Roo.TreePanel
31441  * @extends Roo.ContentPanel
31442  * @constructor
31443  * Create a new TreePanel. - defaults to fit/scoll contents.
31444  * @param {String/Object} config A string to set only the panel's title, or a config object
31445  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31446  */
31447 Roo.TreePanel = function(config){
31448     var el = config.el;
31449     var tree = config.tree;
31450     delete config.tree; 
31451     delete config.el; // hopefull!
31452     
31453     // wrapper for IE7 strict & safari scroll issue
31454     
31455     var treeEl = el.createChild();
31456     config.resizeEl = treeEl;
31457     
31458     
31459     
31460     Roo.TreePanel.superclass.constructor.call(this, el, config);
31461  
31462  
31463     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31464     //console.log(tree);
31465     this.on('activate', function()
31466     {
31467         if (this.tree.rendered) {
31468             return;
31469         }
31470         //console.log('render tree');
31471         this.tree.render();
31472     });
31473     
31474     this.on('resize',  function (cp, w, h) {
31475             this.tree.innerCt.setWidth(w);
31476             this.tree.innerCt.setHeight(h);
31477             this.tree.innerCt.setStyle('overflow-y', 'auto');
31478     });
31479
31480         
31481     
31482 };
31483
31484 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31485     fitToFrame : true,
31486     autoScroll : true
31487 });
31488
31489
31490
31491
31492
31493
31494
31495
31496
31497
31498
31499 /*
31500  * Based on:
31501  * Ext JS Library 1.1.1
31502  * Copyright(c) 2006-2007, Ext JS, LLC.
31503  *
31504  * Originally Released Under LGPL - original licence link has changed is not relivant.
31505  *
31506  * Fork - LGPL
31507  * <script type="text/javascript">
31508  */
31509  
31510
31511 /**
31512  * @class Roo.ReaderLayout
31513  * @extends Roo.BorderLayout
31514  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31515  * center region containing two nested regions (a top one for a list view and one for item preview below),
31516  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31517  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31518  * expedites the setup of the overall layout and regions for this common application style.
31519  * Example:
31520  <pre><code>
31521 var reader = new Roo.ReaderLayout();
31522 var CP = Roo.ContentPanel;  // shortcut for adding
31523
31524 reader.beginUpdate();
31525 reader.add("north", new CP("north", "North"));
31526 reader.add("west", new CP("west", {title: "West"}));
31527 reader.add("east", new CP("east", {title: "East"}));
31528
31529 reader.regions.listView.add(new CP("listView", "List"));
31530 reader.regions.preview.add(new CP("preview", "Preview"));
31531 reader.endUpdate();
31532 </code></pre>
31533 * @constructor
31534 * Create a new ReaderLayout
31535 * @param {Object} config Configuration options
31536 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31537 * document.body if omitted)
31538 */
31539 Roo.ReaderLayout = function(config, renderTo){
31540     var c = config || {size:{}};
31541     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31542         north: c.north !== false ? Roo.apply({
31543             split:false,
31544             initialSize: 32,
31545             titlebar: false
31546         }, c.north) : false,
31547         west: c.west !== false ? Roo.apply({
31548             split:true,
31549             initialSize: 200,
31550             minSize: 175,
31551             maxSize: 400,
31552             titlebar: true,
31553             collapsible: true,
31554             animate: true,
31555             margins:{left:5,right:0,bottom:5,top:5},
31556             cmargins:{left:5,right:5,bottom:5,top:5}
31557         }, c.west) : false,
31558         east: c.east !== false ? Roo.apply({
31559             split:true,
31560             initialSize: 200,
31561             minSize: 175,
31562             maxSize: 400,
31563             titlebar: true,
31564             collapsible: true,
31565             animate: true,
31566             margins:{left:0,right:5,bottom:5,top:5},
31567             cmargins:{left:5,right:5,bottom:5,top:5}
31568         }, c.east) : false,
31569         center: Roo.apply({
31570             tabPosition: 'top',
31571             autoScroll:false,
31572             closeOnTab: true,
31573             titlebar:false,
31574             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31575         }, c.center)
31576     });
31577
31578     this.el.addClass('x-reader');
31579
31580     this.beginUpdate();
31581
31582     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31583         south: c.preview !== false ? Roo.apply({
31584             split:true,
31585             initialSize: 200,
31586             minSize: 100,
31587             autoScroll:true,
31588             collapsible:true,
31589             titlebar: true,
31590             cmargins:{top:5,left:0, right:0, bottom:0}
31591         }, c.preview) : false,
31592         center: Roo.apply({
31593             autoScroll:false,
31594             titlebar:false,
31595             minHeight:200
31596         }, c.listView)
31597     });
31598     this.add('center', new Roo.NestedLayoutPanel(inner,
31599             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31600
31601     this.endUpdate();
31602
31603     this.regions.preview = inner.getRegion('south');
31604     this.regions.listView = inner.getRegion('center');
31605 };
31606
31607 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31608  * Based on:
31609  * Ext JS Library 1.1.1
31610  * Copyright(c) 2006-2007, Ext JS, LLC.
31611  *
31612  * Originally Released Under LGPL - original licence link has changed is not relivant.
31613  *
31614  * Fork - LGPL
31615  * <script type="text/javascript">
31616  */
31617  
31618 /**
31619  * @class Roo.grid.Grid
31620  * @extends Roo.util.Observable
31621  * This class represents the primary interface of a component based grid control.
31622  * <br><br>Usage:<pre><code>
31623  var grid = new Roo.grid.Grid("my-container-id", {
31624      ds: myDataStore,
31625      cm: myColModel,
31626      selModel: mySelectionModel,
31627      autoSizeColumns: true,
31628      monitorWindowResize: false,
31629      trackMouseOver: true
31630  });
31631  // set any options
31632  grid.render();
31633  * </code></pre>
31634  * <b>Common Problems:</b><br/>
31635  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31636  * element will correct this<br/>
31637  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31638  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31639  * are unpredictable.<br/>
31640  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31641  * grid to calculate dimensions/offsets.<br/>
31642   * @constructor
31643  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31644  * The container MUST have some type of size defined for the grid to fill. The container will be
31645  * automatically set to position relative if it isn't already.
31646  * @param {Object} config A config object that sets properties on this grid.
31647  */
31648 Roo.grid.Grid = function(container, config){
31649         // initialize the container
31650         this.container = Roo.get(container);
31651         this.container.update("");
31652         this.container.setStyle("overflow", "hidden");
31653     this.container.addClass('x-grid-container');
31654
31655     this.id = this.container.id;
31656
31657     Roo.apply(this, config);
31658     // check and correct shorthanded configs
31659     if(this.ds){
31660         this.dataSource = this.ds;
31661         delete this.ds;
31662     }
31663     if(this.cm){
31664         this.colModel = this.cm;
31665         delete this.cm;
31666     }
31667     if(this.sm){
31668         this.selModel = this.sm;
31669         delete this.sm;
31670     }
31671
31672     if (this.selModel) {
31673         this.selModel = Roo.factory(this.selModel, Roo.grid);
31674         this.sm = this.selModel;
31675         this.sm.xmodule = this.xmodule || false;
31676     }
31677     if (typeof(this.colModel.config) == 'undefined') {
31678         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31679         this.cm = this.colModel;
31680         this.cm.xmodule = this.xmodule || false;
31681     }
31682     if (this.dataSource) {
31683         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31684         this.ds = this.dataSource;
31685         this.ds.xmodule = this.xmodule || false;
31686         
31687     }
31688     
31689     
31690     
31691     if(this.width){
31692         this.container.setWidth(this.width);
31693     }
31694
31695     if(this.height){
31696         this.container.setHeight(this.height);
31697     }
31698     /** @private */
31699         this.addEvents({
31700             // raw events
31701             /**
31702              * @event click
31703              * The raw click event for the entire grid.
31704              * @param {Roo.EventObject} e
31705              */
31706             "click" : true,
31707             /**
31708              * @event dblclick
31709              * The raw dblclick event for the entire grid.
31710              * @param {Roo.EventObject} e
31711              */
31712             "dblclick" : true,
31713             /**
31714              * @event contextmenu
31715              * The raw contextmenu event for the entire grid.
31716              * @param {Roo.EventObject} e
31717              */
31718             "contextmenu" : true,
31719             /**
31720              * @event mousedown
31721              * The raw mousedown event for the entire grid.
31722              * @param {Roo.EventObject} e
31723              */
31724             "mousedown" : true,
31725             /**
31726              * @event mouseup
31727              * The raw mouseup event for the entire grid.
31728              * @param {Roo.EventObject} e
31729              */
31730             "mouseup" : true,
31731             /**
31732              * @event mouseover
31733              * The raw mouseover event for the entire grid.
31734              * @param {Roo.EventObject} e
31735              */
31736             "mouseover" : true,
31737             /**
31738              * @event mouseout
31739              * The raw mouseout event for the entire grid.
31740              * @param {Roo.EventObject} e
31741              */
31742             "mouseout" : true,
31743             /**
31744              * @event keypress
31745              * The raw keypress event for the entire grid.
31746              * @param {Roo.EventObject} e
31747              */
31748             "keypress" : true,
31749             /**
31750              * @event keydown
31751              * The raw keydown event for the entire grid.
31752              * @param {Roo.EventObject} e
31753              */
31754             "keydown" : true,
31755
31756             // custom events
31757
31758             /**
31759              * @event cellclick
31760              * Fires when a cell is clicked
31761              * @param {Grid} this
31762              * @param {Number} rowIndex
31763              * @param {Number} columnIndex
31764              * @param {Roo.EventObject} e
31765              */
31766             "cellclick" : true,
31767             /**
31768              * @event celldblclick
31769              * Fires when a cell is double clicked
31770              * @param {Grid} this
31771              * @param {Number} rowIndex
31772              * @param {Number} columnIndex
31773              * @param {Roo.EventObject} e
31774              */
31775             "celldblclick" : true,
31776             /**
31777              * @event rowclick
31778              * Fires when a row is clicked
31779              * @param {Grid} this
31780              * @param {Number} rowIndex
31781              * @param {Roo.EventObject} e
31782              */
31783             "rowclick" : true,
31784             /**
31785              * @event rowdblclick
31786              * Fires when a row is double clicked
31787              * @param {Grid} this
31788              * @param {Number} rowIndex
31789              * @param {Roo.EventObject} e
31790              */
31791             "rowdblclick" : true,
31792             /**
31793              * @event headerclick
31794              * Fires when a header is clicked
31795              * @param {Grid} this
31796              * @param {Number} columnIndex
31797              * @param {Roo.EventObject} e
31798              */
31799             "headerclick" : true,
31800             /**
31801              * @event headerdblclick
31802              * Fires when a header cell is double clicked
31803              * @param {Grid} this
31804              * @param {Number} columnIndex
31805              * @param {Roo.EventObject} e
31806              */
31807             "headerdblclick" : true,
31808             /**
31809              * @event rowcontextmenu
31810              * Fires when a row is right clicked
31811              * @param {Grid} this
31812              * @param {Number} rowIndex
31813              * @param {Roo.EventObject} e
31814              */
31815             "rowcontextmenu" : true,
31816             /**
31817          * @event cellcontextmenu
31818          * Fires when a cell is right clicked
31819          * @param {Grid} this
31820          * @param {Number} rowIndex
31821          * @param {Number} cellIndex
31822          * @param {Roo.EventObject} e
31823          */
31824          "cellcontextmenu" : true,
31825             /**
31826              * @event headercontextmenu
31827              * Fires when a header is right clicked
31828              * @param {Grid} this
31829              * @param {Number} columnIndex
31830              * @param {Roo.EventObject} e
31831              */
31832             "headercontextmenu" : true,
31833             /**
31834              * @event bodyscroll
31835              * Fires when the body element is scrolled
31836              * @param {Number} scrollLeft
31837              * @param {Number} scrollTop
31838              */
31839             "bodyscroll" : true,
31840             /**
31841              * @event columnresize
31842              * Fires when the user resizes a column
31843              * @param {Number} columnIndex
31844              * @param {Number} newSize
31845              */
31846             "columnresize" : true,
31847             /**
31848              * @event columnmove
31849              * Fires when the user moves a column
31850              * @param {Number} oldIndex
31851              * @param {Number} newIndex
31852              */
31853             "columnmove" : true,
31854             /**
31855              * @event startdrag
31856              * Fires when row(s) start being dragged
31857              * @param {Grid} this
31858              * @param {Roo.GridDD} dd The drag drop object
31859              * @param {event} e The raw browser event
31860              */
31861             "startdrag" : true,
31862             /**
31863              * @event enddrag
31864              * Fires when a drag operation is complete
31865              * @param {Grid} this
31866              * @param {Roo.GridDD} dd The drag drop object
31867              * @param {event} e The raw browser event
31868              */
31869             "enddrag" : true,
31870             /**
31871              * @event dragdrop
31872              * Fires when dragged row(s) are dropped on a valid DD target
31873              * @param {Grid} this
31874              * @param {Roo.GridDD} dd The drag drop object
31875              * @param {String} targetId The target drag drop object
31876              * @param {event} e The raw browser event
31877              */
31878             "dragdrop" : true,
31879             /**
31880              * @event dragover
31881              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31882              * @param {Grid} this
31883              * @param {Roo.GridDD} dd The drag drop object
31884              * @param {String} targetId The target drag drop object
31885              * @param {event} e The raw browser event
31886              */
31887             "dragover" : true,
31888             /**
31889              * @event dragenter
31890              *  Fires when the dragged row(s) first cross another DD target while being dragged
31891              * @param {Grid} this
31892              * @param {Roo.GridDD} dd The drag drop object
31893              * @param {String} targetId The target drag drop object
31894              * @param {event} e The raw browser event
31895              */
31896             "dragenter" : true,
31897             /**
31898              * @event dragout
31899              * Fires when the dragged row(s) leave another DD target while being dragged
31900              * @param {Grid} this
31901              * @param {Roo.GridDD} dd The drag drop object
31902              * @param {String} targetId The target drag drop object
31903              * @param {event} e The raw browser event
31904              */
31905             "dragout" : true,
31906         /**
31907          * @event render
31908          * Fires when the grid is rendered
31909          * @param {Grid} grid
31910          */
31911         render : true
31912     });
31913
31914     Roo.grid.Grid.superclass.constructor.call(this);
31915 };
31916 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
31917     
31918     /**
31919      * @cfg {String} ddGroup - drag drop group.
31920          */
31921     
31922     /**
31923      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
31924          */
31925         minColumnWidth : 25,
31926
31927     /**
31928          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
31929          * <b>on initial render.</b> It is more efficient to explicitly size the columns
31930          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
31931          */
31932         autoSizeColumns : false,
31933
31934         /**
31935          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
31936          */
31937         autoSizeHeaders : true,
31938
31939         /**
31940          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
31941          */
31942         monitorWindowResize : true,
31943
31944         /**
31945          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
31946          * rows measured to get a columns size. Default is 0 (all rows).
31947          */
31948         maxRowsToMeasure : 0,
31949
31950         /**
31951          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
31952          */
31953         trackMouseOver : true,
31954
31955     /**
31956          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
31957          */
31958     
31959         /**
31960          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
31961          */
31962         enableDragDrop : false,
31963
31964         /**
31965          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
31966          */
31967         enableColumnMove : true,
31968
31969         /**
31970          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
31971          */
31972         enableColumnHide : true,
31973
31974         /**
31975          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
31976          */
31977         enableRowHeightSync : false,
31978
31979         /**
31980          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
31981          */
31982         stripeRows : true,
31983
31984         /**
31985          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
31986          */
31987         autoHeight : false,
31988
31989     /**
31990      * @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.
31991      */
31992     autoExpandColumn : false,
31993
31994     /**
31995     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
31996     * Default is 50.
31997     */
31998     autoExpandMin : 50,
31999
32000     /**
32001     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32002     */
32003     autoExpandMax : 1000,
32004
32005     /**
32006          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32007          */
32008         view : null,
32009
32010         /**
32011      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32012          */
32013         loadMask : false,
32014
32015     // private
32016     rendered : false,
32017
32018     /**
32019     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32020     * of a fixed width. Default is false.
32021     */
32022     /**
32023     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32024     */
32025     /**
32026      * Called once after all setup has been completed and the grid is ready to be rendered.
32027      * @return {Roo.grid.Grid} this
32028      */
32029     render : function(){
32030         var c = this.container;
32031         // try to detect autoHeight/width mode
32032         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32033             this.autoHeight = true;
32034         }
32035         var view = this.getView();
32036         view.init(this);
32037
32038         c.on("click", this.onClick, this);
32039         c.on("dblclick", this.onDblClick, this);
32040         c.on("contextmenu", this.onContextMenu, this);
32041         c.on("keydown", this.onKeyDown, this);
32042
32043         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32044
32045         this.getSelectionModel().init(this);
32046
32047         view.render();
32048
32049         if(this.loadMask){
32050             this.loadMask = new Roo.LoadMask(this.container,
32051                     Roo.apply({store:this.dataSource}, this.loadMask));
32052         }
32053         
32054         
32055         if (this.toolbar && this.toolbar.xtype) {
32056             this.toolbar.container = this.getView().getHeaderPanel(true);
32057             this.toolbar = new Ext.Toolbar(this.toolbar);
32058         }
32059         if (this.footer && this.footer.xtype) {
32060             this.footer.dataSource = this.getDataSource();
32061             this.footer.container = this.getView().getFooterPanel(true);
32062             this.footer = Roo.factory(this.footer, Roo);
32063         }
32064         this.rendered = true;
32065         this.fireEvent('render', this);
32066         return this;
32067     },
32068
32069         /**
32070          * Reconfigures the grid to use a different Store and Column Model.
32071          * The View will be bound to the new objects and refreshed.
32072          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32073          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32074          */
32075     reconfigure : function(dataSource, colModel){
32076         if(this.loadMask){
32077             this.loadMask.destroy();
32078             this.loadMask = new Roo.LoadMask(this.container,
32079                     Roo.apply({store:dataSource}, this.loadMask));
32080         }
32081         this.view.bind(dataSource, colModel);
32082         this.dataSource = dataSource;
32083         this.colModel = colModel;
32084         this.view.refresh(true);
32085     },
32086
32087     // private
32088     onKeyDown : function(e){
32089         this.fireEvent("keydown", e);
32090     },
32091
32092     /**
32093      * Destroy this grid.
32094      * @param {Boolean} removeEl True to remove the element
32095      */
32096     destroy : function(removeEl, keepListeners){
32097         if(this.loadMask){
32098             this.loadMask.destroy();
32099         }
32100         var c = this.container;
32101         c.removeAllListeners();
32102         this.view.destroy();
32103         this.colModel.purgeListeners();
32104         if(!keepListeners){
32105             this.purgeListeners();
32106         }
32107         c.update("");
32108         if(removeEl === true){
32109             c.remove();
32110         }
32111     },
32112
32113     // private
32114     processEvent : function(name, e){
32115         this.fireEvent(name, e);
32116         var t = e.getTarget();
32117         var v = this.view;
32118         var header = v.findHeaderIndex(t);
32119         if(header !== false){
32120             this.fireEvent("header" + name, this, header, e);
32121         }else{
32122             var row = v.findRowIndex(t);
32123             var cell = v.findCellIndex(t);
32124             if(row !== false){
32125                 this.fireEvent("row" + name, this, row, e);
32126                 if(cell !== false){
32127                     this.fireEvent("cell" + name, this, row, cell, e);
32128                 }
32129             }
32130         }
32131     },
32132
32133     // private
32134     onClick : function(e){
32135         this.processEvent("click", e);
32136     },
32137
32138     // private
32139     onContextMenu : function(e, t){
32140         this.processEvent("contextmenu", e);
32141     },
32142
32143     // private
32144     onDblClick : function(e){
32145         this.processEvent("dblclick", e);
32146     },
32147
32148     // private
32149     walkCells : function(row, col, step, fn, scope){
32150         var cm = this.colModel, clen = cm.getColumnCount();
32151         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32152         if(step < 0){
32153             if(col < 0){
32154                 row--;
32155                 first = false;
32156             }
32157             while(row >= 0){
32158                 if(!first){
32159                     col = clen-1;
32160                 }
32161                 first = false;
32162                 while(col >= 0){
32163                     if(fn.call(scope || this, row, col, cm) === true){
32164                         return [row, col];
32165                     }
32166                     col--;
32167                 }
32168                 row--;
32169             }
32170         } else {
32171             if(col >= clen){
32172                 row++;
32173                 first = false;
32174             }
32175             while(row < rlen){
32176                 if(!first){
32177                     col = 0;
32178                 }
32179                 first = false;
32180                 while(col < clen){
32181                     if(fn.call(scope || this, row, col, cm) === true){
32182                         return [row, col];
32183                     }
32184                     col++;
32185                 }
32186                 row++;
32187             }
32188         }
32189         return null;
32190     },
32191
32192     // private
32193     getSelections : function(){
32194         return this.selModel.getSelections();
32195     },
32196
32197     /**
32198      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32199      * but if manual update is required this method will initiate it.
32200      */
32201     autoSize : function(){
32202         if(this.rendered){
32203             this.view.layout();
32204             if(this.view.adjustForScroll){
32205                 this.view.adjustForScroll();
32206             }
32207         }
32208     },
32209
32210     /**
32211      * Returns the grid's underlying element.
32212      * @return {Element} The element
32213      */
32214     getGridEl : function(){
32215         return this.container;
32216     },
32217
32218     // private for compatibility, overridden by editor grid
32219     stopEditing : function(){},
32220
32221     /**
32222      * Returns the grid's SelectionModel.
32223      * @return {SelectionModel}
32224      */
32225     getSelectionModel : function(){
32226         if(!this.selModel){
32227             this.selModel = new Roo.grid.RowSelectionModel();
32228         }
32229         return this.selModel;
32230     },
32231
32232     /**
32233      * Returns the grid's DataSource.
32234      * @return {DataSource}
32235      */
32236     getDataSource : function(){
32237         return this.dataSource;
32238     },
32239
32240     /**
32241      * Returns the grid's ColumnModel.
32242      * @return {ColumnModel}
32243      */
32244     getColumnModel : function(){
32245         return this.colModel;
32246     },
32247
32248     /**
32249      * Returns the grid's GridView object.
32250      * @return {GridView}
32251      */
32252     getView : function(){
32253         if(!this.view){
32254             this.view = new Roo.grid.GridView(this.viewConfig);
32255         }
32256         return this.view;
32257     },
32258     /**
32259      * Called to get grid's drag proxy text, by default returns this.ddText.
32260      * @return {String}
32261      */
32262     getDragDropText : function(){
32263         var count = this.selModel.getCount();
32264         return String.format(this.ddText, count, count == 1 ? '' : 's');
32265     }
32266 });
32267 /**
32268  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32269  * %0 is replaced with the number of selected rows.
32270  * @type String
32271  */
32272 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32273  * Based on:
32274  * Ext JS Library 1.1.1
32275  * Copyright(c) 2006-2007, Ext JS, LLC.
32276  *
32277  * Originally Released Under LGPL - original licence link has changed is not relivant.
32278  *
32279  * Fork - LGPL
32280  * <script type="text/javascript">
32281  */
32282  
32283 Roo.grid.AbstractGridView = function(){
32284         this.grid = null;
32285         
32286         this.events = {
32287             "beforerowremoved" : true,
32288             "beforerowsinserted" : true,
32289             "beforerefresh" : true,
32290             "rowremoved" : true,
32291             "rowsinserted" : true,
32292             "rowupdated" : true,
32293             "refresh" : true
32294         };
32295     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32296 };
32297
32298 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32299     rowClass : "x-grid-row",
32300     cellClass : "x-grid-cell",
32301     tdClass : "x-grid-td",
32302     hdClass : "x-grid-hd",
32303     splitClass : "x-grid-hd-split",
32304     
32305         init: function(grid){
32306         this.grid = grid;
32307                 var cid = this.grid.getGridEl().id;
32308         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32309         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32310         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32311         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32312         },
32313         
32314         getColumnRenderers : function(){
32315         var renderers = [];
32316         var cm = this.grid.colModel;
32317         var colCount = cm.getColumnCount();
32318         for(var i = 0; i < colCount; i++){
32319             renderers[i] = cm.getRenderer(i);
32320         }
32321         return renderers;
32322     },
32323     
32324     getColumnIds : function(){
32325         var ids = [];
32326         var cm = this.grid.colModel;
32327         var colCount = cm.getColumnCount();
32328         for(var i = 0; i < colCount; i++){
32329             ids[i] = cm.getColumnId(i);
32330         }
32331         return ids;
32332     },
32333     
32334     getDataIndexes : function(){
32335         if(!this.indexMap){
32336             this.indexMap = this.buildIndexMap();
32337         }
32338         return this.indexMap.colToData;
32339     },
32340     
32341     getColumnIndexByDataIndex : function(dataIndex){
32342         if(!this.indexMap){
32343             this.indexMap = this.buildIndexMap();
32344         }
32345         return this.indexMap.dataToCol[dataIndex];
32346     },
32347     
32348     /**
32349      * Set a css style for a column dynamically. 
32350      * @param {Number} colIndex The index of the column
32351      * @param {String} name The css property name
32352      * @param {String} value The css value
32353      */
32354     setCSSStyle : function(colIndex, name, value){
32355         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32356         Roo.util.CSS.updateRule(selector, name, value);
32357     },
32358     
32359     generateRules : function(cm){
32360         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32361         Roo.util.CSS.removeStyleSheet(rulesId);
32362         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32363             var cid = cm.getColumnId(i);
32364             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32365                          this.tdSelector, cid, " {\n}\n",
32366                          this.hdSelector, cid, " {\n}\n",
32367                          this.splitSelector, cid, " {\n}\n");
32368         }
32369         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32370     }
32371 });/*
32372  * Based on:
32373  * Ext JS Library 1.1.1
32374  * Copyright(c) 2006-2007, Ext JS, LLC.
32375  *
32376  * Originally Released Under LGPL - original licence link has changed is not relivant.
32377  *
32378  * Fork - LGPL
32379  * <script type="text/javascript">
32380  */
32381
32382 // private
32383 // This is a support class used internally by the Grid components
32384 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32385     this.grid = grid;
32386     this.view = grid.getView();
32387     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32388     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32389     if(hd2){
32390         this.setHandleElId(Roo.id(hd));
32391         this.setOuterHandleElId(Roo.id(hd2));
32392     }
32393     this.scroll = false;
32394 };
32395 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32396     maxDragWidth: 120,
32397     getDragData : function(e){
32398         var t = Roo.lib.Event.getTarget(e);
32399         var h = this.view.findHeaderCell(t);
32400         if(h){
32401             return {ddel: h.firstChild, header:h};
32402         }
32403         return false;
32404     },
32405
32406     onInitDrag : function(e){
32407         this.view.headersDisabled = true;
32408         var clone = this.dragData.ddel.cloneNode(true);
32409         clone.id = Roo.id();
32410         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32411         this.proxy.update(clone);
32412         return true;
32413     },
32414
32415     afterValidDrop : function(){
32416         var v = this.view;
32417         setTimeout(function(){
32418             v.headersDisabled = false;
32419         }, 50);
32420     },
32421
32422     afterInvalidDrop : function(){
32423         var v = this.view;
32424         setTimeout(function(){
32425             v.headersDisabled = false;
32426         }, 50);
32427     }
32428 });
32429 /*
32430  * Based on:
32431  * Ext JS Library 1.1.1
32432  * Copyright(c) 2006-2007, Ext JS, LLC.
32433  *
32434  * Originally Released Under LGPL - original licence link has changed is not relivant.
32435  *
32436  * Fork - LGPL
32437  * <script type="text/javascript">
32438  */
32439 // private
32440 // This is a support class used internally by the Grid components
32441 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32442     this.grid = grid;
32443     this.view = grid.getView();
32444     // split the proxies so they don't interfere with mouse events
32445     this.proxyTop = Roo.DomHelper.append(document.body, {
32446         cls:"col-move-top", html:"&#160;"
32447     }, true);
32448     this.proxyBottom = Roo.DomHelper.append(document.body, {
32449         cls:"col-move-bottom", html:"&#160;"
32450     }, true);
32451     this.proxyTop.hide = this.proxyBottom.hide = function(){
32452         this.setLeftTop(-100,-100);
32453         this.setStyle("visibility", "hidden");
32454     };
32455     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32456     // temporarily disabled
32457     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32458     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32459 };
32460 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32461     proxyOffsets : [-4, -9],
32462     fly: Roo.Element.fly,
32463
32464     getTargetFromEvent : function(e){
32465         var t = Roo.lib.Event.getTarget(e);
32466         var cindex = this.view.findCellIndex(t);
32467         if(cindex !== false){
32468             return this.view.getHeaderCell(cindex);
32469         }
32470     },
32471
32472     nextVisible : function(h){
32473         var v = this.view, cm = this.grid.colModel;
32474         h = h.nextSibling;
32475         while(h){
32476             if(!cm.isHidden(v.getCellIndex(h))){
32477                 return h;
32478             }
32479             h = h.nextSibling;
32480         }
32481         return null;
32482     },
32483
32484     prevVisible : function(h){
32485         var v = this.view, cm = this.grid.colModel;
32486         h = h.prevSibling;
32487         while(h){
32488             if(!cm.isHidden(v.getCellIndex(h))){
32489                 return h;
32490             }
32491             h = h.prevSibling;
32492         }
32493         return null;
32494     },
32495
32496     positionIndicator : function(h, n, e){
32497         var x = Roo.lib.Event.getPageX(e);
32498         var r = Roo.lib.Dom.getRegion(n.firstChild);
32499         var px, pt, py = r.top + this.proxyOffsets[1];
32500         if((r.right - x) <= (r.right-r.left)/2){
32501             px = r.right+this.view.borderWidth;
32502             pt = "after";
32503         }else{
32504             px = r.left;
32505             pt = "before";
32506         }
32507         var oldIndex = this.view.getCellIndex(h);
32508         var newIndex = this.view.getCellIndex(n);
32509
32510         if(this.grid.colModel.isFixed(newIndex)){
32511             return false;
32512         }
32513
32514         var locked = this.grid.colModel.isLocked(newIndex);
32515
32516         if(pt == "after"){
32517             newIndex++;
32518         }
32519         if(oldIndex < newIndex){
32520             newIndex--;
32521         }
32522         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32523             return false;
32524         }
32525         px +=  this.proxyOffsets[0];
32526         this.proxyTop.setLeftTop(px, py);
32527         this.proxyTop.show();
32528         if(!this.bottomOffset){
32529             this.bottomOffset = this.view.mainHd.getHeight();
32530         }
32531         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32532         this.proxyBottom.show();
32533         return pt;
32534     },
32535
32536     onNodeEnter : function(n, dd, e, data){
32537         if(data.header != n){
32538             this.positionIndicator(data.header, n, e);
32539         }
32540     },
32541
32542     onNodeOver : function(n, dd, e, data){
32543         var result = false;
32544         if(data.header != n){
32545             result = this.positionIndicator(data.header, n, e);
32546         }
32547         if(!result){
32548             this.proxyTop.hide();
32549             this.proxyBottom.hide();
32550         }
32551         return result ? this.dropAllowed : this.dropNotAllowed;
32552     },
32553
32554     onNodeOut : function(n, dd, e, data){
32555         this.proxyTop.hide();
32556         this.proxyBottom.hide();
32557     },
32558
32559     onNodeDrop : function(n, dd, e, data){
32560         var h = data.header;
32561         if(h != n){
32562             var cm = this.grid.colModel;
32563             var x = Roo.lib.Event.getPageX(e);
32564             var r = Roo.lib.Dom.getRegion(n.firstChild);
32565             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32566             var oldIndex = this.view.getCellIndex(h);
32567             var newIndex = this.view.getCellIndex(n);
32568             var locked = cm.isLocked(newIndex);
32569             if(pt == "after"){
32570                 newIndex++;
32571             }
32572             if(oldIndex < newIndex){
32573                 newIndex--;
32574             }
32575             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32576                 return false;
32577             }
32578             cm.setLocked(oldIndex, locked, true);
32579             cm.moveColumn(oldIndex, newIndex);
32580             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32581             return true;
32582         }
32583         return false;
32584     }
32585 });
32586 /*
32587  * Based on:
32588  * Ext JS Library 1.1.1
32589  * Copyright(c) 2006-2007, Ext JS, LLC.
32590  *
32591  * Originally Released Under LGPL - original licence link has changed is not relivant.
32592  *
32593  * Fork - LGPL
32594  * <script type="text/javascript">
32595  */
32596   
32597 /**
32598  * @class Roo.grid.GridView
32599  * @extends Roo.util.Observable
32600  *
32601  * @constructor
32602  * @param {Object} config
32603  */
32604 Roo.grid.GridView = function(config){
32605     Roo.grid.GridView.superclass.constructor.call(this);
32606     this.el = null;
32607
32608     Roo.apply(this, config);
32609 };
32610
32611 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32612
32613     /**
32614      * Override this function to apply custom css classes to rows during rendering
32615      * @param {Record} record The record
32616      * @param {Number} index
32617      * @method getRowClass
32618      */
32619     rowClass : "x-grid-row",
32620
32621     cellClass : "x-grid-col",
32622
32623     tdClass : "x-grid-td",
32624
32625     hdClass : "x-grid-hd",
32626
32627     splitClass : "x-grid-split",
32628
32629     sortClasses : ["sort-asc", "sort-desc"],
32630
32631     enableMoveAnim : false,
32632
32633     hlColor: "C3DAF9",
32634
32635     dh : Roo.DomHelper,
32636
32637     fly : Roo.Element.fly,
32638
32639     css : Roo.util.CSS,
32640
32641     borderWidth: 1,
32642
32643     splitOffset: 3,
32644
32645     scrollIncrement : 22,
32646
32647     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32648
32649     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32650
32651     bind : function(ds, cm){
32652         if(this.ds){
32653             this.ds.un("load", this.onLoad, this);
32654             this.ds.un("datachanged", this.onDataChange, this);
32655             this.ds.un("add", this.onAdd, this);
32656             this.ds.un("remove", this.onRemove, this);
32657             this.ds.un("update", this.onUpdate, this);
32658             this.ds.un("clear", this.onClear, this);
32659         }
32660         if(ds){
32661             ds.on("load", this.onLoad, this);
32662             ds.on("datachanged", this.onDataChange, this);
32663             ds.on("add", this.onAdd, this);
32664             ds.on("remove", this.onRemove, this);
32665             ds.on("update", this.onUpdate, this);
32666             ds.on("clear", this.onClear, this);
32667         }
32668         this.ds = ds;
32669
32670         if(this.cm){
32671             this.cm.un("widthchange", this.onColWidthChange, this);
32672             this.cm.un("headerchange", this.onHeaderChange, this);
32673             this.cm.un("hiddenchange", this.onHiddenChange, this);
32674             this.cm.un("columnmoved", this.onColumnMove, this);
32675             this.cm.un("columnlockchange", this.onColumnLock, this);
32676         }
32677         if(cm){
32678             this.generateRules(cm);
32679             cm.on("widthchange", this.onColWidthChange, this);
32680             cm.on("headerchange", this.onHeaderChange, this);
32681             cm.on("hiddenchange", this.onHiddenChange, this);
32682             cm.on("columnmoved", this.onColumnMove, this);
32683             cm.on("columnlockchange", this.onColumnLock, this);
32684         }
32685         this.cm = cm;
32686     },
32687
32688     init: function(grid){
32689                 Roo.grid.GridView.superclass.init.call(this, grid);
32690
32691                 this.bind(grid.dataSource, grid.colModel);
32692
32693             grid.on("headerclick", this.handleHeaderClick, this);
32694
32695         if(grid.trackMouseOver){
32696             grid.on("mouseover", this.onRowOver, this);
32697                 grid.on("mouseout", this.onRowOut, this);
32698             }
32699             grid.cancelTextSelection = function(){};
32700                 this.gridId = grid.id;
32701
32702                 var tpls = this.templates || {};
32703
32704                 if(!tpls.master){
32705                     tpls.master = new Roo.Template(
32706                        '<div class="x-grid" hidefocus="true">',
32707                           '<div class="x-grid-topbar"></div>',
32708                           '<div class="x-grid-scroller"><div></div></div>',
32709                           '<div class="x-grid-locked">',
32710                               '<div class="x-grid-header">{lockedHeader}</div>',
32711                               '<div class="x-grid-body">{lockedBody}</div>',
32712                           "</div>",
32713                           '<div class="x-grid-viewport">',
32714                               '<div class="x-grid-header">{header}</div>',
32715                               '<div class="x-grid-body">{body}</div>',
32716                           "</div>",
32717                           '<div class="x-grid-bottombar"></div>',
32718                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32719                           '<div class="x-grid-resize-proxy">&#160;</div>',
32720                        "</div>"
32721                     );
32722                     tpls.master.disableformats = true;
32723                 }
32724
32725                 if(!tpls.header){
32726                     tpls.header = new Roo.Template(
32727                        '<table border="0" cellspacing="0" cellpadding="0">',
32728                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32729                        "</table>{splits}"
32730                     );
32731                     tpls.header.disableformats = true;
32732                 }
32733                 tpls.header.compile();
32734
32735                 if(!tpls.hcell){
32736                     tpls.hcell = new Roo.Template(
32737                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32738                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32739                         "</div></td>"
32740                      );
32741                      tpls.hcell.disableFormats = true;
32742                 }
32743                 tpls.hcell.compile();
32744
32745                 if(!tpls.hsplit){
32746                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32747                     tpls.hsplit.disableFormats = true;
32748                 }
32749                 tpls.hsplit.compile();
32750
32751                 if(!tpls.body){
32752                     tpls.body = new Roo.Template(
32753                        '<table border="0" cellspacing="0" cellpadding="0">',
32754                        "<tbody>{rows}</tbody>",
32755                        "</table>"
32756                     );
32757                     tpls.body.disableFormats = true;
32758                 }
32759                 tpls.body.compile();
32760
32761                 if(!tpls.row){
32762                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32763                     tpls.row.disableFormats = true;
32764                 }
32765                 tpls.row.compile();
32766
32767                 if(!tpls.cell){
32768                     tpls.cell = new Roo.Template(
32769                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32770                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32771                         "</td>"
32772                     );
32773             tpls.cell.disableFormats = true;
32774         }
32775                 tpls.cell.compile();
32776
32777                 this.templates = tpls;
32778         },
32779
32780         // remap these for backwards compat
32781     onColWidthChange : function(){
32782         this.updateColumns.apply(this, arguments);
32783     },
32784     onHeaderChange : function(){
32785         this.updateHeaders.apply(this, arguments);
32786     }, 
32787     onHiddenChange : function(){
32788         this.handleHiddenChange.apply(this, arguments);
32789     },
32790     onColumnMove : function(){
32791         this.handleColumnMove.apply(this, arguments);
32792     },
32793     onColumnLock : function(){
32794         this.handleLockChange.apply(this, arguments);
32795     },
32796
32797     onDataChange : function(){
32798         this.refresh();
32799         this.updateHeaderSortState();
32800     },
32801
32802         onClear : function(){
32803         this.refresh();
32804     },
32805
32806         onUpdate : function(ds, record){
32807         this.refreshRow(record);
32808     },
32809
32810     refreshRow : function(record){
32811         var ds = this.ds, index;
32812         if(typeof record == 'number'){
32813             index = record;
32814             record = ds.getAt(index);
32815         }else{
32816             index = ds.indexOf(record);
32817         }
32818         this.insertRows(ds, index, index, true);
32819         this.onRemove(ds, record, index+1, true);
32820         this.syncRowHeights(index, index);
32821         this.layout();
32822         this.fireEvent("rowupdated", this, index, record);
32823     },
32824
32825     onAdd : function(ds, records, index){
32826         this.insertRows(ds, index, index + (records.length-1));
32827     },
32828
32829     onRemove : function(ds, record, index, isUpdate){
32830         if(isUpdate !== true){
32831             this.fireEvent("beforerowremoved", this, index, record);
32832         }
32833         var bt = this.getBodyTable(), lt = this.getLockedTable();
32834         if(bt.rows[index]){
32835             bt.firstChild.removeChild(bt.rows[index]);
32836         }
32837         if(lt.rows[index]){
32838             lt.firstChild.removeChild(lt.rows[index]);
32839         }
32840         if(isUpdate !== true){
32841             this.stripeRows(index);
32842             this.syncRowHeights(index, index);
32843             this.layout();
32844             this.fireEvent("rowremoved", this, index, record);
32845         }
32846     },
32847
32848     onLoad : function(){
32849         this.scrollToTop();
32850     },
32851
32852     /**
32853      * Scrolls the grid to the top
32854      */
32855     scrollToTop : function(){
32856         if(this.scroller){
32857             this.scroller.dom.scrollTop = 0;
32858             this.syncScroll();
32859         }
32860     },
32861
32862     /**
32863      * Gets a panel in the header of the grid that can be used for toolbars etc.
32864      * After modifying the contents of this panel a call to grid.autoSize() may be
32865      * required to register any changes in size.
32866      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32867      * @return Roo.Element
32868      */
32869     getHeaderPanel : function(doShow){
32870         if(doShow){
32871             this.headerPanel.show();
32872         }
32873         return this.headerPanel;
32874         },
32875
32876         /**
32877      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32878      * After modifying the contents of this panel a call to grid.autoSize() may be
32879      * required to register any changes in size.
32880      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32881      * @return Roo.Element
32882      */
32883     getFooterPanel : function(doShow){
32884         if(doShow){
32885             this.footerPanel.show();
32886         }
32887         return this.footerPanel;
32888         },
32889
32890         initElements : function(){
32891             var E = Roo.Element;
32892             var el = this.grid.getGridEl().dom.firstChild;
32893             var cs = el.childNodes;
32894
32895             this.el = new E(el);
32896             this.headerPanel = new E(el.firstChild);
32897             this.headerPanel.enableDisplayMode("block");
32898
32899         this.scroller = new E(cs[1]);
32900             this.scrollSizer = new E(this.scroller.dom.firstChild);
32901
32902             this.lockedWrap = new E(cs[2]);
32903             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
32904             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
32905
32906             this.mainWrap = new E(cs[3]);
32907             this.mainHd = new E(this.mainWrap.dom.firstChild);
32908             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
32909
32910             this.footerPanel = new E(cs[4]);
32911             this.footerPanel.enableDisplayMode("block");
32912
32913         this.focusEl = new E(cs[5]);
32914         this.focusEl.swallowEvent("click", true);
32915         this.resizeProxy = new E(cs[6]);
32916
32917             this.headerSelector = String.format(
32918                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
32919                this.lockedHd.id, this.mainHd.id
32920             );
32921
32922             this.splitterSelector = String.format(
32923                '#{0} div.x-grid-split, #{1} div.x-grid-split',
32924                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
32925             );
32926     },
32927     idToCssName : function(s)
32928     {
32929         return s.replace(/[^a-z0-9]+/ig, '-');
32930     },
32931
32932         getHeaderCell : function(index){
32933             return Roo.DomQuery.select(this.headerSelector)[index];
32934         },
32935
32936         getHeaderCellMeasure : function(index){
32937             return this.getHeaderCell(index).firstChild;
32938         },
32939
32940         getHeaderCellText : function(index){
32941             return this.getHeaderCell(index).firstChild.firstChild;
32942         },
32943
32944         getLockedTable : function(){
32945             return this.lockedBody.dom.firstChild;
32946         },
32947
32948         getBodyTable : function(){
32949             return this.mainBody.dom.firstChild;
32950         },
32951
32952         getLockedRow : function(index){
32953             return this.getLockedTable().rows[index];
32954         },
32955
32956         getRow : function(index){
32957             return this.getBodyTable().rows[index];
32958         },
32959
32960         getRowComposite : function(index){
32961             if(!this.rowEl){
32962                 this.rowEl = new Roo.CompositeElementLite();
32963             }
32964         var els = [], lrow, mrow;
32965         if(lrow = this.getLockedRow(index)){
32966             els.push(lrow);
32967         }
32968         if(mrow = this.getRow(index)){
32969             els.push(mrow);
32970         }
32971         this.rowEl.elements = els;
32972             return this.rowEl;
32973         },
32974
32975         getCell : function(rowIndex, colIndex){
32976             var locked = this.cm.getLockedCount();
32977             var source;
32978             if(colIndex < locked){
32979                 source = this.lockedBody.dom.firstChild;
32980             }else{
32981                 source = this.mainBody.dom.firstChild;
32982                 colIndex -= locked;
32983             }
32984         return source.rows[rowIndex].childNodes[colIndex];
32985         },
32986
32987         getCellText : function(rowIndex, colIndex){
32988             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
32989         },
32990
32991         getCellBox : function(cell){
32992             var b = this.fly(cell).getBox();
32993         if(Roo.isOpera){ // opera fails to report the Y
32994             b.y = cell.offsetTop + this.mainBody.getY();
32995         }
32996         return b;
32997     },
32998
32999     getCellIndex : function(cell){
33000         var id = String(cell.className).match(this.cellRE);
33001         if(id){
33002             return parseInt(id[1], 10);
33003         }
33004         return 0;
33005     },
33006
33007     findHeaderIndex : function(n){
33008         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33009         return r ? this.getCellIndex(r) : false;
33010     },
33011
33012     findHeaderCell : function(n){
33013         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33014         return r ? r : false;
33015     },
33016
33017     findRowIndex : function(n){
33018         if(!n){
33019             return false;
33020         }
33021         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33022         return r ? r.rowIndex : false;
33023     },
33024
33025     findCellIndex : function(node){
33026         var stop = this.el.dom;
33027         while(node && node != stop){
33028             if(this.findRE.test(node.className)){
33029                 return this.getCellIndex(node);
33030             }
33031             node = node.parentNode;
33032         }
33033         return false;
33034     },
33035
33036     getColumnId : function(index){
33037             return this.cm.getColumnId(index);
33038         },
33039
33040         getSplitters : function(){
33041             if(this.splitterSelector){
33042                return Roo.DomQuery.select(this.splitterSelector);
33043             }else{
33044                 return null;
33045             }
33046         },
33047
33048         getSplitter : function(index){
33049             return this.getSplitters()[index];
33050         },
33051
33052     onRowOver : function(e, t){
33053         var row;
33054         if((row = this.findRowIndex(t)) !== false){
33055             this.getRowComposite(row).addClass("x-grid-row-over");
33056         }
33057     },
33058
33059     onRowOut : function(e, t){
33060         var row;
33061         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33062             this.getRowComposite(row).removeClass("x-grid-row-over");
33063         }
33064     },
33065
33066     renderHeaders : function(){
33067             var cm = this.cm;
33068         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33069         var cb = [], lb = [], sb = [], lsb = [], p = {};
33070         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33071             p.cellId = "x-grid-hd-0-" + i;
33072             p.splitId = "x-grid-csplit-0-" + i;
33073             p.id = cm.getColumnId(i);
33074             p.title = cm.getColumnTooltip(i) || "";
33075             p.value = cm.getColumnHeader(i) || "";
33076             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33077             if(!cm.isLocked(i)){
33078                 cb[cb.length] = ct.apply(p);
33079                 sb[sb.length] = st.apply(p);
33080             }else{
33081                 lb[lb.length] = ct.apply(p);
33082                 lsb[lsb.length] = st.apply(p);
33083             }
33084         }
33085         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33086                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33087         },
33088
33089         updateHeaders : function(){
33090         var html = this.renderHeaders();
33091         this.lockedHd.update(html[0]);
33092         this.mainHd.update(html[1]);
33093     },
33094
33095     /**
33096      * Focuses the specified row.
33097      * @param {Number} row The row index
33098      */
33099     focusRow : function(row){
33100         var x = this.scroller.dom.scrollLeft;
33101         this.focusCell(row, 0, false);
33102         this.scroller.dom.scrollLeft = x;
33103     },
33104
33105     /**
33106      * Focuses the specified cell.
33107      * @param {Number} row The row index
33108      * @param {Number} col The column index
33109      * @param {Boolean} hscroll false to disable horizontal scrolling
33110      */
33111     focusCell : function(row, col, hscroll){
33112         var el = this.ensureVisible(row, col, hscroll);
33113         this.focusEl.alignTo(el, "tl-tl");
33114         if(Roo.isGecko){
33115             this.focusEl.focus();
33116         }else{
33117             this.focusEl.focus.defer(1, this.focusEl);
33118         }
33119     },
33120
33121     /**
33122      * Scrolls the specified cell into view
33123      * @param {Number} row The row index
33124      * @param {Number} col The column index
33125      * @param {Boolean} hscroll false to disable horizontal scrolling
33126      */
33127     ensureVisible : function(row, col, hscroll){
33128         if(typeof row != "number"){
33129             row = row.rowIndex;
33130         }
33131         if(row < 0 && row >= this.ds.getCount()){
33132             return;
33133         }
33134         col = (col !== undefined ? col : 0);
33135         var cm = this.grid.colModel;
33136         while(cm.isHidden(col)){
33137             col++;
33138         }
33139
33140         var el = this.getCell(row, col);
33141         if(!el){
33142             return;
33143         }
33144         var c = this.scroller.dom;
33145
33146         var ctop = parseInt(el.offsetTop, 10);
33147         var cleft = parseInt(el.offsetLeft, 10);
33148         var cbot = ctop + el.offsetHeight;
33149         var cright = cleft + el.offsetWidth;
33150
33151         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33152         var stop = parseInt(c.scrollTop, 10);
33153         var sleft = parseInt(c.scrollLeft, 10);
33154         var sbot = stop + ch;
33155         var sright = sleft + c.clientWidth;
33156
33157         if(ctop < stop){
33158                 c.scrollTop = ctop;
33159         }else if(cbot > sbot){
33160             c.scrollTop = cbot-ch;
33161         }
33162
33163         if(hscroll !== false){
33164             if(cleft < sleft){
33165                 c.scrollLeft = cleft;
33166             }else if(cright > sright){
33167                 c.scrollLeft = cright-c.clientWidth;
33168             }
33169         }
33170         return el;
33171     },
33172
33173     updateColumns : function(){
33174         this.grid.stopEditing();
33175         var cm = this.grid.colModel, colIds = this.getColumnIds();
33176         //var totalWidth = cm.getTotalWidth();
33177         var pos = 0;
33178         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33179             //if(cm.isHidden(i)) continue;
33180             var w = cm.getColumnWidth(i);
33181             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33182             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33183         }
33184         this.updateSplitters();
33185     },
33186
33187     generateRules : function(cm){
33188         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33189         Roo.util.CSS.removeStyleSheet(rulesId);
33190         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33191             var cid = cm.getColumnId(i);
33192             var align = '';
33193             if(cm.config[i].align){
33194                 align = 'text-align:'+cm.config[i].align+';';
33195             }
33196             var hidden = '';
33197             if(cm.isHidden(i)){
33198                 hidden = 'display:none;';
33199             }
33200             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33201             ruleBuf.push(
33202                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33203                     this.hdSelector, cid, " {\n", align, width, "}\n",
33204                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33205                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33206         }
33207         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33208     },
33209
33210     updateSplitters : function(){
33211         var cm = this.cm, s = this.getSplitters();
33212         if(s){ // splitters not created yet
33213             var pos = 0, locked = true;
33214             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33215                 if(cm.isHidden(i)) continue;
33216                 var w = cm.getColumnWidth(i);
33217                 if(!cm.isLocked(i) && locked){
33218                     pos = 0;
33219                     locked = false;
33220                 }
33221                 pos += w;
33222                 s[i].style.left = (pos-this.splitOffset) + "px";
33223             }
33224         }
33225     },
33226
33227     handleHiddenChange : function(colModel, colIndex, hidden){
33228         if(hidden){
33229             this.hideColumn(colIndex);
33230         }else{
33231             this.unhideColumn(colIndex);
33232         }
33233     },
33234
33235     hideColumn : function(colIndex){
33236         var cid = this.getColumnId(colIndex);
33237         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33238         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33239         if(Roo.isSafari){
33240             this.updateHeaders();
33241         }
33242         this.updateSplitters();
33243         this.layout();
33244     },
33245
33246     unhideColumn : function(colIndex){
33247         var cid = this.getColumnId(colIndex);
33248         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33249         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33250
33251         if(Roo.isSafari){
33252             this.updateHeaders();
33253         }
33254         this.updateSplitters();
33255         this.layout();
33256     },
33257
33258     insertRows : function(dm, firstRow, lastRow, isUpdate){
33259         if(firstRow == 0 && lastRow == dm.getCount()-1){
33260             this.refresh();
33261         }else{
33262             if(!isUpdate){
33263                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33264             }
33265             var s = this.getScrollState();
33266             var markup = this.renderRows(firstRow, lastRow);
33267             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33268             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33269             this.restoreScroll(s);
33270             if(!isUpdate){
33271                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33272                 this.syncRowHeights(firstRow, lastRow);
33273                 this.stripeRows(firstRow);
33274                 this.layout();
33275             }
33276         }
33277     },
33278
33279     bufferRows : function(markup, target, index){
33280         var before = null, trows = target.rows, tbody = target.tBodies[0];
33281         if(index < trows.length){
33282             before = trows[index];
33283         }
33284         var b = document.createElement("div");
33285         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33286         var rows = b.firstChild.rows;
33287         for(var i = 0, len = rows.length; i < len; i++){
33288             if(before){
33289                 tbody.insertBefore(rows[0], before);
33290             }else{
33291                 tbody.appendChild(rows[0]);
33292             }
33293         }
33294         b.innerHTML = "";
33295         b = null;
33296     },
33297
33298     deleteRows : function(dm, firstRow, lastRow){
33299         if(dm.getRowCount()<1){
33300             this.fireEvent("beforerefresh", this);
33301             this.mainBody.update("");
33302             this.lockedBody.update("");
33303             this.fireEvent("refresh", this);
33304         }else{
33305             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33306             var bt = this.getBodyTable();
33307             var tbody = bt.firstChild;
33308             var rows = bt.rows;
33309             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33310                 tbody.removeChild(rows[firstRow]);
33311             }
33312             this.stripeRows(firstRow);
33313             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33314         }
33315     },
33316
33317     updateRows : function(dataSource, firstRow, lastRow){
33318         var s = this.getScrollState();
33319         this.refresh();
33320         this.restoreScroll(s);
33321     },
33322
33323     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33324         if(!noRefresh){
33325            this.refresh();
33326         }
33327         this.updateHeaderSortState();
33328     },
33329
33330     getScrollState : function(){
33331         var sb = this.scroller.dom;
33332         return {left: sb.scrollLeft, top: sb.scrollTop};
33333     },
33334
33335     stripeRows : function(startRow){
33336         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33337             return;
33338         }
33339         startRow = startRow || 0;
33340         var rows = this.getBodyTable().rows;
33341         var lrows = this.getLockedTable().rows;
33342         var cls = ' x-grid-row-alt ';
33343         for(var i = startRow, len = rows.length; i < len; i++){
33344             var row = rows[i], lrow = lrows[i];
33345             var isAlt = ((i+1) % 2 == 0);
33346             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33347             if(isAlt == hasAlt){
33348                 continue;
33349             }
33350             if(isAlt){
33351                 row.className += " x-grid-row-alt";
33352             }else{
33353                 row.className = row.className.replace("x-grid-row-alt", "");
33354             }
33355             if(lrow){
33356                 lrow.className = row.className;
33357             }
33358         }
33359     },
33360
33361     restoreScroll : function(state){
33362         var sb = this.scroller.dom;
33363         sb.scrollLeft = state.left;
33364         sb.scrollTop = state.top;
33365         this.syncScroll();
33366     },
33367
33368     syncScroll : function(){
33369         var sb = this.scroller.dom;
33370         var sh = this.mainHd.dom;
33371         var bs = this.mainBody.dom;
33372         var lv = this.lockedBody.dom;
33373         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33374         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33375     },
33376
33377     handleScroll : function(e){
33378         this.syncScroll();
33379         var sb = this.scroller.dom;
33380         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33381         e.stopEvent();
33382     },
33383
33384     handleWheel : function(e){
33385         var d = e.getWheelDelta();
33386         this.scroller.dom.scrollTop -= d*22;
33387         // set this here to prevent jumpy scrolling on large tables
33388         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33389         e.stopEvent();
33390     },
33391
33392     renderRows : function(startRow, endRow){
33393         // pull in all the crap needed to render rows
33394         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33395         var colCount = cm.getColumnCount();
33396
33397         if(ds.getCount() < 1){
33398             return ["", ""];
33399         }
33400
33401         // build a map for all the columns
33402         var cs = [];
33403         for(var i = 0; i < colCount; i++){
33404             var name = cm.getDataIndex(i);
33405             cs[i] = {
33406                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33407                 renderer : cm.getRenderer(i),
33408                 id : cm.getColumnId(i),
33409                 locked : cm.isLocked(i)
33410             };
33411         }
33412
33413         startRow = startRow || 0;
33414         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33415
33416         // records to render
33417         var rs = ds.getRange(startRow, endRow);
33418
33419         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33420     },
33421
33422     // As much as I hate to duplicate code, this was branched because FireFox really hates
33423     // [].join("") on strings. The performance difference was substantial enough to
33424     // branch this function
33425     doRender : Roo.isGecko ?
33426             function(cs, rs, ds, startRow, colCount, stripe){
33427                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33428                 // buffers
33429                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33430                 for(var j = 0, len = rs.length; j < len; j++){
33431                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33432                     for(var i = 0; i < colCount; i++){
33433                         c = cs[i];
33434                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33435                         p.id = c.id;
33436                         p.css = p.attr = "";
33437                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33438                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33439                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33440                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33441                         }
33442                         var markup = ct.apply(p);
33443                         if(!c.locked){
33444                             cb+= markup;
33445                         }else{
33446                             lcb+= markup;
33447                         }
33448                     }
33449                     var alt = [];
33450                     if(stripe && ((rowIndex+1) % 2 == 0)){
33451                         alt[0] = "x-grid-row-alt";
33452                     }
33453                     if(r.dirty){
33454                         alt[1] = " x-grid-dirty-row";
33455                     }
33456                     rp.cells = lcb;
33457                     if(this.getRowClass){
33458                         alt[2] = this.getRowClass(r, rowIndex);
33459                     }
33460                     rp.alt = alt.join(" ");
33461                     lbuf+= rt.apply(rp);
33462                     rp.cells = cb;
33463                     buf+=  rt.apply(rp);
33464                 }
33465                 return [lbuf, buf];
33466             } :
33467             function(cs, rs, ds, startRow, colCount, stripe){
33468                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33469                 // buffers
33470                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33471                 for(var j = 0, len = rs.length; j < len; j++){
33472                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33473                     for(var i = 0; i < colCount; i++){
33474                         c = cs[i];
33475                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33476                         p.id = c.id;
33477                         p.css = p.attr = "";
33478                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33479                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33480                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33481                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33482                         }
33483                         var markup = ct.apply(p);
33484                         if(!c.locked){
33485                             cb[cb.length] = markup;
33486                         }else{
33487                             lcb[lcb.length] = markup;
33488                         }
33489                     }
33490                     var alt = [];
33491                     if(stripe && ((rowIndex+1) % 2 == 0)){
33492                         alt[0] = "x-grid-row-alt";
33493                     }
33494                     if(r.dirty){
33495                         alt[1] = " x-grid-dirty-row";
33496                     }
33497                     rp.cells = lcb;
33498                     if(this.getRowClass){
33499                         alt[2] = this.getRowClass(r, rowIndex);
33500                     }
33501                     rp.alt = alt.join(" ");
33502                     rp.cells = lcb.join("");
33503                     lbuf[lbuf.length] = rt.apply(rp);
33504                     rp.cells = cb.join("");
33505                     buf[buf.length] =  rt.apply(rp);
33506                 }
33507                 return [lbuf.join(""), buf.join("")];
33508             },
33509
33510     renderBody : function(){
33511         var markup = this.renderRows();
33512         var bt = this.templates.body;
33513         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33514     },
33515
33516     /**
33517      * Refreshes the grid
33518      * @param {Boolean} headersToo
33519      */
33520     refresh : function(headersToo){
33521         this.fireEvent("beforerefresh", this);
33522         this.grid.stopEditing();
33523         var result = this.renderBody();
33524         this.lockedBody.update(result[0]);
33525         this.mainBody.update(result[1]);
33526         if(headersToo === true){
33527             this.updateHeaders();
33528             this.updateColumns();
33529             this.updateSplitters();
33530             this.updateHeaderSortState();
33531         }
33532         this.syncRowHeights();
33533         this.layout();
33534         this.fireEvent("refresh", this);
33535     },
33536
33537     handleColumnMove : function(cm, oldIndex, newIndex){
33538         this.indexMap = null;
33539         var s = this.getScrollState();
33540         this.refresh(true);
33541         this.restoreScroll(s);
33542         this.afterMove(newIndex);
33543     },
33544
33545     afterMove : function(colIndex){
33546         if(this.enableMoveAnim && Roo.enableFx){
33547             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33548         }
33549     },
33550
33551     updateCell : function(dm, rowIndex, dataIndex){
33552         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33553         if(typeof colIndex == "undefined"){ // not present in grid
33554             return;
33555         }
33556         var cm = this.grid.colModel;
33557         var cell = this.getCell(rowIndex, colIndex);
33558         var cellText = this.getCellText(rowIndex, colIndex);
33559
33560         var p = {
33561             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33562             id : cm.getColumnId(colIndex),
33563             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33564         };
33565         var renderer = cm.getRenderer(colIndex);
33566         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33567         if(typeof val == "undefined" || val === "") val = "&#160;";
33568         cellText.innerHTML = val;
33569         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33570         this.syncRowHeights(rowIndex, rowIndex);
33571     },
33572
33573     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33574         var maxWidth = 0;
33575         if(this.grid.autoSizeHeaders){
33576             var h = this.getHeaderCellMeasure(colIndex);
33577             maxWidth = Math.max(maxWidth, h.scrollWidth);
33578         }
33579         var tb, index;
33580         if(this.cm.isLocked(colIndex)){
33581             tb = this.getLockedTable();
33582             index = colIndex;
33583         }else{
33584             tb = this.getBodyTable();
33585             index = colIndex - this.cm.getLockedCount();
33586         }
33587         if(tb && tb.rows){
33588             var rows = tb.rows;
33589             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33590             for(var i = 0; i < stopIndex; i++){
33591                 var cell = rows[i].childNodes[index].firstChild;
33592                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33593             }
33594         }
33595         return maxWidth + /*margin for error in IE*/ 5;
33596     },
33597     /**
33598      * Autofit a column to its content.
33599      * @param {Number} colIndex
33600      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33601      */
33602      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33603          if(this.cm.isHidden(colIndex)){
33604              return; // can't calc a hidden column
33605          }
33606         if(forceMinSize){
33607             var cid = this.cm.getColumnId(colIndex);
33608             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33609            if(this.grid.autoSizeHeaders){
33610                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33611            }
33612         }
33613         var newWidth = this.calcColumnWidth(colIndex);
33614         this.cm.setColumnWidth(colIndex,
33615             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33616         if(!suppressEvent){
33617             this.grid.fireEvent("columnresize", colIndex, newWidth);
33618         }
33619     },
33620
33621     /**
33622      * Autofits all columns to their content and then expands to fit any extra space in the grid
33623      */
33624      autoSizeColumns : function(){
33625         var cm = this.grid.colModel;
33626         var colCount = cm.getColumnCount();
33627         for(var i = 0; i < colCount; i++){
33628             this.autoSizeColumn(i, true, true);
33629         }
33630         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33631             this.fitColumns();
33632         }else{
33633             this.updateColumns();
33634             this.layout();
33635         }
33636     },
33637
33638     /**
33639      * Autofits all columns to the grid's width proportionate with their current size
33640      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33641      */
33642     fitColumns : function(reserveScrollSpace){
33643         var cm = this.grid.colModel;
33644         var colCount = cm.getColumnCount();
33645         var cols = [];
33646         var width = 0;
33647         var i, w;
33648         for (i = 0; i < colCount; i++){
33649             if(!cm.isHidden(i) && !cm.isFixed(i)){
33650                 w = cm.getColumnWidth(i);
33651                 cols.push(i);
33652                 cols.push(w);
33653                 width += w;
33654             }
33655         }
33656         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33657         if(reserveScrollSpace){
33658             avail -= 17;
33659         }
33660         var frac = (avail - cm.getTotalWidth())/width;
33661         while (cols.length){
33662             w = cols.pop();
33663             i = cols.pop();
33664             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33665         }
33666         this.updateColumns();
33667         this.layout();
33668     },
33669
33670     onRowSelect : function(rowIndex){
33671         var row = this.getRowComposite(rowIndex);
33672         row.addClass("x-grid-row-selected");
33673     },
33674
33675     onRowDeselect : function(rowIndex){
33676         var row = this.getRowComposite(rowIndex);
33677         row.removeClass("x-grid-row-selected");
33678     },
33679
33680     onCellSelect : function(row, col){
33681         var cell = this.getCell(row, col);
33682         if(cell){
33683             Roo.fly(cell).addClass("x-grid-cell-selected");
33684         }
33685     },
33686
33687     onCellDeselect : function(row, col){
33688         var cell = this.getCell(row, col);
33689         if(cell){
33690             Roo.fly(cell).removeClass("x-grid-cell-selected");
33691         }
33692     },
33693
33694     updateHeaderSortState : function(){
33695         var state = this.ds.getSortState();
33696         if(!state){
33697             return;
33698         }
33699         this.sortState = state;
33700         var sortColumn = this.cm.findColumnIndex(state.field);
33701         if(sortColumn != -1){
33702             var sortDir = state.direction;
33703             var sc = this.sortClasses;
33704             var hds = this.el.select(this.headerSelector).removeClass(sc);
33705             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33706         }
33707     },
33708
33709     handleHeaderClick : function(g, index){
33710         if(this.headersDisabled){
33711             return;
33712         }
33713         var dm = g.dataSource, cm = g.colModel;
33714             if(!cm.isSortable(index)){
33715             return;
33716         }
33717             g.stopEditing();
33718         dm.sort(cm.getDataIndex(index));
33719     },
33720
33721
33722     destroy : function(){
33723         if(this.colMenu){
33724             this.colMenu.removeAll();
33725             Roo.menu.MenuMgr.unregister(this.colMenu);
33726             this.colMenu.getEl().remove();
33727             delete this.colMenu;
33728         }
33729         if(this.hmenu){
33730             this.hmenu.removeAll();
33731             Roo.menu.MenuMgr.unregister(this.hmenu);
33732             this.hmenu.getEl().remove();
33733             delete this.hmenu;
33734         }
33735         if(this.grid.enableColumnMove){
33736             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33737             if(dds){
33738                 for(var dd in dds){
33739                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33740                         var elid = dds[dd].dragElId;
33741                         dds[dd].unreg();
33742                         Roo.get(elid).remove();
33743                     } else if(dds[dd].config.isTarget){
33744                         dds[dd].proxyTop.remove();
33745                         dds[dd].proxyBottom.remove();
33746                         dds[dd].unreg();
33747                     }
33748                     if(Roo.dd.DDM.locationCache[dd]){
33749                         delete Roo.dd.DDM.locationCache[dd];
33750                     }
33751                 }
33752                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33753             }
33754         }
33755         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33756         this.bind(null, null);
33757         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33758     },
33759
33760     handleLockChange : function(){
33761         this.refresh(true);
33762     },
33763
33764     onDenyColumnLock : function(){
33765
33766     },
33767
33768     onDenyColumnHide : function(){
33769
33770     },
33771
33772     handleHdMenuClick : function(item){
33773         var index = this.hdCtxIndex;
33774         var cm = this.cm, ds = this.ds;
33775         switch(item.id){
33776             case "asc":
33777                 ds.sort(cm.getDataIndex(index), "ASC");
33778                 break;
33779             case "desc":
33780                 ds.sort(cm.getDataIndex(index), "DESC");
33781                 break;
33782             case "lock":
33783                 var lc = cm.getLockedCount();
33784                 if(cm.getColumnCount(true) <= lc+1){
33785                     this.onDenyColumnLock();
33786                     return;
33787                 }
33788                 if(lc != index){
33789                     cm.setLocked(index, true, true);
33790                     cm.moveColumn(index, lc);
33791                     this.grid.fireEvent("columnmove", index, lc);
33792                 }else{
33793                     cm.setLocked(index, true);
33794                 }
33795             break;
33796             case "unlock":
33797                 var lc = cm.getLockedCount();
33798                 if((lc-1) != index){
33799                     cm.setLocked(index, false, true);
33800                     cm.moveColumn(index, lc-1);
33801                     this.grid.fireEvent("columnmove", index, lc-1);
33802                 }else{
33803                     cm.setLocked(index, false);
33804                 }
33805             break;
33806             default:
33807                 index = cm.getIndexById(item.id.substr(4));
33808                 if(index != -1){
33809                     if(item.checked && cm.getColumnCount(true) <= 1){
33810                         this.onDenyColumnHide();
33811                         return false;
33812                     }
33813                     cm.setHidden(index, item.checked);
33814                 }
33815         }
33816         return true;
33817     },
33818
33819     beforeColMenuShow : function(){
33820         var cm = this.cm,  colCount = cm.getColumnCount();
33821         this.colMenu.removeAll();
33822         for(var i = 0; i < colCount; i++){
33823             this.colMenu.add(new Roo.menu.CheckItem({
33824                 id: "col-"+cm.getColumnId(i),
33825                 text: cm.getColumnHeader(i),
33826                 checked: !cm.isHidden(i),
33827                 hideOnClick:false
33828             }));
33829         }
33830     },
33831
33832     handleHdCtx : function(g, index, e){
33833         e.stopEvent();
33834         var hd = this.getHeaderCell(index);
33835         this.hdCtxIndex = index;
33836         var ms = this.hmenu.items, cm = this.cm;
33837         ms.get("asc").setDisabled(!cm.isSortable(index));
33838         ms.get("desc").setDisabled(!cm.isSortable(index));
33839         if(this.grid.enableColLock !== false){
33840             ms.get("lock").setDisabled(cm.isLocked(index));
33841             ms.get("unlock").setDisabled(!cm.isLocked(index));
33842         }
33843         this.hmenu.show(hd, "tl-bl");
33844     },
33845
33846     handleHdOver : function(e){
33847         var hd = this.findHeaderCell(e.getTarget());
33848         if(hd && !this.headersDisabled){
33849             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33850                this.fly(hd).addClass("x-grid-hd-over");
33851             }
33852         }
33853     },
33854
33855     handleHdOut : function(e){
33856         var hd = this.findHeaderCell(e.getTarget());
33857         if(hd){
33858             this.fly(hd).removeClass("x-grid-hd-over");
33859         }
33860     },
33861
33862     handleSplitDblClick : function(e, t){
33863         var i = this.getCellIndex(t);
33864         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33865             this.autoSizeColumn(i, true);
33866             this.layout();
33867         }
33868     },
33869
33870     render : function(){
33871
33872         var cm = this.cm;
33873         var colCount = cm.getColumnCount();
33874
33875         if(this.grid.monitorWindowResize === true){
33876             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33877         }
33878         var header = this.renderHeaders();
33879         var body = this.templates.body.apply({rows:""});
33880         var html = this.templates.master.apply({
33881             lockedBody: body,
33882             body: body,
33883             lockedHeader: header[0],
33884             header: header[1]
33885         });
33886
33887         //this.updateColumns();
33888
33889         this.grid.getGridEl().dom.innerHTML = html;
33890
33891         this.initElements();
33892         
33893         // a kludge to fix the random scolling effect in webkit
33894         this.el.on("scroll", function() {
33895             this.el.dom.scrollTop=0; // hopefully not recursive..
33896         },this);
33897
33898         this.scroller.on("scroll", this.handleScroll, this);
33899         this.lockedBody.on("mousewheel", this.handleWheel, this);
33900         this.mainBody.on("mousewheel", this.handleWheel, this);
33901
33902         this.mainHd.on("mouseover", this.handleHdOver, this);
33903         this.mainHd.on("mouseout", this.handleHdOut, this);
33904         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
33905                 {delegate: "."+this.splitClass});
33906
33907         this.lockedHd.on("mouseover", this.handleHdOver, this);
33908         this.lockedHd.on("mouseout", this.handleHdOut, this);
33909         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
33910                 {delegate: "."+this.splitClass});
33911
33912         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
33913             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33914         }
33915
33916         this.updateSplitters();
33917
33918         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
33919             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33920             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
33921         }
33922
33923         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
33924             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
33925             this.hmenu.add(
33926                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
33927                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
33928             );
33929             if(this.grid.enableColLock !== false){
33930                 this.hmenu.add('-',
33931                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
33932                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
33933                 );
33934             }
33935             if(this.grid.enableColumnHide !== false){
33936
33937                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
33938                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
33939                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
33940
33941                 this.hmenu.add('-',
33942                     {id:"columns", text: this.columnsText, menu: this.colMenu}
33943                 );
33944             }
33945             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
33946
33947             this.grid.on("headercontextmenu", this.handleHdCtx, this);
33948         }
33949
33950         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
33951             this.dd = new Roo.grid.GridDragZone(this.grid, {
33952                 ddGroup : this.grid.ddGroup || 'GridDD'
33953             });
33954         }
33955
33956         /*
33957         for(var i = 0; i < colCount; i++){
33958             if(cm.isHidden(i)){
33959                 this.hideColumn(i);
33960             }
33961             if(cm.config[i].align){
33962                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
33963                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
33964             }
33965         }*/
33966         
33967         this.updateHeaderSortState();
33968
33969         this.beforeInitialResize();
33970         this.layout(true);
33971
33972         // two part rendering gives faster view to the user
33973         this.renderPhase2.defer(1, this);
33974     },
33975
33976     renderPhase2 : function(){
33977         // render the rows now
33978         this.refresh();
33979         if(this.grid.autoSizeColumns){
33980             this.autoSizeColumns();
33981         }
33982     },
33983
33984     beforeInitialResize : function(){
33985
33986     },
33987
33988     onColumnSplitterMoved : function(i, w){
33989         this.userResized = true;
33990         var cm = this.grid.colModel;
33991         cm.setColumnWidth(i, w, true);
33992         var cid = cm.getColumnId(i);
33993         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33994         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
33995         this.updateSplitters();
33996         this.layout();
33997         this.grid.fireEvent("columnresize", i, w);
33998     },
33999
34000     syncRowHeights : function(startIndex, endIndex){
34001         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34002             startIndex = startIndex || 0;
34003             var mrows = this.getBodyTable().rows;
34004             var lrows = this.getLockedTable().rows;
34005             var len = mrows.length-1;
34006             endIndex = Math.min(endIndex || len, len);
34007             for(var i = startIndex; i <= endIndex; i++){
34008                 var m = mrows[i], l = lrows[i];
34009                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34010                 m.style.height = l.style.height = h + "px";
34011             }
34012         }
34013     },
34014
34015     layout : function(initialRender, is2ndPass){
34016         var g = this.grid;
34017         var auto = g.autoHeight;
34018         var scrollOffset = 16;
34019         var c = g.getGridEl(), cm = this.cm,
34020                 expandCol = g.autoExpandColumn,
34021                 gv = this;
34022         //c.beginMeasure();
34023
34024         if(!c.dom.offsetWidth){ // display:none?
34025             if(initialRender){
34026                 this.lockedWrap.show();
34027                 this.mainWrap.show();
34028             }
34029             return;
34030         }
34031
34032         var hasLock = this.cm.isLocked(0);
34033
34034         var tbh = this.headerPanel.getHeight();
34035         var bbh = this.footerPanel.getHeight();
34036
34037         if(auto){
34038             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34039             var newHeight = ch + c.getBorderWidth("tb");
34040             if(g.maxHeight){
34041                 newHeight = Math.min(g.maxHeight, newHeight);
34042             }
34043             c.setHeight(newHeight);
34044         }
34045
34046         if(g.autoWidth){
34047             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34048         }
34049
34050         var s = this.scroller;
34051
34052         var csize = c.getSize(true);
34053
34054         this.el.setSize(csize.width, csize.height);
34055
34056         this.headerPanel.setWidth(csize.width);
34057         this.footerPanel.setWidth(csize.width);
34058
34059         var hdHeight = this.mainHd.getHeight();
34060         var vw = csize.width;
34061         var vh = csize.height - (tbh + bbh);
34062
34063         s.setSize(vw, vh);
34064
34065         var bt = this.getBodyTable();
34066         var ltWidth = hasLock ?
34067                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34068
34069         var scrollHeight = bt.offsetHeight;
34070         var scrollWidth = ltWidth + bt.offsetWidth;
34071         var vscroll = false, hscroll = false;
34072
34073         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34074
34075         var lw = this.lockedWrap, mw = this.mainWrap;
34076         var lb = this.lockedBody, mb = this.mainBody;
34077
34078         setTimeout(function(){
34079             var t = s.dom.offsetTop;
34080             var w = s.dom.clientWidth,
34081                 h = s.dom.clientHeight;
34082
34083             lw.setTop(t);
34084             lw.setSize(ltWidth, h);
34085
34086             mw.setLeftTop(ltWidth, t);
34087             mw.setSize(w-ltWidth, h);
34088
34089             lb.setHeight(h-hdHeight);
34090             mb.setHeight(h-hdHeight);
34091
34092             if(is2ndPass !== true && !gv.userResized && expandCol){
34093                 // high speed resize without full column calculation
34094                 
34095                 var ci = cm.getIndexById(expandCol);
34096                 if (ci < 0) {
34097                     ci = cm.findColumnIndex(expandCol);
34098                 }
34099                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34100                 var expandId = cm.getColumnId(ci);
34101                 var  tw = cm.getTotalWidth(false);
34102                 var currentWidth = cm.getColumnWidth(ci);
34103                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34104                 if(currentWidth != cw){
34105                     cm.setColumnWidth(ci, cw, true);
34106                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34107                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34108                     gv.updateSplitters();
34109                     gv.layout(false, true);
34110                 }
34111             }
34112
34113             if(initialRender){
34114                 lw.show();
34115                 mw.show();
34116             }
34117             //c.endMeasure();
34118         }, 10);
34119     },
34120
34121     onWindowResize : function(){
34122         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34123             return;
34124         }
34125         this.layout();
34126     },
34127
34128     appendFooter : function(parentEl){
34129         return null;
34130     },
34131
34132     sortAscText : "Sort Ascending",
34133     sortDescText : "Sort Descending",
34134     lockText : "Lock Column",
34135     unlockText : "Unlock Column",
34136     columnsText : "Columns"
34137 });
34138
34139
34140 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34141     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34142     this.proxy.el.addClass('x-grid3-col-dd');
34143 };
34144
34145 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34146     handleMouseDown : function(e){
34147
34148     },
34149
34150     callHandleMouseDown : function(e){
34151         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34152     }
34153 });
34154 /*
34155  * Based on:
34156  * Ext JS Library 1.1.1
34157  * Copyright(c) 2006-2007, Ext JS, LLC.
34158  *
34159  * Originally Released Under LGPL - original licence link has changed is not relivant.
34160  *
34161  * Fork - LGPL
34162  * <script type="text/javascript">
34163  */
34164  
34165 // private
34166 // This is a support class used internally by the Grid components
34167 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34168     this.grid = grid;
34169     this.view = grid.getView();
34170     this.proxy = this.view.resizeProxy;
34171     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34172         "gridSplitters" + this.grid.getGridEl().id, {
34173         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34174     });
34175     this.setHandleElId(Roo.id(hd));
34176     this.setOuterHandleElId(Roo.id(hd2));
34177     this.scroll = false;
34178 };
34179 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34180     fly: Roo.Element.fly,
34181
34182     b4StartDrag : function(x, y){
34183         this.view.headersDisabled = true;
34184         this.proxy.setHeight(this.view.mainWrap.getHeight());
34185         var w = this.cm.getColumnWidth(this.cellIndex);
34186         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34187         this.resetConstraints();
34188         this.setXConstraint(minw, 1000);
34189         this.setYConstraint(0, 0);
34190         this.minX = x - minw;
34191         this.maxX = x + 1000;
34192         this.startPos = x;
34193         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34194     },
34195
34196
34197     handleMouseDown : function(e){
34198         ev = Roo.EventObject.setEvent(e);
34199         var t = this.fly(ev.getTarget());
34200         if(t.hasClass("x-grid-split")){
34201             this.cellIndex = this.view.getCellIndex(t.dom);
34202             this.split = t.dom;
34203             this.cm = this.grid.colModel;
34204             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34205                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34206             }
34207         }
34208     },
34209
34210     endDrag : function(e){
34211         this.view.headersDisabled = false;
34212         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34213         var diff = endX - this.startPos;
34214         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34215     },
34216
34217     autoOffset : function(){
34218         this.setDelta(0,0);
34219     }
34220 });/*
34221  * Based on:
34222  * Ext JS Library 1.1.1
34223  * Copyright(c) 2006-2007, Ext JS, LLC.
34224  *
34225  * Originally Released Under LGPL - original licence link has changed is not relivant.
34226  *
34227  * Fork - LGPL
34228  * <script type="text/javascript">
34229  */
34230  
34231 // private
34232 // This is a support class used internally by the Grid components
34233 Roo.grid.GridDragZone = function(grid, config){
34234     this.view = grid.getView();
34235     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34236     if(this.view.lockedBody){
34237         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34238         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34239     }
34240     this.scroll = false;
34241     this.grid = grid;
34242     this.ddel = document.createElement('div');
34243     this.ddel.className = 'x-grid-dd-wrap';
34244 };
34245
34246 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34247     ddGroup : "GridDD",
34248
34249     getDragData : function(e){
34250         var t = Roo.lib.Event.getTarget(e);
34251         var rowIndex = this.view.findRowIndex(t);
34252         if(rowIndex !== false){
34253             var sm = this.grid.selModel;
34254             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34255               //  sm.mouseDown(e, t);
34256             //}
34257             if (e.hasModifier()){
34258                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34259             }
34260             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34261         }
34262         return false;
34263     },
34264
34265     onInitDrag : function(e){
34266         var data = this.dragData;
34267         this.ddel.innerHTML = this.grid.getDragDropText();
34268         this.proxy.update(this.ddel);
34269         // fire start drag?
34270     },
34271
34272     afterRepair : function(){
34273         this.dragging = false;
34274     },
34275
34276     getRepairXY : function(e, data){
34277         return false;
34278     },
34279
34280     onEndDrag : function(data, e){
34281         // fire end drag?
34282     },
34283
34284     onValidDrop : function(dd, e, id){
34285         // fire drag drop?
34286         this.hideProxy();
34287     },
34288
34289     beforeInvalidDrop : function(e, id){
34290
34291     }
34292 });/*
34293  * Based on:
34294  * Ext JS Library 1.1.1
34295  * Copyright(c) 2006-2007, Ext JS, LLC.
34296  *
34297  * Originally Released Under LGPL - original licence link has changed is not relivant.
34298  *
34299  * Fork - LGPL
34300  * <script type="text/javascript">
34301  */
34302  
34303
34304 /**
34305  * @class Roo.grid.ColumnModel
34306  * @extends Roo.util.Observable
34307  * This is the default implementation of a ColumnModel used by the Grid. It defines
34308  * the columns in the grid.
34309  * <br>Usage:<br>
34310  <pre><code>
34311  var colModel = new Roo.grid.ColumnModel([
34312         {header: "Ticker", width: 60, sortable: true, locked: true},
34313         {header: "Company Name", width: 150, sortable: true},
34314         {header: "Market Cap.", width: 100, sortable: true},
34315         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34316         {header: "Employees", width: 100, sortable: true, resizable: false}
34317  ]);
34318  </code></pre>
34319  * <p>
34320  
34321  * The config options listed for this class are options which may appear in each
34322  * individual column definition.
34323  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34324  * @constructor
34325  * @param {Object} config An Array of column config objects. See this class's
34326  * config objects for details.
34327 */
34328 Roo.grid.ColumnModel = function(config){
34329         /**
34330      * The config passed into the constructor
34331      */
34332     this.config = config;
34333     this.lookup = {};
34334
34335     // if no id, create one
34336     // if the column does not have a dataIndex mapping,
34337     // map it to the order it is in the config
34338     for(var i = 0, len = config.length; i < len; i++){
34339         var c = config[i];
34340         if(typeof c.dataIndex == "undefined"){
34341             c.dataIndex = i;
34342         }
34343         if(typeof c.renderer == "string"){
34344             c.renderer = Roo.util.Format[c.renderer];
34345         }
34346         if(typeof c.id == "undefined"){
34347             c.id = Roo.id();
34348         }
34349         if(c.editor && c.editor.xtype){
34350             c.editor  = Roo.factory(c.editor, Roo.grid);
34351         }
34352         if(c.editor && c.editor.isFormField){
34353             c.editor = new Roo.grid.GridEditor(c.editor);
34354         }
34355         this.lookup[c.id] = c;
34356     }
34357
34358     /**
34359      * The width of columns which have no width specified (defaults to 100)
34360      * @type Number
34361      */
34362     this.defaultWidth = 100;
34363
34364     /**
34365      * Default sortable of columns which have no sortable specified (defaults to false)
34366      * @type Boolean
34367      */
34368     this.defaultSortable = false;
34369
34370     this.addEvents({
34371         /**
34372              * @event widthchange
34373              * Fires when the width of a column changes.
34374              * @param {ColumnModel} this
34375              * @param {Number} columnIndex The column index
34376              * @param {Number} newWidth The new width
34377              */
34378             "widthchange": true,
34379         /**
34380              * @event headerchange
34381              * Fires when the text of a header changes.
34382              * @param {ColumnModel} this
34383              * @param {Number} columnIndex The column index
34384              * @param {Number} newText The new header text
34385              */
34386             "headerchange": true,
34387         /**
34388              * @event hiddenchange
34389              * Fires when a column is hidden or "unhidden".
34390              * @param {ColumnModel} this
34391              * @param {Number} columnIndex The column index
34392              * @param {Boolean} hidden true if hidden, false otherwise
34393              */
34394             "hiddenchange": true,
34395             /**
34396          * @event columnmoved
34397          * Fires when a column is moved.
34398          * @param {ColumnModel} this
34399          * @param {Number} oldIndex
34400          * @param {Number} newIndex
34401          */
34402         "columnmoved" : true,
34403         /**
34404          * @event columlockchange
34405          * Fires when a column's locked state is changed
34406          * @param {ColumnModel} this
34407          * @param {Number} colIndex
34408          * @param {Boolean} locked true if locked
34409          */
34410         "columnlockchange" : true
34411     });
34412     Roo.grid.ColumnModel.superclass.constructor.call(this);
34413 };
34414 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34415     /**
34416      * @cfg {String} header The header text to display in the Grid view.
34417      */
34418     /**
34419      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34420      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34421      * specified, the column's index is used as an index into the Record's data Array.
34422      */
34423     /**
34424      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34425      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34426      */
34427     /**
34428      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34429      * Defaults to the value of the {@link #defaultSortable} property.
34430      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34431      */
34432     /**
34433      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34434      */
34435     /**
34436      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34437      */
34438     /**
34439      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34440      */
34441     /**
34442      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34443      */
34444     /**
34445      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34446      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34447      * default renderer uses the raw data value.
34448      */
34449        /**
34450      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34451      */
34452     /**
34453      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34454      */
34455
34456     /**
34457      * Returns the id of the column at the specified index.
34458      * @param {Number} index The column index
34459      * @return {String} the id
34460      */
34461     getColumnId : function(index){
34462         return this.config[index].id;
34463     },
34464
34465     /**
34466      * Returns the column for a specified id.
34467      * @param {String} id The column id
34468      * @return {Object} the column
34469      */
34470     getColumnById : function(id){
34471         return this.lookup[id];
34472     },
34473
34474     
34475     /**
34476      * Returns the column for a specified dataIndex.
34477      * @param {String} dataIndex The column dataIndex
34478      * @return {Object|Boolean} the column or false if not found
34479      */
34480     getColumnByDataIndex: function(dataIndex){
34481         var index = this.findColumnIndex(dataIndex);
34482         return index > -1 ? this.config[index] : false;
34483     },
34484     
34485     /**
34486      * Returns the index for a specified column id.
34487      * @param {String} id The column id
34488      * @return {Number} the index, or -1 if not found
34489      */
34490     getIndexById : function(id){
34491         for(var i = 0, len = this.config.length; i < len; i++){
34492             if(this.config[i].id == id){
34493                 return i;
34494             }
34495         }
34496         return -1;
34497     },
34498     
34499     /**
34500      * Returns the index for a specified column dataIndex.
34501      * @param {String} dataIndex The column dataIndex
34502      * @return {Number} the index, or -1 if not found
34503      */
34504     
34505     findColumnIndex : function(dataIndex){
34506         for(var i = 0, len = this.config.length; i < len; i++){
34507             if(this.config[i].dataIndex == dataIndex){
34508                 return i;
34509             }
34510         }
34511         return -1;
34512     },
34513     
34514     
34515     moveColumn : function(oldIndex, newIndex){
34516         var c = this.config[oldIndex];
34517         this.config.splice(oldIndex, 1);
34518         this.config.splice(newIndex, 0, c);
34519         this.dataMap = null;
34520         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34521     },
34522
34523     isLocked : function(colIndex){
34524         return this.config[colIndex].locked === true;
34525     },
34526
34527     setLocked : function(colIndex, value, suppressEvent){
34528         if(this.isLocked(colIndex) == value){
34529             return;
34530         }
34531         this.config[colIndex].locked = value;
34532         if(!suppressEvent){
34533             this.fireEvent("columnlockchange", this, colIndex, value);
34534         }
34535     },
34536
34537     getTotalLockedWidth : function(){
34538         var totalWidth = 0;
34539         for(var i = 0; i < this.config.length; i++){
34540             if(this.isLocked(i) && !this.isHidden(i)){
34541                 this.totalWidth += this.getColumnWidth(i);
34542             }
34543         }
34544         return totalWidth;
34545     },
34546
34547     getLockedCount : function(){
34548         for(var i = 0, len = this.config.length; i < len; i++){
34549             if(!this.isLocked(i)){
34550                 return i;
34551             }
34552         }
34553     },
34554
34555     /**
34556      * Returns the number of columns.
34557      * @return {Number}
34558      */
34559     getColumnCount : function(visibleOnly){
34560         if(visibleOnly === true){
34561             var c = 0;
34562             for(var i = 0, len = this.config.length; i < len; i++){
34563                 if(!this.isHidden(i)){
34564                     c++;
34565                 }
34566             }
34567             return c;
34568         }
34569         return this.config.length;
34570     },
34571
34572     /**
34573      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34574      * @param {Function} fn
34575      * @param {Object} scope (optional)
34576      * @return {Array} result
34577      */
34578     getColumnsBy : function(fn, scope){
34579         var r = [];
34580         for(var i = 0, len = this.config.length; i < len; i++){
34581             var c = this.config[i];
34582             if(fn.call(scope||this, c, i) === true){
34583                 r[r.length] = c;
34584             }
34585         }
34586         return r;
34587     },
34588
34589     /**
34590      * Returns true if the specified column is sortable.
34591      * @param {Number} col The column index
34592      * @return {Boolean}
34593      */
34594     isSortable : function(col){
34595         if(typeof this.config[col].sortable == "undefined"){
34596             return this.defaultSortable;
34597         }
34598         return this.config[col].sortable;
34599     },
34600
34601     /**
34602      * Returns the rendering (formatting) function defined for the column.
34603      * @param {Number} col The column index.
34604      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34605      */
34606     getRenderer : function(col){
34607         if(!this.config[col].renderer){
34608             return Roo.grid.ColumnModel.defaultRenderer;
34609         }
34610         return this.config[col].renderer;
34611     },
34612
34613     /**
34614      * Sets the rendering (formatting) function for a column.
34615      * @param {Number} col The column index
34616      * @param {Function} fn The function to use to process the cell's raw data
34617      * to return HTML markup for the grid view. The render function is called with
34618      * the following parameters:<ul>
34619      * <li>Data value.</li>
34620      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34621      * <li>css A CSS style string to apply to the table cell.</li>
34622      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34623      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34624      * <li>Row index</li>
34625      * <li>Column index</li>
34626      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34627      */
34628     setRenderer : function(col, fn){
34629         this.config[col].renderer = fn;
34630     },
34631
34632     /**
34633      * Returns the width for the specified column.
34634      * @param {Number} col The column index
34635      * @return {Number}
34636      */
34637     getColumnWidth : function(col){
34638         return this.config[col].width || this.defaultWidth;
34639     },
34640
34641     /**
34642      * Sets the width for a column.
34643      * @param {Number} col The column index
34644      * @param {Number} width The new width
34645      */
34646     setColumnWidth : function(col, width, suppressEvent){
34647         this.config[col].width = width;
34648         this.totalWidth = null;
34649         if(!suppressEvent){
34650              this.fireEvent("widthchange", this, col, width);
34651         }
34652     },
34653
34654     /**
34655      * Returns the total width of all columns.
34656      * @param {Boolean} includeHidden True to include hidden column widths
34657      * @return {Number}
34658      */
34659     getTotalWidth : function(includeHidden){
34660         if(!this.totalWidth){
34661             this.totalWidth = 0;
34662             for(var i = 0, len = this.config.length; i < len; i++){
34663                 if(includeHidden || !this.isHidden(i)){
34664                     this.totalWidth += this.getColumnWidth(i);
34665                 }
34666             }
34667         }
34668         return this.totalWidth;
34669     },
34670
34671     /**
34672      * Returns the header for the specified column.
34673      * @param {Number} col The column index
34674      * @return {String}
34675      */
34676     getColumnHeader : function(col){
34677         return this.config[col].header;
34678     },
34679
34680     /**
34681      * Sets the header for a column.
34682      * @param {Number} col The column index
34683      * @param {String} header The new header
34684      */
34685     setColumnHeader : function(col, header){
34686         this.config[col].header = header;
34687         this.fireEvent("headerchange", this, col, header);
34688     },
34689
34690     /**
34691      * Returns the tooltip for the specified column.
34692      * @param {Number} col The column index
34693      * @return {String}
34694      */
34695     getColumnTooltip : function(col){
34696             return this.config[col].tooltip;
34697     },
34698     /**
34699      * Sets the tooltip for a column.
34700      * @param {Number} col The column index
34701      * @param {String} tooltip The new tooltip
34702      */
34703     setColumnTooltip : function(col, tooltip){
34704             this.config[col].tooltip = tooltip;
34705     },
34706
34707     /**
34708      * Returns the dataIndex for the specified column.
34709      * @param {Number} col The column index
34710      * @return {Number}
34711      */
34712     getDataIndex : function(col){
34713         return this.config[col].dataIndex;
34714     },
34715
34716     /**
34717      * Sets the dataIndex for a column.
34718      * @param {Number} col The column index
34719      * @param {Number} dataIndex The new dataIndex
34720      */
34721     setDataIndex : function(col, dataIndex){
34722         this.config[col].dataIndex = dataIndex;
34723     },
34724
34725     
34726     
34727     /**
34728      * Returns true if the cell is editable.
34729      * @param {Number} colIndex The column index
34730      * @param {Number} rowIndex The row index
34731      * @return {Boolean}
34732      */
34733     isCellEditable : function(colIndex, rowIndex){
34734         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34735     },
34736
34737     /**
34738      * Returns the editor defined for the cell/column.
34739      * return false or null to disable editing.
34740      * @param {Number} colIndex The column index
34741      * @param {Number} rowIndex The row index
34742      * @return {Object}
34743      */
34744     getCellEditor : function(colIndex, rowIndex){
34745         return this.config[colIndex].editor;
34746     },
34747
34748     /**
34749      * Sets if a column is editable.
34750      * @param {Number} col The column index
34751      * @param {Boolean} editable True if the column is editable
34752      */
34753     setEditable : function(col, editable){
34754         this.config[col].editable = editable;
34755     },
34756
34757
34758     /**
34759      * Returns true if the column is hidden.
34760      * @param {Number} colIndex The column index
34761      * @return {Boolean}
34762      */
34763     isHidden : function(colIndex){
34764         return this.config[colIndex].hidden;
34765     },
34766
34767
34768     /**
34769      * Returns true if the column width cannot be changed
34770      */
34771     isFixed : function(colIndex){
34772         return this.config[colIndex].fixed;
34773     },
34774
34775     /**
34776      * Returns true if the column can be resized
34777      * @return {Boolean}
34778      */
34779     isResizable : function(colIndex){
34780         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34781     },
34782     /**
34783      * Sets if a column is hidden.
34784      * @param {Number} colIndex The column index
34785      * @param {Boolean} hidden True if the column is hidden
34786      */
34787     setHidden : function(colIndex, hidden){
34788         this.config[colIndex].hidden = hidden;
34789         this.totalWidth = null;
34790         this.fireEvent("hiddenchange", this, colIndex, hidden);
34791     },
34792
34793     /**
34794      * Sets the editor for a column.
34795      * @param {Number} col The column index
34796      * @param {Object} editor The editor object
34797      */
34798     setEditor : function(col, editor){
34799         this.config[col].editor = editor;
34800     }
34801 });
34802
34803 Roo.grid.ColumnModel.defaultRenderer = function(value){
34804         if(typeof value == "string" && value.length < 1){
34805             return "&#160;";
34806         }
34807         return value;
34808 };
34809
34810 // Alias for backwards compatibility
34811 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34812 /*
34813  * Based on:
34814  * Ext JS Library 1.1.1
34815  * Copyright(c) 2006-2007, Ext JS, LLC.
34816  *
34817  * Originally Released Under LGPL - original licence link has changed is not relivant.
34818  *
34819  * Fork - LGPL
34820  * <script type="text/javascript">
34821  */
34822
34823 /**
34824  * @class Roo.grid.AbstractSelectionModel
34825  * @extends Roo.util.Observable
34826  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34827  * implemented by descendant classes.  This class should not be directly instantiated.
34828  * @constructor
34829  */
34830 Roo.grid.AbstractSelectionModel = function(){
34831     this.locked = false;
34832     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34833 };
34834
34835 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34836     /** @ignore Called by the grid automatically. Do not call directly. */
34837     init : function(grid){
34838         this.grid = grid;
34839         this.initEvents();
34840     },
34841
34842     /**
34843      * Locks the selections.
34844      */
34845     lock : function(){
34846         this.locked = true;
34847     },
34848
34849     /**
34850      * Unlocks the selections.
34851      */
34852     unlock : function(){
34853         this.locked = false;
34854     },
34855
34856     /**
34857      * Returns true if the selections are locked.
34858      * @return {Boolean}
34859      */
34860     isLocked : function(){
34861         return this.locked;
34862     }
34863 });/*
34864  * Based on:
34865  * Ext JS Library 1.1.1
34866  * Copyright(c) 2006-2007, Ext JS, LLC.
34867  *
34868  * Originally Released Under LGPL - original licence link has changed is not relivant.
34869  *
34870  * Fork - LGPL
34871  * <script type="text/javascript">
34872  */
34873 /**
34874  * @extends Roo.grid.AbstractSelectionModel
34875  * @class Roo.grid.RowSelectionModel
34876  * The default SelectionModel used by {@link Roo.grid.Grid}.
34877  * It supports multiple selections and keyboard selection/navigation. 
34878  * @constructor
34879  * @param {Object} config
34880  */
34881 Roo.grid.RowSelectionModel = function(config){
34882     Roo.apply(this, config);
34883     this.selections = new Roo.util.MixedCollection(false, function(o){
34884         return o.id;
34885     });
34886
34887     this.last = false;
34888     this.lastActive = false;
34889
34890     this.addEvents({
34891         /**
34892              * @event selectionchange
34893              * Fires when the selection changes
34894              * @param {SelectionModel} this
34895              */
34896             "selectionchange" : true,
34897         /**
34898              * @event afterselectionchange
34899              * Fires after the selection changes (eg. by key press or clicking)
34900              * @param {SelectionModel} this
34901              */
34902             "afterselectionchange" : true,
34903         /**
34904              * @event beforerowselect
34905              * Fires when a row is selected being selected, return false to cancel.
34906              * @param {SelectionModel} this
34907              * @param {Number} rowIndex The selected index
34908              * @param {Boolean} keepExisting False if other selections will be cleared
34909              */
34910             "beforerowselect" : true,
34911         /**
34912              * @event rowselect
34913              * Fires when a row is selected.
34914              * @param {SelectionModel} this
34915              * @param {Number} rowIndex The selected index
34916              * @param {Roo.data.Record} r The record
34917              */
34918             "rowselect" : true,
34919         /**
34920              * @event rowdeselect
34921              * Fires when a row is deselected.
34922              * @param {SelectionModel} this
34923              * @param {Number} rowIndex The selected index
34924              */
34925         "rowdeselect" : true
34926     });
34927     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
34928     this.locked = false;
34929 };
34930
34931 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
34932     /**
34933      * @cfg {Boolean} singleSelect
34934      * True to allow selection of only one row at a time (defaults to false)
34935      */
34936     singleSelect : false,
34937
34938     // private
34939     initEvents : function(){
34940
34941         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
34942             this.grid.on("mousedown", this.handleMouseDown, this);
34943         }else{ // allow click to work like normal
34944             this.grid.on("rowclick", this.handleDragableRowClick, this);
34945         }
34946
34947         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
34948             "up" : function(e){
34949                 if(!e.shiftKey){
34950                     this.selectPrevious(e.shiftKey);
34951                 }else if(this.last !== false && this.lastActive !== false){
34952                     var last = this.last;
34953                     this.selectRange(this.last,  this.lastActive-1);
34954                     this.grid.getView().focusRow(this.lastActive);
34955                     if(last !== false){
34956                         this.last = last;
34957                     }
34958                 }else{
34959                     this.selectFirstRow();
34960                 }
34961                 this.fireEvent("afterselectionchange", this);
34962             },
34963             "down" : function(e){
34964                 if(!e.shiftKey){
34965                     this.selectNext(e.shiftKey);
34966                 }else if(this.last !== false && this.lastActive !== false){
34967                     var last = this.last;
34968                     this.selectRange(this.last,  this.lastActive+1);
34969                     this.grid.getView().focusRow(this.lastActive);
34970                     if(last !== false){
34971                         this.last = last;
34972                     }
34973                 }else{
34974                     this.selectFirstRow();
34975                 }
34976                 this.fireEvent("afterselectionchange", this);
34977             },
34978             scope: this
34979         });
34980
34981         var view = this.grid.view;
34982         view.on("refresh", this.onRefresh, this);
34983         view.on("rowupdated", this.onRowUpdated, this);
34984         view.on("rowremoved", this.onRemove, this);
34985     },
34986
34987     // private
34988     onRefresh : function(){
34989         var ds = this.grid.dataSource, i, v = this.grid.view;
34990         var s = this.selections;
34991         s.each(function(r){
34992             if((i = ds.indexOfId(r.id)) != -1){
34993                 v.onRowSelect(i);
34994             }else{
34995                 s.remove(r);
34996             }
34997         });
34998     },
34999
35000     // private
35001     onRemove : function(v, index, r){
35002         this.selections.remove(r);
35003     },
35004
35005     // private
35006     onRowUpdated : function(v, index, r){
35007         if(this.isSelected(r)){
35008             v.onRowSelect(index);
35009         }
35010     },
35011
35012     /**
35013      * Select records.
35014      * @param {Array} records The records to select
35015      * @param {Boolean} keepExisting (optional) True to keep existing selections
35016      */
35017     selectRecords : function(records, keepExisting){
35018         if(!keepExisting){
35019             this.clearSelections();
35020         }
35021         var ds = this.grid.dataSource;
35022         for(var i = 0, len = records.length; i < len; i++){
35023             this.selectRow(ds.indexOf(records[i]), true);
35024         }
35025     },
35026
35027     /**
35028      * Gets the number of selected rows.
35029      * @return {Number}
35030      */
35031     getCount : function(){
35032         return this.selections.length;
35033     },
35034
35035     /**
35036      * Selects the first row in the grid.
35037      */
35038     selectFirstRow : function(){
35039         this.selectRow(0);
35040     },
35041
35042     /**
35043      * Select the last row.
35044      * @param {Boolean} keepExisting (optional) True to keep existing selections
35045      */
35046     selectLastRow : function(keepExisting){
35047         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35048     },
35049
35050     /**
35051      * Selects the row immediately following the last selected row.
35052      * @param {Boolean} keepExisting (optional) True to keep existing selections
35053      */
35054     selectNext : function(keepExisting){
35055         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35056             this.selectRow(this.last+1, keepExisting);
35057             this.grid.getView().focusRow(this.last);
35058         }
35059     },
35060
35061     /**
35062      * Selects the row that precedes the last selected row.
35063      * @param {Boolean} keepExisting (optional) True to keep existing selections
35064      */
35065     selectPrevious : function(keepExisting){
35066         if(this.last){
35067             this.selectRow(this.last-1, keepExisting);
35068             this.grid.getView().focusRow(this.last);
35069         }
35070     },
35071
35072     /**
35073      * Returns the selected records
35074      * @return {Array} Array of selected records
35075      */
35076     getSelections : function(){
35077         return [].concat(this.selections.items);
35078     },
35079
35080     /**
35081      * Returns the first selected record.
35082      * @return {Record}
35083      */
35084     getSelected : function(){
35085         return this.selections.itemAt(0);
35086     },
35087
35088
35089     /**
35090      * Clears all selections.
35091      */
35092     clearSelections : function(fast){
35093         if(this.locked) return;
35094         if(fast !== true){
35095             var ds = this.grid.dataSource;
35096             var s = this.selections;
35097             s.each(function(r){
35098                 this.deselectRow(ds.indexOfId(r.id));
35099             }, this);
35100             s.clear();
35101         }else{
35102             this.selections.clear();
35103         }
35104         this.last = false;
35105     },
35106
35107
35108     /**
35109      * Selects all rows.
35110      */
35111     selectAll : function(){
35112         if(this.locked) return;
35113         this.selections.clear();
35114         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35115             this.selectRow(i, true);
35116         }
35117     },
35118
35119     /**
35120      * Returns True if there is a selection.
35121      * @return {Boolean}
35122      */
35123     hasSelection : function(){
35124         return this.selections.length > 0;
35125     },
35126
35127     /**
35128      * Returns True if the specified row is selected.
35129      * @param {Number/Record} record The record or index of the record to check
35130      * @return {Boolean}
35131      */
35132     isSelected : function(index){
35133         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35134         return (r && this.selections.key(r.id) ? true : false);
35135     },
35136
35137     /**
35138      * Returns True if the specified record id is selected.
35139      * @param {String} id The id of record to check
35140      * @return {Boolean}
35141      */
35142     isIdSelected : function(id){
35143         return (this.selections.key(id) ? true : false);
35144     },
35145
35146     // private
35147     handleMouseDown : function(e, t){
35148         var view = this.grid.getView(), rowIndex;
35149         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35150             return;
35151         };
35152         if(e.shiftKey && this.last !== false){
35153             var last = this.last;
35154             this.selectRange(last, rowIndex, e.ctrlKey);
35155             this.last = last; // reset the last
35156             view.focusRow(rowIndex);
35157         }else{
35158             var isSelected = this.isSelected(rowIndex);
35159             if(e.button !== 0 && isSelected){
35160                 view.focusRow(rowIndex);
35161             }else if(e.ctrlKey && isSelected){
35162                 this.deselectRow(rowIndex);
35163             }else if(!isSelected){
35164                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35165                 view.focusRow(rowIndex);
35166             }
35167         }
35168         this.fireEvent("afterselectionchange", this);
35169     },
35170     // private
35171     handleDragableRowClick :  function(grid, rowIndex, e) 
35172     {
35173         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35174             this.selectRow(rowIndex, false);
35175             grid.view.focusRow(rowIndex);
35176              this.fireEvent("afterselectionchange", this);
35177         }
35178     },
35179     
35180     /**
35181      * Selects multiple rows.
35182      * @param {Array} rows Array of the indexes of the row to select
35183      * @param {Boolean} keepExisting (optional) True to keep existing selections
35184      */
35185     selectRows : function(rows, keepExisting){
35186         if(!keepExisting){
35187             this.clearSelections();
35188         }
35189         for(var i = 0, len = rows.length; i < len; i++){
35190             this.selectRow(rows[i], true);
35191         }
35192     },
35193
35194     /**
35195      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35196      * @param {Number} startRow The index of the first row in the range
35197      * @param {Number} endRow The index of the last row in the range
35198      * @param {Boolean} keepExisting (optional) True to retain existing selections
35199      */
35200     selectRange : function(startRow, endRow, keepExisting){
35201         if(this.locked) return;
35202         if(!keepExisting){
35203             this.clearSelections();
35204         }
35205         if(startRow <= endRow){
35206             for(var i = startRow; i <= endRow; i++){
35207                 this.selectRow(i, true);
35208             }
35209         }else{
35210             for(var i = startRow; i >= endRow; i--){
35211                 this.selectRow(i, true);
35212             }
35213         }
35214     },
35215
35216     /**
35217      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35218      * @param {Number} startRow The index of the first row in the range
35219      * @param {Number} endRow The index of the last row in the range
35220      */
35221     deselectRange : function(startRow, endRow, preventViewNotify){
35222         if(this.locked) return;
35223         for(var i = startRow; i <= endRow; i++){
35224             this.deselectRow(i, preventViewNotify);
35225         }
35226     },
35227
35228     /**
35229      * Selects a row.
35230      * @param {Number} row The index of the row to select
35231      * @param {Boolean} keepExisting (optional) True to keep existing selections
35232      */
35233     selectRow : function(index, keepExisting, preventViewNotify){
35234         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35235         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35236             if(!keepExisting || this.singleSelect){
35237                 this.clearSelections();
35238             }
35239             var r = this.grid.dataSource.getAt(index);
35240             this.selections.add(r);
35241             this.last = this.lastActive = index;
35242             if(!preventViewNotify){
35243                 this.grid.getView().onRowSelect(index);
35244             }
35245             this.fireEvent("rowselect", this, index, r);
35246             this.fireEvent("selectionchange", this);
35247         }
35248     },
35249
35250     /**
35251      * Deselects a row.
35252      * @param {Number} row The index of the row to deselect
35253      */
35254     deselectRow : function(index, preventViewNotify){
35255         if(this.locked) return;
35256         if(this.last == index){
35257             this.last = false;
35258         }
35259         if(this.lastActive == index){
35260             this.lastActive = false;
35261         }
35262         var r = this.grid.dataSource.getAt(index);
35263         this.selections.remove(r);
35264         if(!preventViewNotify){
35265             this.grid.getView().onRowDeselect(index);
35266         }
35267         this.fireEvent("rowdeselect", this, index);
35268         this.fireEvent("selectionchange", this);
35269     },
35270
35271     // private
35272     restoreLast : function(){
35273         if(this._last){
35274             this.last = this._last;
35275         }
35276     },
35277
35278     // private
35279     acceptsNav : function(row, col, cm){
35280         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35281     },
35282
35283     // private
35284     onEditorKey : function(field, e){
35285         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35286         if(k == e.TAB){
35287             e.stopEvent();
35288             ed.completeEdit();
35289             if(e.shiftKey){
35290                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35291             }else{
35292                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35293             }
35294         }else if(k == e.ENTER && !e.ctrlKey){
35295             e.stopEvent();
35296             ed.completeEdit();
35297             if(e.shiftKey){
35298                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35299             }else{
35300                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35301             }
35302         }else if(k == e.ESC){
35303             ed.cancelEdit();
35304         }
35305         if(newCell){
35306             g.startEditing(newCell[0], newCell[1]);
35307         }
35308     }
35309 });/*
35310  * Based on:
35311  * Ext JS Library 1.1.1
35312  * Copyright(c) 2006-2007, Ext JS, LLC.
35313  *
35314  * Originally Released Under LGPL - original licence link has changed is not relivant.
35315  *
35316  * Fork - LGPL
35317  * <script type="text/javascript">
35318  */
35319 /**
35320  * @class Roo.grid.CellSelectionModel
35321  * @extends Roo.grid.AbstractSelectionModel
35322  * This class provides the basic implementation for cell selection in a grid.
35323  * @constructor
35324  * @param {Object} config The object containing the configuration of this model.
35325  */
35326 Roo.grid.CellSelectionModel = function(config){
35327     Roo.apply(this, config);
35328
35329     this.selection = null;
35330
35331     this.addEvents({
35332         /**
35333              * @event beforerowselect
35334              * Fires before a cell is selected.
35335              * @param {SelectionModel} this
35336              * @param {Number} rowIndex The selected row index
35337              * @param {Number} colIndex The selected cell index
35338              */
35339             "beforecellselect" : true,
35340         /**
35341              * @event cellselect
35342              * Fires when a cell is selected.
35343              * @param {SelectionModel} this
35344              * @param {Number} rowIndex The selected row index
35345              * @param {Number} colIndex The selected cell index
35346              */
35347             "cellselect" : true,
35348         /**
35349              * @event selectionchange
35350              * Fires when the active selection changes.
35351              * @param {SelectionModel} this
35352              * @param {Object} selection null for no selection or an object (o) with two properties
35353                 <ul>
35354                 <li>o.record: the record object for the row the selection is in</li>
35355                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35356                 </ul>
35357              */
35358             "selectionchange" : true
35359     });
35360     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35361 };
35362
35363 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35364
35365     /** @ignore */
35366     initEvents : function(){
35367         this.grid.on("mousedown", this.handleMouseDown, this);
35368         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35369         var view = this.grid.view;
35370         view.on("refresh", this.onViewChange, this);
35371         view.on("rowupdated", this.onRowUpdated, this);
35372         view.on("beforerowremoved", this.clearSelections, this);
35373         view.on("beforerowsinserted", this.clearSelections, this);
35374         if(this.grid.isEditor){
35375             this.grid.on("beforeedit", this.beforeEdit,  this);
35376         }
35377     },
35378
35379         //private
35380     beforeEdit : function(e){
35381         this.select(e.row, e.column, false, true, e.record);
35382     },
35383
35384         //private
35385     onRowUpdated : function(v, index, r){
35386         if(this.selection && this.selection.record == r){
35387             v.onCellSelect(index, this.selection.cell[1]);
35388         }
35389     },
35390
35391         //private
35392     onViewChange : function(){
35393         this.clearSelections(true);
35394     },
35395
35396         /**
35397          * Returns the currently selected cell,.
35398          * @return {Array} The selected cell (row, column) or null if none selected.
35399          */
35400     getSelectedCell : function(){
35401         return this.selection ? this.selection.cell : null;
35402     },
35403
35404     /**
35405      * Clears all selections.
35406      * @param {Boolean} true to prevent the gridview from being notified about the change.
35407      */
35408     clearSelections : function(preventNotify){
35409         var s = this.selection;
35410         if(s){
35411             if(preventNotify !== true){
35412                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35413             }
35414             this.selection = null;
35415             this.fireEvent("selectionchange", this, null);
35416         }
35417     },
35418
35419     /**
35420      * Returns true if there is a selection.
35421      * @return {Boolean}
35422      */
35423     hasSelection : function(){
35424         return this.selection ? true : false;
35425     },
35426
35427     /** @ignore */
35428     handleMouseDown : function(e, t){
35429         var v = this.grid.getView();
35430         if(this.isLocked()){
35431             return;
35432         };
35433         var row = v.findRowIndex(t);
35434         var cell = v.findCellIndex(t);
35435         if(row !== false && cell !== false){
35436             this.select(row, cell);
35437         }
35438     },
35439
35440     /**
35441      * Selects a cell.
35442      * @param {Number} rowIndex
35443      * @param {Number} collIndex
35444      */
35445     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35446         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35447             this.clearSelections();
35448             r = r || this.grid.dataSource.getAt(rowIndex);
35449             this.selection = {
35450                 record : r,
35451                 cell : [rowIndex, colIndex]
35452             };
35453             if(!preventViewNotify){
35454                 var v = this.grid.getView();
35455                 v.onCellSelect(rowIndex, colIndex);
35456                 if(preventFocus !== true){
35457                     v.focusCell(rowIndex, colIndex);
35458                 }
35459             }
35460             this.fireEvent("cellselect", this, rowIndex, colIndex);
35461             this.fireEvent("selectionchange", this, this.selection);
35462         }
35463     },
35464
35465         //private
35466     isSelectable : function(rowIndex, colIndex, cm){
35467         return !cm.isHidden(colIndex);
35468     },
35469
35470     /** @ignore */
35471     handleKeyDown : function(e){
35472         Roo.log('Cell Sel Model handleKeyDown');
35473         if(!e.isNavKeyPress()){
35474             return;
35475         }
35476         var g = this.grid, s = this.selection;
35477         if(!s){
35478             e.stopEvent();
35479             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35480             if(cell){
35481                 this.select(cell[0], cell[1]);
35482             }
35483             return;
35484         }
35485         var sm = this;
35486         var walk = function(row, col, step){
35487             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35488         };
35489         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35490         var newCell;
35491
35492         switch(k){
35493             case e.TAB:
35494                 // handled by onEditorKey
35495                 if (g.isEditor && g.editing) {
35496                     return;
35497                 }
35498                 if(e.shiftKey){
35499                      newCell = walk(r, c-1, -1);
35500                 }else{
35501                      newCell = walk(r, c+1, 1);
35502                 }
35503              break;
35504              case e.DOWN:
35505                  newCell = walk(r+1, c, 1);
35506              break;
35507              case e.UP:
35508                  newCell = walk(r-1, c, -1);
35509              break;
35510              case e.RIGHT:
35511                  newCell = walk(r, c+1, 1);
35512              break;
35513              case e.LEFT:
35514                  newCell = walk(r, c-1, -1);
35515              break;
35516              case e.ENTER:
35517                  if(g.isEditor && !g.editing){
35518                     g.startEditing(r, c);
35519                     e.stopEvent();
35520                     return;
35521                 }
35522              break;
35523         };
35524         if(newCell){
35525             this.select(newCell[0], newCell[1]);
35526             e.stopEvent();
35527         }
35528     },
35529
35530     acceptsNav : function(row, col, cm){
35531         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35532     },
35533
35534     onEditorKey : function(field, e){
35535         
35536         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35537         ///Roo.log('onEditorKey' + k);
35538         
35539         if(k == e.TAB){
35540             if(e.shiftKey){
35541                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35542             }else{
35543                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35544             }
35545             e.stopEvent();
35546         }else if(k == e.ENTER && !e.ctrlKey){
35547             ed.completeEdit();
35548             e.stopEvent();
35549             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35550         }else if(k == e.ESC){
35551             ed.cancelEdit();
35552         }
35553         
35554         
35555         if(newCell){
35556             //Roo.log('next cell after edit');
35557             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35558         }
35559     }
35560 });/*
35561  * Based on:
35562  * Ext JS Library 1.1.1
35563  * Copyright(c) 2006-2007, Ext JS, LLC.
35564  *
35565  * Originally Released Under LGPL - original licence link has changed is not relivant.
35566  *
35567  * Fork - LGPL
35568  * <script type="text/javascript">
35569  */
35570  
35571 /**
35572  * @class Roo.grid.EditorGrid
35573  * @extends Roo.grid.Grid
35574  * Class for creating and editable grid.
35575  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35576  * The container MUST have some type of size defined for the grid to fill. The container will be 
35577  * automatically set to position relative if it isn't already.
35578  * @param {Object} dataSource The data model to bind to
35579  * @param {Object} colModel The column model with info about this grid's columns
35580  */
35581 Roo.grid.EditorGrid = function(container, config){
35582     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35583     this.getGridEl().addClass("xedit-grid");
35584
35585     if(!this.selModel){
35586         this.selModel = new Roo.grid.CellSelectionModel();
35587     }
35588
35589     this.activeEditor = null;
35590
35591         this.addEvents({
35592             /**
35593              * @event beforeedit
35594              * Fires before cell editing is triggered. The edit event object has the following properties <br />
35595              * <ul style="padding:5px;padding-left:16px;">
35596              * <li>grid - This grid</li>
35597              * <li>record - The record being edited</li>
35598              * <li>field - The field name being edited</li>
35599              * <li>value - The value for the field being edited.</li>
35600              * <li>row - The grid row index</li>
35601              * <li>column - The grid column index</li>
35602              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35603              * </ul>
35604              * @param {Object} e An edit event (see above for description)
35605              */
35606             "beforeedit" : true,
35607             /**
35608              * @event afteredit
35609              * Fires after a cell is edited. <br />
35610              * <ul style="padding:5px;padding-left:16px;">
35611              * <li>grid - This grid</li>
35612              * <li>record - The record being edited</li>
35613              * <li>field - The field name being edited</li>
35614              * <li>value - The value being set</li>
35615              * <li>originalValue - The original value for the field, before the edit.</li>
35616              * <li>row - The grid row index</li>
35617              * <li>column - The grid column index</li>
35618              * </ul>
35619              * @param {Object} e An edit event (see above for description)
35620              */
35621             "afteredit" : true,
35622             /**
35623              * @event validateedit
35624              * Fires after a cell is edited, but before the value is set in the record. 
35625          * You can use this to modify the value being set in the field, Return false
35626              * to cancel the change. The edit event object has the following properties <br />
35627              * <ul style="padding:5px;padding-left:16px;">
35628          * <li>editor - This editor</li>
35629              * <li>grid - This grid</li>
35630              * <li>record - The record being edited</li>
35631              * <li>field - The field name being edited</li>
35632              * <li>value - The value being set</li>
35633              * <li>originalValue - The original value for the field, before the edit.</li>
35634              * <li>row - The grid row index</li>
35635              * <li>column - The grid column index</li>
35636              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35637              * </ul>
35638              * @param {Object} e An edit event (see above for description)
35639              */
35640             "validateedit" : true
35641         });
35642     this.on("bodyscroll", this.stopEditing,  this);
35643     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35644 };
35645
35646 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35647     /**
35648      * @cfg {Number} clicksToEdit
35649      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35650      */
35651     clicksToEdit: 2,
35652
35653     // private
35654     isEditor : true,
35655     // private
35656     trackMouseOver: false, // causes very odd FF errors
35657
35658     onCellDblClick : function(g, row, col){
35659         this.startEditing(row, col);
35660     },
35661
35662     onEditComplete : function(ed, value, startValue){
35663         this.editing = false;
35664         this.activeEditor = null;
35665         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35666         var r = ed.record;
35667         var field = this.colModel.getDataIndex(ed.col);
35668         var e = {
35669             grid: this,
35670             record: r,
35671             field: field,
35672             originalValue: startValue,
35673             value: value,
35674             row: ed.row,
35675             column: ed.col,
35676             cancel:false,
35677             editor: ed
35678         };
35679         if(String(value) !== String(startValue)){
35680             
35681             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35682                 r.set(field, e.value);
35683                 // if we are dealing with a combo box..
35684                 // then we also set the 'name' colum to be the displayField
35685                 if (ed.field.displayField && ed.field.name) {
35686                     r.set(ed.field.name, ed.field.el.dom.value);
35687                 }
35688                 
35689                 delete e.cancel; //?? why!!!
35690                 this.fireEvent("afteredit", e);
35691             }
35692         } else {
35693             this.fireEvent("afteredit", e); // always fire it!
35694         }
35695         this.view.focusCell(ed.row, ed.col);
35696     },
35697
35698     /**
35699      * Starts editing the specified for the specified row/column
35700      * @param {Number} rowIndex
35701      * @param {Number} colIndex
35702      */
35703     startEditing : function(row, col){
35704         this.stopEditing();
35705         if(this.colModel.isCellEditable(col, row)){
35706             this.view.ensureVisible(row, col, true);
35707             var r = this.dataSource.getAt(row);
35708             var field = this.colModel.getDataIndex(col);
35709             var e = {
35710                 grid: this,
35711                 record: r,
35712                 field: field,
35713                 value: r.data[field],
35714                 row: row,
35715                 column: col,
35716                 cancel:false
35717             };
35718             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35719                 this.editing = true;
35720                 var ed = this.colModel.getCellEditor(col, row);
35721                 
35722                 if (!ed) {
35723                     return;
35724                 }
35725                 if(!ed.rendered){
35726                     ed.render(ed.parentEl || document.body);
35727                 }
35728                 ed.field.reset();
35729                 (function(){ // complex but required for focus issues in safari, ie and opera
35730                     ed.row = row;
35731                     ed.col = col;
35732                     ed.record = r;
35733                     ed.on("complete", this.onEditComplete, this, {single: true});
35734                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35735                     this.activeEditor = ed;
35736                     var v = r.data[field];
35737                     ed.startEdit(this.view.getCell(row, col), v);
35738                     // combo's with 'displayField and name set
35739                     if (ed.field.displayField && ed.field.name) {
35740                         ed.field.el.dom.value = r.data[ed.field.name];
35741                     }
35742                     
35743                     
35744                 }).defer(50, this);
35745             }
35746         }
35747     },
35748         
35749     /**
35750      * Stops any active editing
35751      */
35752     stopEditing : function(){
35753         if(this.activeEditor){
35754             this.activeEditor.completeEdit();
35755         }
35756         this.activeEditor = null;
35757     }
35758 });/*
35759  * Based on:
35760  * Ext JS Library 1.1.1
35761  * Copyright(c) 2006-2007, Ext JS, LLC.
35762  *
35763  * Originally Released Under LGPL - original licence link has changed is not relivant.
35764  *
35765  * Fork - LGPL
35766  * <script type="text/javascript">
35767  */
35768
35769 // private - not really -- you end up using it !
35770 // This is a support class used internally by the Grid components
35771
35772 /**
35773  * @class Roo.grid.GridEditor
35774  * @extends Roo.Editor
35775  * Class for creating and editable grid elements.
35776  * @param {Object} config any settings (must include field)
35777  */
35778 Roo.grid.GridEditor = function(field, config){
35779     if (!config && field.field) {
35780         config = field;
35781         field = Roo.factory(config.field, Roo.form);
35782     }
35783     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35784     field.monitorTab = false;
35785 };
35786
35787 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35788     
35789     /**
35790      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35791      */
35792     
35793     alignment: "tl-tl",
35794     autoSize: "width",
35795     hideEl : false,
35796     cls: "x-small-editor x-grid-editor",
35797     shim:false,
35798     shadow:"frame"
35799 });/*
35800  * Based on:
35801  * Ext JS Library 1.1.1
35802  * Copyright(c) 2006-2007, Ext JS, LLC.
35803  *
35804  * Originally Released Under LGPL - original licence link has changed is not relivant.
35805  *
35806  * Fork - LGPL
35807  * <script type="text/javascript">
35808  */
35809   
35810
35811   
35812 Roo.grid.PropertyRecord = Roo.data.Record.create([
35813     {name:'name',type:'string'},  'value'
35814 ]);
35815
35816
35817 Roo.grid.PropertyStore = function(grid, source){
35818     this.grid = grid;
35819     this.store = new Roo.data.Store({
35820         recordType : Roo.grid.PropertyRecord
35821     });
35822     this.store.on('update', this.onUpdate,  this);
35823     if(source){
35824         this.setSource(source);
35825     }
35826     Roo.grid.PropertyStore.superclass.constructor.call(this);
35827 };
35828
35829
35830
35831 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35832     setSource : function(o){
35833         this.source = o;
35834         this.store.removeAll();
35835         var data = [];
35836         for(var k in o){
35837             if(this.isEditableValue(o[k])){
35838                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35839             }
35840         }
35841         this.store.loadRecords({records: data}, {}, true);
35842     },
35843
35844     onUpdate : function(ds, record, type){
35845         if(type == Roo.data.Record.EDIT){
35846             var v = record.data['value'];
35847             var oldValue = record.modified['value'];
35848             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35849                 this.source[record.id] = v;
35850                 record.commit();
35851                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35852             }else{
35853                 record.reject();
35854             }
35855         }
35856     },
35857
35858     getProperty : function(row){
35859        return this.store.getAt(row);
35860     },
35861
35862     isEditableValue: function(val){
35863         if(val && val instanceof Date){
35864             return true;
35865         }else if(typeof val == 'object' || typeof val == 'function'){
35866             return false;
35867         }
35868         return true;
35869     },
35870
35871     setValue : function(prop, value){
35872         this.source[prop] = value;
35873         this.store.getById(prop).set('value', value);
35874     },
35875
35876     getSource : function(){
35877         return this.source;
35878     }
35879 });
35880
35881 Roo.grid.PropertyColumnModel = function(grid, store){
35882     this.grid = grid;
35883     var g = Roo.grid;
35884     g.PropertyColumnModel.superclass.constructor.call(this, [
35885         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35886         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35887     ]);
35888     this.store = store;
35889     this.bselect = Roo.DomHelper.append(document.body, {
35890         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
35891             {tag: 'option', value: 'true', html: 'true'},
35892             {tag: 'option', value: 'false', html: 'false'}
35893         ]
35894     });
35895     Roo.id(this.bselect);
35896     var f = Roo.form;
35897     this.editors = {
35898         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
35899         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
35900         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
35901         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
35902         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
35903     };
35904     this.renderCellDelegate = this.renderCell.createDelegate(this);
35905     this.renderPropDelegate = this.renderProp.createDelegate(this);
35906 };
35907
35908 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
35909     
35910     
35911     nameText : 'Name',
35912     valueText : 'Value',
35913     
35914     dateFormat : 'm/j/Y',
35915     
35916     
35917     renderDate : function(dateVal){
35918         return dateVal.dateFormat(this.dateFormat);
35919     },
35920
35921     renderBool : function(bVal){
35922         return bVal ? 'true' : 'false';
35923     },
35924
35925     isCellEditable : function(colIndex, rowIndex){
35926         return colIndex == 1;
35927     },
35928
35929     getRenderer : function(col){
35930         return col == 1 ?
35931             this.renderCellDelegate : this.renderPropDelegate;
35932     },
35933
35934     renderProp : function(v){
35935         return this.getPropertyName(v);
35936     },
35937
35938     renderCell : function(val){
35939         var rv = val;
35940         if(val instanceof Date){
35941             rv = this.renderDate(val);
35942         }else if(typeof val == 'boolean'){
35943             rv = this.renderBool(val);
35944         }
35945         return Roo.util.Format.htmlEncode(rv);
35946     },
35947
35948     getPropertyName : function(name){
35949         var pn = this.grid.propertyNames;
35950         return pn && pn[name] ? pn[name] : name;
35951     },
35952
35953     getCellEditor : function(colIndex, rowIndex){
35954         var p = this.store.getProperty(rowIndex);
35955         var n = p.data['name'], val = p.data['value'];
35956         
35957         if(typeof(this.grid.customEditors[n]) == 'string'){
35958             return this.editors[this.grid.customEditors[n]];
35959         }
35960         if(typeof(this.grid.customEditors[n]) != 'undefined'){
35961             return this.grid.customEditors[n];
35962         }
35963         if(val instanceof Date){
35964             return this.editors['date'];
35965         }else if(typeof val == 'number'){
35966             return this.editors['number'];
35967         }else if(typeof val == 'boolean'){
35968             return this.editors['boolean'];
35969         }else{
35970             return this.editors['string'];
35971         }
35972     }
35973 });
35974
35975 /**
35976  * @class Roo.grid.PropertyGrid
35977  * @extends Roo.grid.EditorGrid
35978  * This class represents the  interface of a component based property grid control.
35979  * <br><br>Usage:<pre><code>
35980  var grid = new Roo.grid.PropertyGrid("my-container-id", {
35981       
35982  });
35983  // set any options
35984  grid.render();
35985  * </code></pre>
35986   
35987  * @constructor
35988  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
35989  * The container MUST have some type of size defined for the grid to fill. The container will be
35990  * automatically set to position relative if it isn't already.
35991  * @param {Object} config A config object that sets properties on this grid.
35992  */
35993 Roo.grid.PropertyGrid = function(container, config){
35994     config = config || {};
35995     var store = new Roo.grid.PropertyStore(this);
35996     this.store = store;
35997     var cm = new Roo.grid.PropertyColumnModel(this, store);
35998     store.store.sort('name', 'ASC');
35999     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36000         ds: store.store,
36001         cm: cm,
36002         enableColLock:false,
36003         enableColumnMove:false,
36004         stripeRows:false,
36005         trackMouseOver: false,
36006         clicksToEdit:1
36007     }, config));
36008     this.getGridEl().addClass('x-props-grid');
36009     this.lastEditRow = null;
36010     this.on('columnresize', this.onColumnResize, this);
36011     this.addEvents({
36012          /**
36013              * @event beforepropertychange
36014              * Fires before a property changes (return false to stop?)
36015              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36016              * @param {String} id Record Id
36017              * @param {String} newval New Value
36018          * @param {String} oldval Old Value
36019              */
36020         "beforepropertychange": true,
36021         /**
36022              * @event propertychange
36023              * Fires after a property changes
36024              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36025              * @param {String} id Record Id
36026              * @param {String} newval New Value
36027          * @param {String} oldval Old Value
36028              */
36029         "propertychange": true
36030     });
36031     this.customEditors = this.customEditors || {};
36032 };
36033 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36034     
36035      /**
36036      * @cfg {Object} customEditors map of colnames=> custom editors.
36037      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36038      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36039      * false disables editing of the field.
36040          */
36041     
36042       /**
36043      * @cfg {Object} propertyNames map of property Names to their displayed value
36044          */
36045     
36046     render : function(){
36047         Roo.grid.PropertyGrid.superclass.render.call(this);
36048         this.autoSize.defer(100, this);
36049     },
36050
36051     autoSize : function(){
36052         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36053         if(this.view){
36054             this.view.fitColumns();
36055         }
36056     },
36057
36058     onColumnResize : function(){
36059         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36060         this.autoSize();
36061     },
36062     /**
36063      * Sets the data for the Grid
36064      * accepts a Key => Value object of all the elements avaiable.
36065      * @param {Object} data  to appear in grid.
36066      */
36067     setSource : function(source){
36068         this.store.setSource(source);
36069         //this.autoSize();
36070     },
36071     /**
36072      * Gets all the data from the grid.
36073      * @return {Object} data  data stored in grid
36074      */
36075     getSource : function(){
36076         return this.store.getSource();
36077     }
36078 });/*
36079  * Based on:
36080  * Ext JS Library 1.1.1
36081  * Copyright(c) 2006-2007, Ext JS, LLC.
36082  *
36083  * Originally Released Under LGPL - original licence link has changed is not relivant.
36084  *
36085  * Fork - LGPL
36086  * <script type="text/javascript">
36087  */
36088  
36089 /**
36090  * @class Roo.LoadMask
36091  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36092  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36093  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36094  * element's UpdateManager load indicator and will be destroyed after the initial load.
36095  * @constructor
36096  * Create a new LoadMask
36097  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36098  * @param {Object} config The config object
36099  */
36100 Roo.LoadMask = function(el, config){
36101     this.el = Roo.get(el);
36102     Roo.apply(this, config);
36103     if(this.store){
36104         this.store.on('beforeload', this.onBeforeLoad, this);
36105         this.store.on('load', this.onLoad, this);
36106         this.store.on('loadexception', this.onLoad, this);
36107         this.removeMask = false;
36108     }else{
36109         var um = this.el.getUpdateManager();
36110         um.showLoadIndicator = false; // disable the default indicator
36111         um.on('beforeupdate', this.onBeforeLoad, this);
36112         um.on('update', this.onLoad, this);
36113         um.on('failure', this.onLoad, this);
36114         this.removeMask = true;
36115     }
36116 };
36117
36118 Roo.LoadMask.prototype = {
36119     /**
36120      * @cfg {Boolean} removeMask
36121      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36122      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36123      */
36124     /**
36125      * @cfg {String} msg
36126      * The text to display in a centered loading message box (defaults to 'Loading...')
36127      */
36128     msg : 'Loading...',
36129     /**
36130      * @cfg {String} msgCls
36131      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36132      */
36133     msgCls : 'x-mask-loading',
36134
36135     /**
36136      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36137      * @type Boolean
36138      */
36139     disabled: false,
36140
36141     /**
36142      * Disables the mask to prevent it from being displayed
36143      */
36144     disable : function(){
36145        this.disabled = true;
36146     },
36147
36148     /**
36149      * Enables the mask so that it can be displayed
36150      */
36151     enable : function(){
36152         this.disabled = false;
36153     },
36154
36155     // private
36156     onLoad : function(){
36157         this.el.unmask(this.removeMask);
36158     },
36159
36160     // private
36161     onBeforeLoad : function(){
36162         if(!this.disabled){
36163             this.el.mask(this.msg, this.msgCls);
36164         }
36165     },
36166
36167     // private
36168     destroy : function(){
36169         if(this.store){
36170             this.store.un('beforeload', this.onBeforeLoad, this);
36171             this.store.un('load', this.onLoad, this);
36172             this.store.un('loadexception', this.onLoad, this);
36173         }else{
36174             var um = this.el.getUpdateManager();
36175             um.un('beforeupdate', this.onBeforeLoad, this);
36176             um.un('update', this.onLoad, this);
36177             um.un('failure', this.onLoad, this);
36178         }
36179     }
36180 };/*
36181  * Based on:
36182  * Ext JS Library 1.1.1
36183  * Copyright(c) 2006-2007, Ext JS, LLC.
36184  *
36185  * Originally Released Under LGPL - original licence link has changed is not relivant.
36186  *
36187  * Fork - LGPL
36188  * <script type="text/javascript">
36189  */
36190 Roo.XTemplate = function(){
36191     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36192     var s = this.html;
36193
36194     s = ['<tpl>', s, '</tpl>'].join('');
36195
36196     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36197
36198     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36199     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36200     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36201     var m, id = 0;
36202     var tpls = [];
36203
36204     while(m = s.match(re)){
36205        var m2 = m[0].match(nameRe);
36206        var m3 = m[0].match(ifRe);
36207        var m4 = m[0].match(execRe);
36208        var exp = null, fn = null, exec = null;
36209        var name = m2 && m2[1] ? m2[1] : '';
36210        if(m3){
36211            exp = m3 && m3[1] ? m3[1] : null;
36212            if(exp){
36213                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36214            }
36215        }
36216        if(m4){
36217            exp = m4 && m4[1] ? m4[1] : null;
36218            if(exp){
36219                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36220            }
36221        }
36222        if(name){
36223            switch(name){
36224                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36225                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36226                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36227            }
36228        }
36229        tpls.push({
36230             id: id,
36231             target: name,
36232             exec: exec,
36233             test: fn,
36234             body: m[1]||''
36235         });
36236        s = s.replace(m[0], '{xtpl'+ id + '}');
36237        ++id;
36238     }
36239     for(var i = tpls.length-1; i >= 0; --i){
36240         this.compileTpl(tpls[i]);
36241     }
36242     this.master = tpls[tpls.length-1];
36243     this.tpls = tpls;
36244 };
36245 Roo.extend(Roo.XTemplate, Roo.Template, {
36246
36247     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36248
36249     applySubTemplate : function(id, values, parent){
36250         var t = this.tpls[id];
36251         if(t.test && !t.test.call(this, values, parent)){
36252             return '';
36253         }
36254         if(t.exec && t.exec.call(this, values, parent)){
36255             return '';
36256         }
36257         var vs = t.target ? t.target.call(this, values, parent) : values;
36258         parent = t.target ? values : parent;
36259         if(t.target && vs instanceof Array){
36260             var buf = [];
36261             for(var i = 0, len = vs.length; i < len; i++){
36262                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36263             }
36264             return buf.join('');
36265         }
36266         return t.compiled.call(this, vs, parent);
36267     },
36268
36269     compileTpl : function(tpl){
36270         var fm = Roo.util.Format;
36271         var useF = this.disableFormats !== true;
36272         var sep = Roo.isGecko ? "+" : ",";
36273         var fn = function(m, name, format, args){
36274             if(name.substr(0, 4) == 'xtpl'){
36275                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36276             }
36277             var v;
36278             if(name.indexOf('.') != -1){
36279                 v = name;
36280             }else{
36281                 v = "values['" + name + "']";
36282             }
36283             if(format && useF){
36284                 args = args ? ',' + args : "";
36285                 if(format.substr(0, 5) != "this."){
36286                     format = "fm." + format + '(';
36287                 }else{
36288                     format = 'this.call("'+ format.substr(5) + '", ';
36289                     args = ", values";
36290                 }
36291             }else{
36292                 args= ''; format = "("+v+" === undefined ? '' : ";
36293             }
36294             return "'"+ sep + format + v + args + ")"+sep+"'";
36295         };
36296         var body;
36297         // branched to use + in gecko and [].join() in others
36298         if(Roo.isGecko){
36299             body = "tpl.compiled = function(values, parent){ return '" +
36300                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36301                     "';};";
36302         }else{
36303             body = ["tpl.compiled = function(values, parent){ return ['"];
36304             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36305             body.push("'].join('');};");
36306             body = body.join('');
36307         }
36308         /** eval:var:zzzzzzz */
36309         eval(body);
36310         return this;
36311     },
36312
36313     applyTemplate : function(values){
36314         return this.master.compiled.call(this, values, {});
36315         var s = this.subs;
36316     },
36317
36318     apply : function(){
36319         return this.applyTemplate.apply(this, arguments);
36320     },
36321
36322     compile : function(){return this;}
36323 });
36324
36325 Roo.XTemplate.from = function(el){
36326     el = Roo.getDom(el);
36327     return new Roo.XTemplate(el.value || el.innerHTML);
36328 };/*
36329  * Original code for Roojs - LGPL
36330  * <script type="text/javascript">
36331  */
36332  
36333 /**
36334  * @class Roo.XComponent
36335  * A delayed Element creator...
36336  * 
36337  * Mypart.xyx = new Roo.XComponent({
36338
36339     parent : 'Mypart.xyz', // empty == document.element.!!
36340     order : '001',
36341     name : 'xxxx'
36342     region : 'xxxx'
36343     disabled : function() {} 
36344      
36345     tree : function() { // return an tree of xtype declared components
36346         var MODULE = this;
36347         return 
36348         {
36349             xtype : 'NestedLayoutPanel',
36350             // technicall
36351         }
36352      ]
36353  *})
36354  * @extends Roo.util.Observable
36355  * @constructor
36356  * @param cfg {Object} configuration of component
36357  * 
36358  */
36359 Roo.XComponent = function(cfg) {
36360     Roo.apply(this, cfg);
36361     this.addEvents({ 
36362         /**
36363              * @event built
36364              * Fires when this the componnt is built
36365              * @param {Roo.XComponent} c the component
36366              */
36367         'built' : true,
36368         /**
36369              * @event buildcomplete
36370              * Fires on the top level element when all elements have been built
36371              * @param {Roo.XComponent} c the top level component.
36372          */
36373         'buildcomplete' : true
36374         
36375     });
36376     
36377     Roo.XComponent.register(this);
36378     this.modules = false;
36379     this.el = false; // where the layout goes..
36380     
36381     
36382 }
36383 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36384     /**
36385      * @property el
36386      * The created element (with Roo.factory())
36387      * @type {Roo.Layout}
36388      */
36389     el  : false,
36390     
36391     /**
36392      * @property el
36393      * for BC  - use el in new code
36394      * @type {Roo.Layout}
36395      */
36396     panel : false,
36397     
36398     /**
36399      * @property layout
36400      * for BC  - use el in new code
36401      * @type {Roo.Layout}
36402      */
36403     layout : false,
36404     
36405      /**
36406      * @cfg {Function|boolean} disabled
36407      * If this module is disabled by some rule, return true from the funtion
36408      */
36409     disabled : false,
36410     
36411     /**
36412      * @cfg {String} parent 
36413      * Name of parent element which it get xtype added to..
36414      */
36415     parent: false,
36416     
36417     /**
36418      * @cfg {String} order
36419      * Used to set the order in which elements are created (usefull for multiple tabs)
36420      */
36421     
36422     order : false,
36423     /**
36424      * @cfg {String} name
36425      * String to display while loading.
36426      */
36427     name : false,
36428     /**
36429      * @cfg {Array} items
36430      * A single item array - the first element is the root of the tree..
36431      * It's done this way to stay compatible with the Xtype system...
36432      */
36433     items : false
36434      
36435      
36436     
36437 });
36438
36439 Roo.apply(Roo.XComponent, {
36440     
36441     /**
36442      * @property  buildCompleted
36443      * True when the builder has completed building the interface.
36444      * @type Boolean
36445      */
36446     buildCompleted : false,
36447      
36448     /**
36449      * @property  topModule
36450      * the upper most module - uses document.element as it's constructor.
36451      * @type Object
36452      */
36453      
36454     topModule  : false,
36455       
36456     /**
36457      * @property  modules
36458      * array of modules to be created by registration system.
36459      * @type Roo.XComponent
36460      */
36461     
36462     modules : [],
36463       
36464     
36465     /**
36466      * Register components to be built later.
36467      *
36468      * This solves the following issues
36469      * - Building is not done on page load, but after an authentication process has occured.
36470      * - Interface elements are registered on page load
36471      * - Parent Interface elements may not be loaded before child, so this handles that..
36472      * 
36473      *
36474      * example:
36475      * 
36476      * MyApp.register({
36477           order : '000001',
36478           module : 'Pman.Tab.projectMgr',
36479           region : 'center',
36480           parent : 'Pman.layout',
36481           disabled : false,  // or use a function..
36482         })
36483      
36484      * * @param {Object} details about module
36485      */
36486     register : function(obj) {
36487         this.modules.push(obj);
36488          
36489     },
36490     /**
36491      * convert a string to an object..
36492      * 
36493      */
36494     
36495     toObject : function(str)
36496     {
36497         if (!str || typeof(str) == 'object') {
36498             return str;
36499         }
36500         var ar = str.split('.');
36501         var rt, o;
36502         rt = ar.shift();
36503             /** eval:var:o */
36504         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36505         if (o === false) {
36506             throw "Module not found : " + str;
36507         }
36508         Roo.each(ar, function(e) {
36509             if (typeof(o[e]) == 'undefined') {
36510                 throw "Module not found : " + str;
36511             }
36512             o = o[e];
36513         });
36514         return o;
36515         
36516     },
36517     
36518     
36519     /**
36520      * move modules into their correct place in the tree..
36521      * 
36522      */
36523     preBuild : function ()
36524     {
36525         
36526         Roo.each(this.modules , function (obj)
36527         {
36528             obj.parent = this.toObject(obj.parent);
36529             
36530             if (!obj.parent) {
36531                 this.topModule = obj;
36532                 return;
36533             }
36534             
36535             if (!obj.parent.modules) {
36536                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36537                     function(o) { return o.order + '' }
36538                 );
36539             }
36540             
36541             obj.parent.modules.add(obj);
36542         }, this);
36543     },
36544     
36545      /**
36546      * make a list of modules to build.
36547      * @return {Array} list of modules. 
36548      */ 
36549     
36550     buildOrder : function()
36551     {
36552         var _this = this;
36553         var cmp = function(a,b) {   
36554             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36555         };
36556         
36557         if (!this.topModule || !this.topModule.modules) {
36558             throw "No top level modules to build";
36559         }
36560        
36561         // make a flat list in order of modules to build.
36562         var mods = [ this.topModule ];
36563         
36564         
36565         // add modules to their parents..
36566         var addMod = function(m) {
36567            // Roo.debug && Roo.log(m.modKey);
36568             
36569             mods.push(m);
36570             if (m.modules) {
36571                 m.modules.keySort('ASC',  cmp );
36572                 m.modules.each(addMod);
36573             }
36574             // not sure if this is used any more..
36575             if (m.finalize) {
36576                 m.finalize.name = m.name + " (clean up) ";
36577                 mods.push(m.finalize);
36578             }
36579             
36580         }
36581         this.topModule.modules.keySort('ASC',  cmp );
36582         this.topModule.modules.each(addMod);
36583         return mods;
36584     },
36585     
36586      /**
36587      * Build the registered modules.
36588      * @param {Object} parent element.
36589      * @param {Function} optional method to call after module has been added.
36590      * 
36591      */ 
36592    
36593     build : function() 
36594     {
36595         
36596         this.preBuild();
36597         var mods = this.buildOrder();
36598       
36599         //this.allmods = mods;
36600         //Roo.debug && Roo.log(mods);
36601         //return;
36602         if (!mods.length) { // should not happen
36603             throw "NO modules!!!";
36604         }
36605         
36606         
36607         
36608         // flash it up as modal - so we store the mask!?
36609         Roo.MessageBox.show({ title: 'loading' });
36610         Roo.MessageBox.show({
36611            title: "Please wait...",
36612            msg: "Building Interface...",
36613            width:450,
36614            progress:true,
36615            closable:false,
36616            modal: false
36617           
36618         });
36619         var total = mods.length;
36620         
36621         var _this = this;
36622         var progressRun = function() {
36623             if (!mods.length) {
36624                 Roo.debug && Roo.log('hide?');
36625                 Roo.MessageBox.hide();
36626                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36627                 return;    
36628             }
36629             
36630             var m = mods.shift();
36631             Roo.debug && Roo.log(m);
36632             if (typeof(m) == 'function') { // not sure if this is supported any more..
36633                 m.call(this);
36634                 return progressRun.defer(10, _this);
36635             } 
36636             
36637             Roo.MessageBox.updateProgress(
36638                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36639                     " of " + total + 
36640                     (m.name ? (' - ' + m.name) : '')
36641                     );
36642             
36643          
36644             
36645             var disabled = (typeof(m.disabled) == 'function') ?
36646                 m.disabled.call(m.module.disabled) : m.disabled;    
36647             
36648             
36649             if (disabled) {
36650                 return progressRun(); // we do not update the display!
36651             }
36652             
36653             if (!m.parent) {
36654                 // it's a top level one..
36655                 var layoutbase = new Ext.BorderLayout(document.body, {
36656                
36657                     center: {
36658                          titlebar: false,
36659                          autoScroll:false,
36660                          closeOnTab: true,
36661                          tabPosition: 'top',
36662                          //resizeTabs: true,
36663                          alwaysShowTabs: true,
36664                          minTabWidth: 140
36665                     }
36666                 });
36667                 var tree = m.tree();
36668                 tree.region = 'center';
36669                 m.el = layoutbase.addxtype(tree);
36670                 m.panel = m.el;
36671                 m.layout = m.panel.layout;    
36672                 return progressRun.defer(10, _this);
36673             }
36674             
36675             var tree = m.tree();
36676             tree.region = tree.region || m.region;
36677             m.el = m.parent.el.addxtype(tree);
36678             m.fireEvent('built', m);
36679             m.panel = m.el;
36680             m.layout = m.panel.layout;    
36681             progressRun.defer(10, _this); 
36682             
36683         }
36684         progressRun.defer(1, _this);
36685      
36686         
36687         
36688     }
36689      
36690    
36691     
36692     
36693 });
36694  //<script type="text/javascript">
36695
36696
36697 /**
36698  * @class Roo.Login
36699  * @extends Roo.LayoutDialog
36700  * A generic Login Dialog..... - only one needed in theory!?!?
36701  *
36702  * Fires XComponent builder on success...
36703  * 
36704  * Sends 
36705  *    username,password, lang = for login actions.
36706  *    check = 1 for periodic checking that sesion is valid.
36707  *    passwordRequest = email request password
36708  *    logout = 1 = to logout
36709  * 
36710  * Affects: (this id="????" elements)
36711  *   loading  (removed) (used to indicate application is loading)
36712  *   loading-mask (hides) (used to hide application when it's building loading)
36713  *   
36714  * 
36715  * Usage: 
36716  *    
36717  * 
36718  * Myapp.login = Roo.Login({
36719      url: xxxx,
36720    
36721      realm : 'Myapp', 
36722      
36723      
36724      method : 'POST',
36725      
36726      
36727      * 
36728  })
36729  * 
36730  * 
36731  * 
36732  **/
36733  
36734 Roo.Login = function(cfg)
36735 {
36736     this.addEvents({
36737         'refreshed' : true
36738     });
36739     
36740     Roo.apply(this,cfg);
36741     
36742     Roo.onReady(function() {
36743         this.onLoad();
36744     }, this);
36745     // call parent..
36746     
36747    
36748     Roo.Login.superclass.constructor.call(this, this);
36749     //this.addxtype(this.items[0]);
36750     
36751     
36752 }
36753
36754
36755 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36756     
36757     /**
36758      * @cfg {String} method
36759      * Method used to query for login details.
36760      */
36761     
36762     method : 'POST',
36763     /**
36764      * @cfg {String} url
36765      * URL to query login data. - eg. baseURL + '/Login.php'
36766      */
36767     url : '',
36768     
36769     /**
36770      * @property user
36771      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36772      * @type {Object} 
36773      */
36774     user : false,
36775     /**
36776      * @property checkFails
36777      * Number of times we have attempted to get authentication check, and failed.
36778      * @type {Number} 
36779      */
36780     checkFails : 0,
36781       /**
36782      * @property intervalID
36783      * The window interval that does the constant login checking.
36784      * @type {Number} 
36785      */
36786     intervalID : 0,
36787     
36788     
36789     onLoad : function() // called on page load...
36790     {
36791         // load 
36792          
36793         if (Roo.get('loading')) { // clear any loading indicator..
36794             Roo.get('loading').remove();
36795         }
36796         
36797         //this.switchLang('en'); // set the language to english..
36798        
36799         this.check({
36800             success:  function(response, opts)  {  // check successfull...
36801             
36802                 var res = this.processResponse(response);
36803                 this.checkFails =0;
36804                 if (!res.success) { // error!
36805                     this.checkFails = 5;
36806                     //console.log('call failure');
36807                     return this.failure(response,opts);
36808                 }
36809                 
36810                 if (!res.data.id) { // id=0 == login failure.
36811                     return this.show();
36812                 }
36813                 
36814                               
36815                         //console.log(success);
36816                 this.fillAuth(res.data);   
36817                 this.checkFails =0;
36818                 Roo.XComponent.build();
36819             },
36820             failure : this.show
36821         });
36822         
36823     }, 
36824     
36825     
36826     check: function(cfg) // called every so often to refresh cookie etc..
36827     {
36828         if (cfg.again) { // could be undefined..
36829             this.checkFails++;
36830         } else {
36831             this.checkFails = 0;
36832         }
36833         var _this = this;
36834         if (this.sending) {
36835             if ( this.checkFails > 4) {
36836                 Roo.MessageBox.alert("Error",  
36837                     "Error getting authentication status. - try reloading, or wait a while", function() {
36838                         _this.sending = false;
36839                     }); 
36840                 return;
36841             }
36842             cfg.again = true;
36843             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36844             return;
36845         }
36846         this.sending = true;
36847         
36848         Roo.Ajax.request({  
36849             url: this.url,
36850             params: {
36851                 getAuthUser: true
36852             },  
36853             method: this.method,
36854             success:  cfg.success || this.success,
36855             failure : cfg.failure || this.failure,
36856             scope : this,
36857             callCfg : cfg
36858               
36859         });  
36860     }, 
36861     
36862     
36863     logout: function()
36864     {
36865         window.onbeforeunload = function() { }; // false does not work for IE..
36866         this.user = false;
36867         var _this = this;
36868         
36869         Roo.Ajax.request({  
36870             url: this.url,
36871             params: {
36872                 logout: 1
36873             },  
36874             method: 'GET',
36875             failure : function() {
36876                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36877                     document.location = document.location.toString() + '?ts=' + Math.random();
36878                 });
36879                 
36880             },
36881             success : function() {
36882                 _this.user = false;
36883                 this.checkFails =0;
36884                 // fixme..
36885                 document.location = document.location.toString() + '?ts=' + Math.random();
36886             }
36887               
36888               
36889         }); 
36890     },
36891     
36892     processResponse : function (response)
36893     {
36894         var res = '';
36895         try {
36896             res = Roo.decode(response.responseText);
36897             // oops...
36898             if (typeof(res) != 'object') {
36899                 res = { success : false, errorMsg : res, errors : true };
36900             }
36901             if (typeof(res.success) == 'undefined') {
36902                 res.success = false;
36903             }
36904             
36905         } catch(e) {
36906             res = { success : false,  errorMsg : response.responseText, errors : true };
36907         }
36908         return res;
36909     },
36910     
36911     success : function(response, opts)  // check successfull...
36912     {  
36913         this.sending = false;
36914         var res = this.processResponse(response);
36915         if (!res.success) {
36916             return this.failure(response, opts);
36917         }
36918         if (!res.data || !res.data.id) {
36919             return this.failure(response,opts);
36920         }
36921         //console.log(res);
36922         this.fillAuth(res.data);
36923         
36924         this.checkFails =0;
36925         
36926     },
36927     
36928     
36929     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
36930     {
36931         this.authUser = -1;
36932         this.sending = false;
36933         var res = this.processResponse(response);
36934         //console.log(res);
36935         if ( this.checkFails > 2) {
36936         
36937             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
36938                 "Error getting authentication status. - try reloading"); 
36939             return;
36940         }
36941         opts.callCfg.again = true;
36942         this.check.defer(1000, this, [ opts.callCfg ]);
36943         return;  
36944     },
36945     
36946     
36947     
36948     fillAuth: function(au) {
36949         this.startAuthCheck();
36950         this.authUserId = au.id;
36951         this.authUser = au;
36952         this.lastChecked = new Date();
36953         this.fireEvent('refreshed', au);
36954         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
36955         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
36956         au.lang = au.lang || 'en';
36957         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
36958         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
36959         this.switchLang(au.lang );
36960         
36961      
36962         // open system... - -on setyp..
36963         if (this.authUserId  < 0) {
36964             Roo.MessageBox.alert("Warning", 
36965                 "This is an open system - please set up a admin user with a password.");  
36966         }
36967          
36968         //Pman.onload(); // which should do nothing if it's a re-auth result...
36969         
36970              
36971     },
36972     
36973     startAuthCheck : function() // starter for timeout checking..
36974     {
36975         if (this.intervalID) { // timer already in place...
36976             return false;
36977         }
36978         var _this = this;
36979         this.intervalID =  window.setInterval(function() {
36980               _this.check(false);
36981             }, 120000); // every 120 secs = 2mins..
36982         
36983         
36984     },
36985          
36986     
36987     switchLang : function (lang) 
36988     {
36989         _T = typeof(_T) == 'undefined' ? false : _T;
36990           if (!_T || !lang.length) {
36991             return;
36992         }
36993         
36994         if (!_T && lang != 'en') {
36995             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
36996             return;
36997         }
36998         
36999         if (typeof(_T.en) == 'undefined') {
37000             _T.en = {};
37001             Roo.apply(_T.en, _T);
37002         }
37003         
37004         if (typeof(_T[lang]) == 'undefined') {
37005             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37006             return;
37007         }
37008         
37009         
37010         Roo.apply(_T, _T[lang]);
37011         // just need to set the text values for everything...
37012         var _this = this;
37013         /* this will not work ...
37014         if (this.form) { 
37015             
37016                
37017             function formLabel(name, val) {
37018                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37019             }
37020             
37021             formLabel('password', "Password"+':');
37022             formLabel('username', "Email Address"+':');
37023             formLabel('lang', "Language"+':');
37024             this.dialog.setTitle("Login");
37025             this.dialog.buttons[0].setText("Forgot Password");
37026             this.dialog.buttons[1].setText("Login");
37027         }
37028         */
37029         
37030         
37031     },
37032     
37033     
37034     title: "Login",
37035     modal: true,
37036     width:  350,
37037     //height: 230,
37038     height: 180,
37039     shadow: true,
37040     minWidth:200,
37041     minHeight:180,
37042     //proxyDrag: true,
37043     closable: false,
37044     draggable: false,
37045     collapsible: false,
37046     resizable: false,
37047     center: {  // needed??
37048         autoScroll:false,
37049         titlebar: false,
37050        // tabPosition: 'top',
37051         hideTabs: true,
37052         closeOnTab: true,
37053         alwaysShowTabs: false
37054     } ,
37055     listeners : {
37056         
37057         show  : function(dlg)
37058         {
37059             //console.log(this);
37060             this.form = this.layout.getRegion('center').activePanel.form;
37061             this.form.dialog = dlg;
37062             this.buttons[0].form = this.form;
37063             this.buttons[0].dialog = dlg;
37064             this.buttons[1].form = this.form;
37065             this.buttons[1].dialog = dlg;
37066            
37067            //this.resizeToLogo.defer(1000,this);
37068             // this is all related to resizing for logos..
37069             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37070            //// if (!sz) {
37071              //   this.resizeToLogo.defer(1000,this);
37072              //   return;
37073            // }
37074             //var w = Ext.lib.Dom.getViewWidth() - 100;
37075             //var h = Ext.lib.Dom.getViewHeight() - 100;
37076             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37077             //this.center();
37078             if (this.disabled) {
37079                 this.hide();
37080                 return;
37081             }
37082             
37083             if (this.user.id < 0) { // used for inital setup situations.
37084                 return;
37085             }
37086             
37087             if (this.intervalID) {
37088                 // remove the timer
37089                 window.clearInterval(this.intervalID);
37090                 this.intervalID = false;
37091             }
37092             
37093             
37094             if (Roo.get('loading')) {
37095                 Roo.get('loading').remove();
37096             }
37097             if (Roo.get('loading-mask')) {
37098                 Roo.get('loading-mask').hide();
37099             }
37100             
37101             //incomming._node = tnode;
37102             this.form.reset();
37103             //this.dialog.modal = !modal;
37104             //this.dialog.show();
37105             this.el.unmask(); 
37106             
37107             
37108             this.form.setValues({
37109                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37110                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37111             });
37112             
37113             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37114             if (this.form.findField('username').getValue().length > 0 ){
37115                 this.form.findField('password').focus();
37116             } else {
37117                this.form.findField('username').focus();
37118             }
37119     
37120         }
37121     },
37122     items : [
37123          {
37124        
37125             xtype : 'ContentPanel',
37126             xns : Roo,
37127             region: 'center',
37128             fitToFrame : true,
37129             
37130             items : [
37131     
37132                 {
37133                
37134                     xtype : 'Form',
37135                     xns : Roo.form,
37136                     labelWidth: 100,
37137                     style : 'margin: 10px;',
37138                     
37139                     listeners : {
37140                         actionfailed : function(f, act) {
37141                             // form can return { errors: .... }
37142                                 
37143                             //act.result.errors // invalid form element list...
37144                             //act.result.errorMsg// invalid form element list...
37145                             
37146                             this.dialog.el.unmask();
37147                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37148                                         "Login failed - communication error - try again.");
37149                                       
37150                         },
37151                         actioncomplete: function(re, act) {
37152                              
37153                             Roo.state.Manager.set(
37154                                 this.dialog.realm + '.username',  
37155                                     this.findField('username').getValue()
37156                             );
37157                             Roo.state.Manager.set(
37158                                 this.dialog.realm + '.lang',  
37159                                 this.findField('lang').getValue() 
37160                             );
37161                             
37162                             this.dialog.fillAuth(act.result.data);
37163                               
37164                             this.dialog.hide();
37165                             
37166                             if (Roo.get('loading-mask')) {
37167                                 Roo.get('loading-mask').show();
37168                             }
37169                             Roo.XComponent.build();
37170                             
37171                              
37172                             
37173                         }
37174                     },
37175                     items : [
37176                         {
37177                             xtype : 'TextField',
37178                             xns : Roo.form,
37179                             fieldLabel: "Email Address",
37180                             name: 'username',
37181                             width:200,
37182                             autoCreate : {tag: "input", type: "text", size: "20"}
37183                         },
37184                         {
37185                             xtype : 'TextField',
37186                             xns : Roo.form,
37187                             fieldLabel: "Password",
37188                             inputType: 'password',
37189                             name: 'password',
37190                             width:200,
37191                             autoCreate : {tag: "input", type: "text", size: "20"},
37192                             listeners : {
37193                                 specialkey : function(e,ev) {
37194                                     if (ev.keyCode == 13) {
37195                                         this.form.dialog.el.mask("Logging in");
37196                                         this.form.doAction('submit', {
37197                                             url: this.form.dialog.url,
37198                                             method: this.form.dialog.method
37199                                         });
37200                                     }
37201                                 }
37202                             }  
37203                         },
37204                         {
37205                             xtype : 'ComboBox',
37206                             xns : Roo.form,
37207                             fieldLabel: "Language",
37208                             name : 'langdisp',
37209                             store: {
37210                                 xtype : 'SimpleStore',
37211                                 fields: ['lang', 'ldisp'],
37212                                 data : [
37213                                     [ 'en', 'English' ],
37214                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37215                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37216                                 ]
37217                             },
37218                             
37219                             valueField : 'lang',
37220                             hiddenName:  'lang',
37221                             width: 200,
37222                             displayField:'ldisp',
37223                             typeAhead: false,
37224                             editable: false,
37225                             mode: 'local',
37226                             triggerAction: 'all',
37227                             emptyText:'Select a Language...',
37228                             selectOnFocus:true,
37229                             listeners : {
37230                                 select :  function(cb, rec, ix) {
37231                                     this.form.switchLang(rec.data.lang);
37232                                 }
37233                             }
37234                         
37235                         }
37236                     ]
37237                 }
37238                   
37239                 
37240             ]
37241         }
37242     ],
37243     buttons : [
37244         {
37245             xtype : 'Button',
37246             xns : 'Roo',
37247             text : "Forgot Password",
37248             listeners : {
37249                 click : function() {
37250                     //console.log(this);
37251                     var n = this.form.findField('username').getValue();
37252                     if (!n.length) {
37253                         Roo.MessageBox.alert("Error", "Fill in your email address");
37254                         return;
37255                     }
37256                     Roo.Ajax.request({
37257                         url: this.dialog.url,
37258                         params: {
37259                             passwordRequest: n
37260                         },
37261                         method: this.dialog.method,
37262                         success:  function(response, opts)  {  // check successfull...
37263                         
37264                             var res = this.dialog.processResponse(response);
37265                             if (!res.success) { // error!
37266                                Roo.MessageBox.alert("Error" ,
37267                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37268                                return;
37269                             }
37270                             Roo.MessageBox.alert("Notice" ,
37271                                 "Please check you email for the Password Reset message");
37272                         },
37273                         failure : function() {
37274                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37275                         }
37276                         
37277                     });
37278                 }
37279             }
37280         },
37281         {
37282             xtype : 'Button',
37283             xns : 'Roo',
37284             text : "Login",
37285             listeners : {
37286                 
37287                 click : function () {
37288                         
37289                     this.dialog.el.mask("Logging in");
37290                     this.form.doAction('submit', {
37291                             url: this.dialog.url,
37292                             method: this.dialog.method
37293                     });
37294                 }
37295             }
37296         }
37297     ]
37298   
37299   
37300 })
37301  
37302
37303
37304