roojs-ui.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  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
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                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
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 by providing an implementation.
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                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
3949          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3950          * @param {Event} e The event
3951          * @param {Object} data An object containing arbitrary data supplied by the drag source
3952          */
3953          "out" : true,
3954          
3955         /**
3956          * @event drop
3957          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3958          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3959          * implementation that does something to process the drop event and returns true so that the drag source's
3960          * repair action does not run.
3961          * 
3962          * IMPORTANT : it should set this.success
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967         */
3968          "drop" : true
3969     });
3970             
3971      
3972     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3973         this.el.dom, 
3974         this.ddGroup || this.group,
3975         {
3976             isTarget: true,
3977             listeners : listeners || {} 
3978            
3979         
3980         }
3981     );
3982
3983 };
3984
3985 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986     /**
3987      * @cfg {String} overClass
3988      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3989      */
3990      /**
3991      * @cfg {String} ddGroup
3992      * The drag drop group to handle drop events for
3993      */
3994      
3995     /**
3996      * @cfg {String} dropAllowed
3997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998      */
3999     dropAllowed : "x-dd-drop-ok",
4000     /**
4001      * @cfg {String} dropNotAllowed
4002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003      */
4004     dropNotAllowed : "x-dd-drop-nodrop",
4005     /**
4006      * @cfg {boolean} success
4007      * set this after drop listener.. 
4008      */
4009     success : false,
4010     /**
4011      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4012      * if the drop point is valid for over/enter..
4013      */
4014     valid : false,
4015     // private
4016     isTarget : true,
4017
4018     // private
4019     isNotifyTarget : true,
4020     
4021     /**
4022      * @hide
4023      */
4024     notifyEnter : function(dd, e, data)
4025     {
4026         this.valid = true;
4027         this.fireEvent('enter', dd, e, data);
4028         if(this.overClass){
4029             this.el.addClass(this.overClass);
4030         }
4031         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4032             this.valid ? this.dropAllowed : this.dropNotAllowed
4033         );
4034     },
4035
4036     /**
4037      * @hide
4038      */
4039     notifyOver : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('over', dd, e, data);
4043         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4044             this.valid ? this.dropAllowed : this.dropNotAllowed
4045         );
4046     },
4047
4048     /**
4049      * @hide
4050      */
4051     notifyOut : function(dd, e, data)
4052     {
4053         this.fireEvent('out', dd, e, data);
4054         if(this.overClass){
4055             this.el.removeClass(this.overClass);
4056         }
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyDrop : function(dd, e, data)
4063     {
4064         this.success = false;
4065         this.fireEvent('drop', dd, e, data);
4066         return this.success;
4067     }
4068 });/*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079
4080 /**
4081  * @class Roo.dd.DragZone
4082  * @extends Roo.dd.DragSource
4083  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4084  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085  * @constructor
4086  * @param {String/HTMLElement/Element} el The container element
4087  * @param {Object} config
4088  */
4089 Roo.dd.DragZone = function(el, config){
4090     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4091     if(this.containerScroll){
4092         Roo.dd.ScrollManager.register(this.el);
4093     }
4094 };
4095
4096 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097     /**
4098      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4099      * for auto scrolling during drag operations.
4100      */
4101     /**
4102      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4103      * method after a failed drop (defaults to "c3daf9" - light blue)
4104      */
4105
4106     /**
4107      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4108      * for a valid target to drag based on the mouse down. Override this method
4109      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4110      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4111      * @param {EventObject} e The mouse down event
4112      * @return {Object} The dragData
4113      */
4114     getDragData : function(e){
4115         return Roo.dd.Registry.getHandleFromEvent(e);
4116     },
4117     
4118     /**
4119      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4120      * this.dragData.ddel
4121      * @param {Number} x The x position of the click on the dragged object
4122      * @param {Number} y The y position of the click on the dragged object
4123      * @return {Boolean} true to continue the drag, false to cancel
4124      */
4125     onInitDrag : function(x, y){
4126         this.proxy.update(this.dragData.ddel.cloneNode(true));
4127         this.onStartDrag(x, y);
4128         return true;
4129     },
4130     
4131     /**
4132      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4133      */
4134     afterRepair : function(){
4135         if(Roo.enableFx){
4136             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137         }
4138         this.dragging = false;
4139     },
4140
4141     /**
4142      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4143      * the XY of this.dragData.ddel
4144      * @param {EventObject} e The mouse up event
4145      * @return {Array} The xy location (e.g. [100, 200])
4146      */
4147     getRepairXY : function(e){
4148         return Roo.Element.fly(this.dragData.ddel).getXY();  
4149     }
4150 });/*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160 /**
4161  * @class Roo.dd.DropZone
4162  * @extends Roo.dd.DropTarget
4163  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4164  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165  * @constructor
4166  * @param {String/HTMLElement/Element} el The container element
4167  * @param {Object} config
4168  */
4169 Roo.dd.DropZone = function(el, config){
4170     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4171 };
4172
4173 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174     /**
4175      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4176      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4177      * provide your own custom lookup.
4178      * @param {Event} e The event
4179      * @return {Object} data The custom data
4180      */
4181     getTargetFromEvent : function(e){
4182         return Roo.dd.Registry.getTargetFromEvent(e);
4183     },
4184
4185     /**
4186      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4187      * that it has registered.  This method has no default implementation and should be overridden to provide
4188      * node-specific processing if necessary.
4189      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4190      * {@link #getTargetFromEvent} for this node)
4191      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4192      * @param {Event} e The event
4193      * @param {Object} data An object containing arbitrary data supplied by the drag source
4194      */
4195     onNodeEnter : function(n, dd, e, data){
4196         
4197     },
4198
4199     /**
4200      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4201      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4202      * overridden to provide the proper feedback.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4209      * underlying {@link Roo.dd.StatusProxy} can be updated
4210      */
4211     onNodeOver : function(n, dd, e, data){
4212         return this.dropAllowed;
4213     },
4214
4215     /**
4216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4217      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4218      * node-specific processing if necessary.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      */
4225     onNodeOut : function(n, dd, e, data){
4226         
4227     },
4228
4229     /**
4230      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4231      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4232      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4233      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4234      * {@link #getTargetFromEvent} for this node)
4235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4236      * @param {Event} e The event
4237      * @param {Object} data An object containing arbitrary data supplied by the drag source
4238      * @return {Boolean} True if the drop was valid, else false
4239      */
4240     onNodeDrop : function(n, dd, e, data){
4241         return false;
4242     },
4243
4244     /**
4245      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4246      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4247      * it should be overridden to provide the proper feedback if necessary.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4252      * underlying {@link Roo.dd.StatusProxy} can be updated
4253      */
4254     onContainerOver : function(dd, e, data){
4255         return this.dropNotAllowed;
4256     },
4257
4258     /**
4259      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4260      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4261      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4262      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {Boolean} True if the drop was valid, else false
4267      */
4268     onContainerDrop : function(dd, e, data){
4269         return false;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4274      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4275      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4276      * you should override this method and provide a custom implementation.
4277      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4278      * @param {Event} e The event
4279      * @param {Object} data An object containing arbitrary data supplied by the drag source
4280      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4281      * underlying {@link Roo.dd.StatusProxy} can be updated
4282      */
4283     notifyEnter : function(dd, e, data){
4284         return this.dropNotAllowed;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4289      * This method will be called on every mouse movement while the drag source is over the drop zone.
4290      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4291      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4292      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4293      * registered node, it will call {@link #onContainerOver}.
4294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4295      * @param {Event} e The event
4296      * @param {Object} data An object containing arbitrary data supplied by the drag source
4297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4298      * underlying {@link Roo.dd.StatusProxy} can be updated
4299      */
4300     notifyOver : function(dd, e, data){
4301         var n = this.getTargetFromEvent(e);
4302         if(!n){ // not over valid drop target
4303             if(this.lastOverNode){
4304                 this.onNodeOut(this.lastOverNode, dd, e, data);
4305                 this.lastOverNode = null;
4306             }
4307             return this.onContainerOver(dd, e, data);
4308         }
4309         if(this.lastOverNode != n){
4310             if(this.lastOverNode){
4311                 this.onNodeOut(this.lastOverNode, dd, e, data);
4312             }
4313             this.onNodeEnter(n, dd, e, data);
4314             this.lastOverNode = n;
4315         }
4316         return this.onNodeOver(n, dd, e, data);
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4321      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4322      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326      */
4327     notifyOut : function(dd, e, data){
4328         if(this.lastOverNode){
4329             this.onNodeOut(this.lastOverNode, dd, e, data);
4330             this.lastOverNode = null;
4331         }
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4336      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4337      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4338      * otherwise it will call {@link #onContainerDrop}.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag source
4342      * @return {Boolean} True if the drop was valid, else false
4343      */
4344     notifyDrop : function(dd, e, data){
4345         if(this.lastOverNode){
4346             this.onNodeOut(this.lastOverNode, dd, e, data);
4347             this.lastOverNode = null;
4348         }
4349         var n = this.getTargetFromEvent(e);
4350         return n ?
4351             this.onNodeDrop(n, dd, e, data) :
4352             this.onContainerDrop(dd, e, data);
4353     },
4354
4355     // private
4356     triggerCacheRefresh : function(){
4357         Roo.dd.DDM.refreshCache(this.groups);
4358     }  
4359 });/*
4360  * Based on:
4361  * Ext JS Library 1.1.1
4362  * Copyright(c) 2006-2007, Ext JS, LLC.
4363  *
4364  * Originally Released Under LGPL - original licence link has changed is not relivant.
4365  *
4366  * Fork - LGPL
4367  * <script type="text/javascript">
4368  */
4369
4370
4371 /**
4372  * @class Roo.data.SortTypes
4373  * @singleton
4374  * Defines the default sorting (casting?) comparison functions used when sorting data.
4375  */
4376 Roo.data.SortTypes = {
4377     /**
4378      * Default sort that does nothing
4379      * @param {Mixed} s The value being converted
4380      * @return {Mixed} The comparison value
4381      */
4382     none : function(s){
4383         return s;
4384     },
4385     
4386     /**
4387      * The regular expression used to strip tags
4388      * @type {RegExp}
4389      * @property
4390      */
4391     stripTagsRE : /<\/?[^>]+>/gi,
4392     
4393     /**
4394      * Strips all HTML tags to sort on text only
4395      * @param {Mixed} s The value being converted
4396      * @return {String} The comparison value
4397      */
4398     asText : function(s){
4399         return String(s).replace(this.stripTagsRE, "");
4400     },
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only - Case insensitive
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asUCText : function(s){
4408         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Case insensitive string
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCString : function(s) {
4417         return String(s).toUpperCase();
4418     },
4419     
4420     /**
4421      * Date sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Number} The comparison value
4424      */
4425     asDate : function(s) {
4426         if(!s){
4427             return 0;
4428         }
4429         if(s instanceof Date){
4430             return s.getTime();
4431         }
4432         return Date.parse(String(s));
4433     },
4434     
4435     /**
4436      * Float sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Float} The comparison value
4439      */
4440     asFloat : function(s) {
4441         var val = parseFloat(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     },
4445     
4446     /**
4447      * Integer sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Number} The comparison value
4450      */
4451     asInt : function(s) {
4452         var val = parseInt(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     }
4456 };/*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466
4467 /**
4468 * @class Roo.data.Record
4469  * Instances of this class encapsulate both record <em>definition</em> information, and record
4470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4471  * to access Records cached in an {@link Roo.data.Store} object.<br>
4472  * <p>
4473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4475  * objects.<br>
4476  * <p>
4477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478  * @constructor
4479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4480  * {@link #create}. The parameters are the same.
4481  * @param {Array} data An associative Array of data values keyed by the field name.
4482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4484  * not specified an integer id is generated.
4485  */
4486 Roo.data.Record = function(data, id){
4487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4488     this.data = data;
4489 };
4490
4491 /**
4492  * Generate a constructor for a specific record layout.
4493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4495  * Each field definition object may contain the following properties: <ul>
4496  * <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,
4497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4503  * this may be omitted.</p></li>
4504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4505  * <ul><li>auto (Default, implies no conversion)</li>
4506  * <li>string</li>
4507  * <li>int</li>
4508  * <li>float</li>
4509  * <li>boolean</li>
4510  * <li>date</li></ul></p></li>
4511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4514  * by the Reader into an object that will be stored in the Record. It is passed the
4515  * following parameters:<ul>
4516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517  * </ul></p></li>
4518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519  * </ul>
4520  * <br>usage:<br><pre><code>
4521 var TopicRecord = Roo.data.Record.create(
4522     {name: 'title', mapping: 'topic_title'},
4523     {name: 'author', mapping: 'username'},
4524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4526     {name: 'lastPoster', mapping: 'user2'},
4527     {name: 'excerpt', mapping: 'post_text'}
4528 );
4529
4530 var myNewRecord = new TopicRecord({
4531     title: 'Do my job please',
4532     author: 'noobie',
4533     totalPosts: 1,
4534     lastPost: new Date(),
4535     lastPoster: 'Animal',
4536     excerpt: 'No way dude!'
4537 });
4538 myStore.add(myNewRecord);
4539 </code></pre>
4540  * @method create
4541  * @static
4542  */
4543 Roo.data.Record.create = function(o){
4544     var f = function(){
4545         f.superclass.constructor.apply(this, arguments);
4546     };
4547     Roo.extend(f, Roo.data.Record);
4548     var p = f.prototype;
4549     p.fields = new Roo.util.MixedCollection(false, function(field){
4550         return field.name;
4551     });
4552     for(var i = 0, len = o.length; i < len; i++){
4553         p.fields.add(new Roo.data.Field(o[i]));
4554     }
4555     f.getField = function(name){
4556         return p.fields.get(name);  
4557     };
4558     return f;
4559 };
4560
4561 Roo.data.Record.AUTO_ID = 1000;
4562 Roo.data.Record.EDIT = 'edit';
4563 Roo.data.Record.REJECT = 'reject';
4564 Roo.data.Record.COMMIT = 'commit';
4565
4566 Roo.data.Record.prototype = {
4567     /**
4568      * Readonly flag - true if this record has been modified.
4569      * @type Boolean
4570      */
4571     dirty : false,
4572     editing : false,
4573     error: null,
4574     modified: null,
4575
4576     // private
4577     join : function(store){
4578         this.store = store;
4579     },
4580
4581     /**
4582      * Set the named field to the specified value.
4583      * @param {String} name The name of the field to set.
4584      * @param {Object} value The value to set the field to.
4585      */
4586     set : function(name, value){
4587         if(this.data[name] == value){
4588             return;
4589         }
4590         this.dirty = true;
4591         if(!this.modified){
4592             this.modified = {};
4593         }
4594         if(typeof this.modified[name] == 'undefined'){
4595             this.modified[name] = this.data[name];
4596         }
4597         this.data[name] = value;
4598         if(!this.editing && this.store){
4599             this.store.afterEdit(this);
4600         }       
4601     },
4602
4603     /**
4604      * Get the value of the named field.
4605      * @param {String} name The name of the field to get the value of.
4606      * @return {Object} The value of the field.
4607      */
4608     get : function(name){
4609         return this.data[name]; 
4610     },
4611
4612     // private
4613     beginEdit : function(){
4614         this.editing = true;
4615         this.modified = {}; 
4616     },
4617
4618     // private
4619     cancelEdit : function(){
4620         this.editing = false;
4621         delete this.modified;
4622     },
4623
4624     // private
4625     endEdit : function(){
4626         this.editing = false;
4627         if(this.dirty && this.store){
4628             this.store.afterEdit(this);
4629         }
4630     },
4631
4632     /**
4633      * Usually called by the {@link Roo.data.Store} which owns the Record.
4634      * Rejects all changes made to the Record since either creation, or the last commit operation.
4635      * Modified fields are reverted to their original values.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of reject operations.
4639      */
4640     reject : function(){
4641         var m = this.modified;
4642         for(var n in m){
4643             if(typeof m[n] != "function"){
4644                 this.data[n] = m[n];
4645             }
4646         }
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterReject(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Commits all changes made to the Record since either creation, or the last commit operation.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of commit operations.
4661      */
4662     commit : function(){
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterCommit(this);
4668         }
4669     },
4670
4671     // private
4672     hasError : function(){
4673         return this.error != null;
4674     },
4675
4676     // private
4677     clearError : function(){
4678         this.error = null;
4679     },
4680
4681     /**
4682      * Creates a copy of this record.
4683      * @param {String} id (optional) A new record id if you don't want to use this record's id
4684      * @return {Record}
4685      */
4686     copy : function(newId) {
4687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4688     }
4689 };/*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700
4701
4702 /**
4703  * @class Roo.data.Store
4704  * @extends Roo.util.Observable
4705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707  * <p>
4708  * 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
4709  * has no knowledge of the format of the data returned by the Proxy.<br>
4710  * <p>
4711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4712  * instances from the data object. These records are cached and made available through accessor functions.
4713  * @constructor
4714  * Creates a new Store.
4715  * @param {Object} config A config object containing the objects needed for the Store to access data,
4716  * and read the data into Records.
4717  */
4718 Roo.data.Store = function(config){
4719     this.data = new Roo.util.MixedCollection(false);
4720     this.data.getKey = function(o){
4721         return o.id;
4722     };
4723     this.baseParams = {};
4724     // private
4725     this.paramNames = {
4726         "start" : "start",
4727         "limit" : "limit",
4728         "sort" : "sort",
4729         "dir" : "dir",
4730         "multisort" : "_multisort"
4731     };
4732
4733     if(config && config.data){
4734         this.inlineData = config.data;
4735         delete config.data;
4736     }
4737
4738     Roo.apply(this, config);
4739     
4740     if(this.reader){ // reader passed
4741         this.reader = Roo.factory(this.reader, Roo.data);
4742         this.reader.xmodule = this.xmodule || false;
4743         if(!this.recordType){
4744             this.recordType = this.reader.recordType;
4745         }
4746         if(this.reader.onMetaChange){
4747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4748         }
4749     }
4750
4751     if(this.recordType){
4752         this.fields = this.recordType.prototype.fields;
4753     }
4754     this.modified = [];
4755
4756     this.addEvents({
4757         /**
4758          * @event datachanged
4759          * Fires when the data cache has changed, and a widget which is using this Store
4760          * as a Record cache should refresh its view.
4761          * @param {Store} this
4762          */
4763         datachanged : true,
4764         /**
4765          * @event metachange
4766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4767          * @param {Store} this
4768          * @param {Object} meta The JSON metadata
4769          */
4770         metachange : true,
4771         /**
4772          * @event add
4773          * Fires when Records have been added to the Store
4774          * @param {Store} this
4775          * @param {Roo.data.Record[]} records The array of Records added
4776          * @param {Number} index The index at which the record(s) were added
4777          */
4778         add : true,
4779         /**
4780          * @event remove
4781          * Fires when a Record has been removed from the Store
4782          * @param {Store} this
4783          * @param {Roo.data.Record} record The Record that was removed
4784          * @param {Number} index The index at which the record was removed
4785          */
4786         remove : true,
4787         /**
4788          * @event update
4789          * Fires when a Record has been updated
4790          * @param {Store} this
4791          * @param {Roo.data.Record} record The Record that was updated
4792          * @param {String} operation The update operation being performed.  Value may be one of:
4793          * <pre><code>
4794  Roo.data.Record.EDIT
4795  Roo.data.Record.REJECT
4796  Roo.data.Record.COMMIT
4797          * </code></pre>
4798          */
4799         update : true,
4800         /**
4801          * @event clear
4802          * Fires when the data cache has been cleared.
4803          * @param {Store} this
4804          */
4805         clear : true,
4806         /**
4807          * @event beforeload
4808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4809          * the load action will be canceled.
4810          * @param {Store} this
4811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4812          */
4813         beforeload : true,
4814         /**
4815          * @event beforeloadadd
4816          * Fires after a new set of Records has been loaded.
4817          * @param {Store} this
4818          * @param {Roo.data.Record[]} records The Records that were loaded
4819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4820          */
4821         beforeloadadd : true,
4822         /**
4823          * @event load
4824          * Fires after a new set of Records has been loaded, before they are added to the store.
4825          * @param {Store} this
4826          * @param {Roo.data.Record[]} records The Records that were loaded
4827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4828          * @params {Object} return from reader
4829          */
4830         load : true,
4831         /**
4832          * @event loadexception
4833          * Fires if an exception occurs in the Proxy during loading.
4834          * Called with the signature of the Proxy's "loadexception" event.
4835          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4836          * 
4837          * @param {Proxy} 
4838          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4839          * @param {Object} load options 
4840          * @param {Object} jsonData from your request (normally this contains the Exception)
4841          */
4842         loadexception : true
4843     });
4844     
4845     if(this.proxy){
4846         this.proxy = Roo.factory(this.proxy, Roo.data);
4847         this.proxy.xmodule = this.xmodule || false;
4848         this.relayEvents(this.proxy,  ["loadexception"]);
4849     }
4850     this.sortToggle = {};
4851     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4852
4853     Roo.data.Store.superclass.constructor.call(this);
4854
4855     if(this.inlineData){
4856         this.loadData(this.inlineData);
4857         delete this.inlineData;
4858     }
4859 };
4860
4861 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4862      /**
4863     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4864     * without a remote query - used by combo/forms at present.
4865     */
4866     
4867     /**
4868     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4869     */
4870     /**
4871     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4872     */
4873     /**
4874     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4875     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4876     */
4877     /**
4878     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4879     * on any HTTP request
4880     */
4881     /**
4882     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4883     */
4884     /**
4885     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4886     */
4887     multiSort: false,
4888     /**
4889     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4890     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4891     */
4892     remoteSort : false,
4893
4894     /**
4895     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4896      * loaded or when a record is removed. (defaults to false).
4897     */
4898     pruneModifiedRecords : false,
4899
4900     // private
4901     lastOptions : null,
4902
4903     /**
4904      * Add Records to the Store and fires the add event.
4905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4906      */
4907     add : function(records){
4908         records = [].concat(records);
4909         for(var i = 0, len = records.length; i < len; i++){
4910             records[i].join(this);
4911         }
4912         var index = this.data.length;
4913         this.data.addAll(records);
4914         this.fireEvent("add", this, records, index);
4915     },
4916
4917     /**
4918      * Remove a Record from the Store and fires the remove event.
4919      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4920      */
4921     remove : function(record){
4922         var index = this.data.indexOf(record);
4923         this.data.removeAt(index);
4924         if(this.pruneModifiedRecords){
4925             this.modified.remove(record);
4926         }
4927         this.fireEvent("remove", this, record, index);
4928     },
4929
4930     /**
4931      * Remove all Records from the Store and fires the clear event.
4932      */
4933     removeAll : function(){
4934         this.data.clear();
4935         if(this.pruneModifiedRecords){
4936             this.modified = [];
4937         }
4938         this.fireEvent("clear", this);
4939     },
4940
4941     /**
4942      * Inserts Records to the Store at the given index and fires the add event.
4943      * @param {Number} index The start index at which to insert the passed Records.
4944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4945      */
4946     insert : function(index, records){
4947         records = [].concat(records);
4948         for(var i = 0, len = records.length; i < len; i++){
4949             this.data.insert(index, records[i]);
4950             records[i].join(this);
4951         }
4952         this.fireEvent("add", this, records, index);
4953     },
4954
4955     /**
4956      * Get the index within the cache of the passed Record.
4957      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4958      * @return {Number} The index of the passed Record. Returns -1 if not found.
4959      */
4960     indexOf : function(record){
4961         return this.data.indexOf(record);
4962     },
4963
4964     /**
4965      * Get the index within the cache of the Record with the passed id.
4966      * @param {String} id The id of the Record to find.
4967      * @return {Number} The index of the Record. Returns -1 if not found.
4968      */
4969     indexOfId : function(id){
4970         return this.data.indexOfKey(id);
4971     },
4972
4973     /**
4974      * Get the Record with the specified id.
4975      * @param {String} id The id of the Record to find.
4976      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4977      */
4978     getById : function(id){
4979         return this.data.key(id);
4980     },
4981
4982     /**
4983      * Get the Record at the specified index.
4984      * @param {Number} index The index of the Record to find.
4985      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4986      */
4987     getAt : function(index){
4988         return this.data.itemAt(index);
4989     },
4990
4991     /**
4992      * Returns a range of Records between specified indices.
4993      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4994      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4995      * @return {Roo.data.Record[]} An array of Records
4996      */
4997     getRange : function(start, end){
4998         return this.data.getRange(start, end);
4999     },
5000
5001     // private
5002     storeOptions : function(o){
5003         o = Roo.apply({}, o);
5004         delete o.callback;
5005         delete o.scope;
5006         this.lastOptions = o;
5007     },
5008
5009     /**
5010      * Loads the Record cache from the configured Proxy using the configured Reader.
5011      * <p>
5012      * If using remote paging, then the first load call must specify the <em>start</em>
5013      * and <em>limit</em> properties in the options.params property to establish the initial
5014      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5015      * <p>
5016      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5017      * and this call will return before the new data has been loaded. Perform any post-processing
5018      * in a callback function, or in a "load" event handler.</strong>
5019      * <p>
5020      * @param {Object} options An object containing properties which control loading options:<ul>
5021      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5022      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5023      * passed the following arguments:<ul>
5024      * <li>r : Roo.data.Record[]</li>
5025      * <li>options: Options object from the load call</li>
5026      * <li>success: Boolean success indicator</li></ul></li>
5027      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5028      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5029      * </ul>
5030      */
5031     load : function(options){
5032         options = options || {};
5033         if(this.fireEvent("beforeload", this, options) !== false){
5034             this.storeOptions(options);
5035             var p = Roo.apply(options.params || {}, this.baseParams);
5036             // if meta was not loaded from remote source.. try requesting it.
5037             if (!this.reader.metaFromRemote) {
5038                 p._requestMeta = 1;
5039             }
5040             if(this.sortInfo && this.remoteSort){
5041                 var pn = this.paramNames;
5042                 p[pn["sort"]] = this.sortInfo.field;
5043                 p[pn["dir"]] = this.sortInfo.direction;
5044             }
5045             if (this.multiSort) {
5046                 var pn = this.paramNames;
5047                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5048             }
5049             
5050             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5051         }
5052     },
5053
5054     /**
5055      * Reloads the Record cache from the configured Proxy using the configured Reader and
5056      * the options from the last load operation performed.
5057      * @param {Object} options (optional) An object containing properties which may override the options
5058      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5059      * the most recently used options are reused).
5060      */
5061     reload : function(options){
5062         this.load(Roo.applyIf(options||{}, this.lastOptions));
5063     },
5064
5065     // private
5066     // Called as a callback by the Reader during a load operation.
5067     loadRecords : function(o, options, success){
5068         if(!o || success === false){
5069             if(success !== false){
5070                 this.fireEvent("load", this, [], options, o);
5071             }
5072             if(options.callback){
5073                 options.callback.call(options.scope || this, [], options, false);
5074             }
5075             return;
5076         }
5077         // if data returned failure - throw an exception.
5078         if (o.success === false) {
5079             // show a message if no listener is registered.
5080             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5081                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5082             }
5083             // loadmask wil be hooked into this..
5084             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5085             return;
5086         }
5087         var r = o.records, t = o.totalRecords || r.length;
5088         
5089         this.fireEvent("beforeloadadd", this, r, options, o);
5090         
5091         if(!options || options.add !== true){
5092             if(this.pruneModifiedRecords){
5093                 this.modified = [];
5094             }
5095             for(var i = 0, len = r.length; i < len; i++){
5096                 r[i].join(this);
5097             }
5098             if(this.snapshot){
5099                 this.data = this.snapshot;
5100                 delete this.snapshot;
5101             }
5102             this.data.clear();
5103             this.data.addAll(r);
5104             this.totalLength = t;
5105             this.applySort();
5106             this.fireEvent("datachanged", this);
5107         }else{
5108             this.totalLength = Math.max(t, this.data.length+r.length);
5109             this.add(r);
5110         }
5111         this.fireEvent("load", this, r, options, o);
5112         if(options.callback){
5113             options.callback.call(options.scope || this, r, options, true);
5114         }
5115     },
5116
5117
5118     /**
5119      * Loads data from a passed data block. A Reader which understands the format of the data
5120      * must have been configured in the constructor.
5121      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5122      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5123      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5124      */
5125     loadData : function(o, append){
5126         var r = this.reader.readRecords(o);
5127         this.loadRecords(r, {add: append}, true);
5128     },
5129
5130     /**
5131      * Gets the number of cached records.
5132      * <p>
5133      * <em>If using paging, this may not be the total size of the dataset. If the data object
5134      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5135      * the data set size</em>
5136      */
5137     getCount : function(){
5138         return this.data.length || 0;
5139     },
5140
5141     /**
5142      * Gets the total number of records in the dataset as returned by the server.
5143      * <p>
5144      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5145      * the dataset size</em>
5146      */
5147     getTotalCount : function(){
5148         return this.totalLength || 0;
5149     },
5150
5151     /**
5152      * Returns the sort state of the Store as an object with two properties:
5153      * <pre><code>
5154  field {String} The name of the field by which the Records are sorted
5155  direction {String} The sort order, "ASC" or "DESC"
5156      * </code></pre>
5157      */
5158     getSortState : function(){
5159         return this.sortInfo;
5160     },
5161
5162     // private
5163     applySort : function(){
5164         if(this.sortInfo && !this.remoteSort){
5165             var s = this.sortInfo, f = s.field;
5166             var st = this.fields.get(f).sortType;
5167             var fn = function(r1, r2){
5168                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5169                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5170             };
5171             this.data.sort(s.direction, fn);
5172             if(this.snapshot && this.snapshot != this.data){
5173                 this.snapshot.sort(s.direction, fn);
5174             }
5175         }
5176     },
5177
5178     /**
5179      * Sets the default sort column and order to be used by the next load operation.
5180      * @param {String} fieldName The name of the field to sort by.
5181      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5182      */
5183     setDefaultSort : function(field, dir){
5184         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5185     },
5186
5187     /**
5188      * Sort the Records.
5189      * If remote sorting is used, the sort is performed on the server, and the cache is
5190      * reloaded. If local sorting is used, the cache is sorted internally.
5191      * @param {String} fieldName The name of the field to sort by.
5192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5193      */
5194     sort : function(fieldName, dir){
5195         var f = this.fields.get(fieldName);
5196         if(!dir){
5197             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5198             
5199             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5200                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5201             }else{
5202                 dir = f.sortDir;
5203             }
5204         }
5205         this.sortToggle[f.name] = dir;
5206         this.sortInfo = {field: f.name, direction: dir};
5207         if(!this.remoteSort){
5208             this.applySort();
5209             this.fireEvent("datachanged", this);
5210         }else{
5211             this.load(this.lastOptions);
5212         }
5213     },
5214
5215     /**
5216      * Calls the specified function for each of the Records in the cache.
5217      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5218      * Returning <em>false</em> aborts and exits the iteration.
5219      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5220      */
5221     each : function(fn, scope){
5222         this.data.each(fn, scope);
5223     },
5224
5225     /**
5226      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5227      * (e.g., during paging).
5228      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5229      */
5230     getModifiedRecords : function(){
5231         return this.modified;
5232     },
5233
5234     // private
5235     createFilterFn : function(property, value, anyMatch){
5236         if(!value.exec){ // not a regex
5237             value = String(value);
5238             if(value.length == 0){
5239                 return false;
5240             }
5241             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5242         }
5243         return function(r){
5244             return value.test(r.data[property]);
5245         };
5246     },
5247
5248     /**
5249      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5250      * @param {String} property A field on your records
5251      * @param {Number} start The record index to start at (defaults to 0)
5252      * @param {Number} end The last record index to include (defaults to length - 1)
5253      * @return {Number} The sum
5254      */
5255     sum : function(property, start, end){
5256         var rs = this.data.items, v = 0;
5257         start = start || 0;
5258         end = (end || end === 0) ? end : rs.length-1;
5259
5260         for(var i = start; i <= end; i++){
5261             v += (rs[i].data[property] || 0);
5262         }
5263         return v;
5264     },
5265
5266     /**
5267      * Filter the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      */
5273     filter : function(property, value, anyMatch){
5274         var fn = this.createFilterFn(property, value, anyMatch);
5275         return fn ? this.filterBy(fn) : this.clearFilter();
5276     },
5277
5278     /**
5279      * Filter by a function. The specified function will be called with each
5280      * record in this data source. If the function returns true the record is included,
5281      * otherwise it is filtered.
5282      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5283      * @param {Object} scope (optional) The scope of the function (defaults to this)
5284      */
5285     filterBy : function(fn, scope){
5286         this.snapshot = this.snapshot || this.data;
5287         this.data = this.queryBy(fn, scope||this);
5288         this.fireEvent("datachanged", this);
5289     },
5290
5291     /**
5292      * Query the records by a specified property.
5293      * @param {String} field A field on your records
5294      * @param {String/RegExp} value Either a string that the field
5295      * should start with or a RegExp to test against the field
5296      * @param {Boolean} anyMatch True to match any part not just the beginning
5297      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      */
5299     query : function(property, value, anyMatch){
5300         var fn = this.createFilterFn(property, value, anyMatch);
5301         return fn ? this.queryBy(fn) : this.data.clone();
5302     },
5303
5304     /**
5305      * Query by a function. The specified function will be called with each
5306      * record in this data source. If the function returns true the record is included
5307      * in the results.
5308      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5309      * @param {Object} scope (optional) The scope of the function (defaults to this)
5310       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5311      **/
5312     queryBy : function(fn, scope){
5313         var data = this.snapshot || this.data;
5314         return data.filterBy(fn, scope||this);
5315     },
5316
5317     /**
5318      * Collects unique values for a particular dataIndex from this store.
5319      * @param {String} dataIndex The property to collect
5320      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5321      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5322      * @return {Array} An array of the unique values
5323      **/
5324     collect : function(dataIndex, allowNull, bypassFilter){
5325         var d = (bypassFilter === true && this.snapshot) ?
5326                 this.snapshot.items : this.data.items;
5327         var v, sv, r = [], l = {};
5328         for(var i = 0, len = d.length; i < len; i++){
5329             v = d[i].data[dataIndex];
5330             sv = String(v);
5331             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5332                 l[sv] = true;
5333                 r[r.length] = v;
5334             }
5335         }
5336         return r;
5337     },
5338
5339     /**
5340      * Revert to a view of the Record cache with no filtering applied.
5341      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5342      */
5343     clearFilter : function(suppressEvent){
5344         if(this.snapshot && this.snapshot != this.data){
5345             this.data = this.snapshot;
5346             delete this.snapshot;
5347             if(suppressEvent !== true){
5348                 this.fireEvent("datachanged", this);
5349             }
5350         }
5351     },
5352
5353     // private
5354     afterEdit : function(record){
5355         if(this.modified.indexOf(record) == -1){
5356             this.modified.push(record);
5357         }
5358         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5359     },
5360     
5361     // private
5362     afterReject : function(record){
5363         this.modified.remove(record);
5364         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5365     },
5366
5367     // private
5368     afterCommit : function(record){
5369         this.modified.remove(record);
5370         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5371     },
5372
5373     /**
5374      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5375      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5376      */
5377     commitChanges : function(){
5378         var m = this.modified.slice(0);
5379         this.modified = [];
5380         for(var i = 0, len = m.length; i < len; i++){
5381             m[i].commit();
5382         }
5383     },
5384
5385     /**
5386      * Cancel outstanding changes on all changed records.
5387      */
5388     rejectChanges : function(){
5389         var m = this.modified.slice(0);
5390         this.modified = [];
5391         for(var i = 0, len = m.length; i < len; i++){
5392             m[i].reject();
5393         }
5394     },
5395
5396     onMetaChange : function(meta, rtype, o){
5397         this.recordType = rtype;
5398         this.fields = rtype.prototype.fields;
5399         delete this.snapshot;
5400         this.sortInfo = meta.sortInfo || this.sortInfo;
5401         this.modified = [];
5402         this.fireEvent('metachange', this, this.reader.meta);
5403     }
5404 });/*
5405  * Based on:
5406  * Ext JS Library 1.1.1
5407  * Copyright(c) 2006-2007, Ext JS, LLC.
5408  *
5409  * Originally Released Under LGPL - original licence link has changed is not relivant.
5410  *
5411  * Fork - LGPL
5412  * <script type="text/javascript">
5413  */
5414
5415 /**
5416  * @class Roo.data.SimpleStore
5417  * @extends Roo.data.Store
5418  * Small helper class to make creating Stores from Array data easier.
5419  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5420  * @cfg {Array} fields An array of field definition objects, or field name strings.
5421  * @cfg {Array} data The multi-dimensional array of data
5422  * @constructor
5423  * @param {Object} config
5424  */
5425 Roo.data.SimpleStore = function(config){
5426     Roo.data.SimpleStore.superclass.constructor.call(this, {
5427         isLocal : true,
5428         reader: new Roo.data.ArrayReader({
5429                 id: config.id
5430             },
5431             Roo.data.Record.create(config.fields)
5432         ),
5433         proxy : new Roo.data.MemoryProxy(config.data)
5434     });
5435     this.load();
5436 };
5437 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5438  * Based on:
5439  * Ext JS Library 1.1.1
5440  * Copyright(c) 2006-2007, Ext JS, LLC.
5441  *
5442  * Originally Released Under LGPL - original licence link has changed is not relivant.
5443  *
5444  * Fork - LGPL
5445  * <script type="text/javascript">
5446  */
5447
5448 /**
5449 /**
5450  * @extends Roo.data.Store
5451  * @class Roo.data.JsonStore
5452  * Small helper class to make creating Stores for JSON data easier. <br/>
5453 <pre><code>
5454 var store = new Roo.data.JsonStore({
5455     url: 'get-images.php',
5456     root: 'images',
5457     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5458 });
5459 </code></pre>
5460  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5461  * JsonReader and HttpProxy (unless inline data is provided).</b>
5462  * @cfg {Array} fields An array of field definition objects, or field name strings.
5463  * @constructor
5464  * @param {Object} config
5465  */
5466 Roo.data.JsonStore = function(c){
5467     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5468         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5469         reader: new Roo.data.JsonReader(c, c.fields)
5470     }));
5471 };
5472 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483  
5484 Roo.data.Field = function(config){
5485     if(typeof config == "string"){
5486         config = {name: config};
5487     }
5488     Roo.apply(this, config);
5489     
5490     if(!this.type){
5491         this.type = "auto";
5492     }
5493     
5494     var st = Roo.data.SortTypes;
5495     // named sortTypes are supported, here we look them up
5496     if(typeof this.sortType == "string"){
5497         this.sortType = st[this.sortType];
5498     }
5499     
5500     // set default sortType for strings and dates
5501     if(!this.sortType){
5502         switch(this.type){
5503             case "string":
5504                 this.sortType = st.asUCString;
5505                 break;
5506             case "date":
5507                 this.sortType = st.asDate;
5508                 break;
5509             default:
5510                 this.sortType = st.none;
5511         }
5512     }
5513
5514     // define once
5515     var stripRe = /[\$,%]/g;
5516
5517     // prebuilt conversion function for this field, instead of
5518     // switching every time we're reading a value
5519     if(!this.convert){
5520         var cv, dateFormat = this.dateFormat;
5521         switch(this.type){
5522             case "":
5523             case "auto":
5524             case undefined:
5525                 cv = function(v){ return v; };
5526                 break;
5527             case "string":
5528                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5529                 break;
5530             case "int":
5531                 cv = function(v){
5532                     return v !== undefined && v !== null && v !== '' ?
5533                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5534                     };
5535                 break;
5536             case "float":
5537                 cv = function(v){
5538                     return v !== undefined && v !== null && v !== '' ?
5539                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5540                     };
5541                 break;
5542             case "bool":
5543             case "boolean":
5544                 cv = function(v){ return v === true || v === "true" || v == 1; };
5545                 break;
5546             case "date":
5547                 cv = function(v){
5548                     if(!v){
5549                         return '';
5550                     }
5551                     if(v instanceof Date){
5552                         return v;
5553                     }
5554                     if(dateFormat){
5555                         if(dateFormat == "timestamp"){
5556                             return new Date(v*1000);
5557                         }
5558                         return Date.parseDate(v, dateFormat);
5559                     }
5560                     var parsed = Date.parse(v);
5561                     return parsed ? new Date(parsed) : null;
5562                 };
5563              break;
5564             
5565         }
5566         this.convert = cv;
5567     }
5568 };
5569
5570 Roo.data.Field.prototype = {
5571     dateFormat: null,
5572     defaultValue: "",
5573     mapping: null,
5574     sortType : null,
5575     sortDir : "ASC"
5576 };/*
5577  * Based on:
5578  * Ext JS Library 1.1.1
5579  * Copyright(c) 2006-2007, Ext JS, LLC.
5580  *
5581  * Originally Released Under LGPL - original licence link has changed is not relivant.
5582  *
5583  * Fork - LGPL
5584  * <script type="text/javascript">
5585  */
5586  
5587 // Base class for reading structured data from a data source.  This class is intended to be
5588 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5589
5590 /**
5591  * @class Roo.data.DataReader
5592  * Base class for reading structured data from a data source.  This class is intended to be
5593  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5594  */
5595
5596 Roo.data.DataReader = function(meta, recordType){
5597     
5598     this.meta = meta;
5599     
5600     this.recordType = recordType instanceof Array ? 
5601         Roo.data.Record.create(recordType) : recordType;
5602 };
5603
5604 Roo.data.DataReader.prototype = {
5605      /**
5606      * Create an empty record
5607      * @param {Object} data (optional) - overlay some values
5608      * @return {Roo.data.Record} record created.
5609      */
5610     newRow :  function(d) {
5611         var da =  {};
5612         this.recordType.prototype.fields.each(function(c) {
5613             switch( c.type) {
5614                 case 'int' : da[c.name] = 0; break;
5615                 case 'date' : da[c.name] = new Date(); break;
5616                 case 'float' : da[c.name] = 0.0; break;
5617                 case 'boolean' : da[c.name] = false; break;
5618                 default : da[c.name] = ""; break;
5619             }
5620             
5621         });
5622         return new this.recordType(Roo.apply(da, d));
5623     }
5624     
5625 };/*
5626  * Based on:
5627  * Ext JS Library 1.1.1
5628  * Copyright(c) 2006-2007, Ext JS, LLC.
5629  *
5630  * Originally Released Under LGPL - original licence link has changed is not relivant.
5631  *
5632  * Fork - LGPL
5633  * <script type="text/javascript">
5634  */
5635
5636 /**
5637  * @class Roo.data.DataProxy
5638  * @extends Roo.data.Observable
5639  * This class is an abstract base class for implementations which provide retrieval of
5640  * unformatted data objects.<br>
5641  * <p>
5642  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5643  * (of the appropriate type which knows how to parse the data object) to provide a block of
5644  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5645  * <p>
5646  * Custom implementations must implement the load method as described in
5647  * {@link Roo.data.HttpProxy#load}.
5648  */
5649 Roo.data.DataProxy = function(){
5650     this.addEvents({
5651         /**
5652          * @event beforeload
5653          * Fires before a network request is made to retrieve a data object.
5654          * @param {Object} This DataProxy object.
5655          * @param {Object} params The params parameter to the load function.
5656          */
5657         beforeload : true,
5658         /**
5659          * @event load
5660          * Fires before the load method's callback is called.
5661          * @param {Object} This DataProxy object.
5662          * @param {Object} o The data object.
5663          * @param {Object} arg The callback argument object passed to the load function.
5664          */
5665         load : true,
5666         /**
5667          * @event loadexception
5668          * Fires if an Exception occurs during data retrieval.
5669          * @param {Object} This DataProxy object.
5670          * @param {Object} o The data object.
5671          * @param {Object} arg The callback argument object passed to the load function.
5672          * @param {Object} e The Exception.
5673          */
5674         loadexception : true
5675     });
5676     Roo.data.DataProxy.superclass.constructor.call(this);
5677 };
5678
5679 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5680
5681     /**
5682      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5683      */
5684 /*
5685  * Based on:
5686  * Ext JS Library 1.1.1
5687  * Copyright(c) 2006-2007, Ext JS, LLC.
5688  *
5689  * Originally Released Under LGPL - original licence link has changed is not relivant.
5690  *
5691  * Fork - LGPL
5692  * <script type="text/javascript">
5693  */
5694 /**
5695  * @class Roo.data.MemoryProxy
5696  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5697  * to the Reader when its load method is called.
5698  * @constructor
5699  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5700  */
5701 Roo.data.MemoryProxy = function(data){
5702     if (data.data) {
5703         data = data.data;
5704     }
5705     Roo.data.MemoryProxy.superclass.constructor.call(this);
5706     this.data = data;
5707 };
5708
5709 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5710     /**
5711      * Load data from the requested source (in this case an in-memory
5712      * data object passed to the constructor), read the data object into
5713      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5714      * process that block using the passed callback.
5715      * @param {Object} params This parameter is not used by the MemoryProxy class.
5716      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5717      * object into a block of Roo.data.Records.
5718      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5719      * The function must be passed <ul>
5720      * <li>The Record block object</li>
5721      * <li>The "arg" argument from the load function</li>
5722      * <li>A boolean success indicator</li>
5723      * </ul>
5724      * @param {Object} scope The scope in which to call the callback
5725      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5726      */
5727     load : function(params, reader, callback, scope, arg){
5728         params = params || {};
5729         var result;
5730         try {
5731             result = reader.readRecords(this.data);
5732         }catch(e){
5733             this.fireEvent("loadexception", this, arg, null, e);
5734             callback.call(scope, null, arg, false);
5735             return;
5736         }
5737         callback.call(scope, result, arg, true);
5738     },
5739     
5740     // private
5741     update : function(params, records){
5742         
5743     }
5744 });/*
5745  * Based on:
5746  * Ext JS Library 1.1.1
5747  * Copyright(c) 2006-2007, Ext JS, LLC.
5748  *
5749  * Originally Released Under LGPL - original licence link has changed is not relivant.
5750  *
5751  * Fork - LGPL
5752  * <script type="text/javascript">
5753  */
5754 /**
5755  * @class Roo.data.HttpProxy
5756  * @extends Roo.data.DataProxy
5757  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5758  * configured to reference a certain URL.<br><br>
5759  * <p>
5760  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5761  * from which the running page was served.<br><br>
5762  * <p>
5763  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5764  * <p>
5765  * Be aware that to enable the browser to parse an XML document, the server must set
5766  * the Content-Type header in the HTTP response to "text/xml".
5767  * @constructor
5768  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5769  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5770  * will be used to make the request.
5771  */
5772 Roo.data.HttpProxy = function(conn){
5773     Roo.data.HttpProxy.superclass.constructor.call(this);
5774     // is conn a conn config or a real conn?
5775     this.conn = conn;
5776     this.useAjax = !conn || !conn.events;
5777   
5778 };
5779
5780 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5781     // thse are take from connection...
5782     
5783     /**
5784      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5785      */
5786     /**
5787      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5788      * extra parameters to each request made by this object. (defaults to undefined)
5789      */
5790     /**
5791      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5792      *  to each request made by this object. (defaults to undefined)
5793      */
5794     /**
5795      * @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)
5796      */
5797     /**
5798      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5799      */
5800      /**
5801      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5802      * @type Boolean
5803      */
5804   
5805
5806     /**
5807      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5808      * @type Boolean
5809      */
5810     /**
5811      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5812      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5813      * a finer-grained basis than the DataProxy events.
5814      */
5815     getConnection : function(){
5816         return this.useAjax ? Roo.Ajax : this.conn;
5817     },
5818
5819     /**
5820      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5821      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5822      * process that block using the passed callback.
5823      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5824      * for the request to the remote server.
5825      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5826      * object into a block of Roo.data.Records.
5827      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5828      * The function must be passed <ul>
5829      * <li>The Record block object</li>
5830      * <li>The "arg" argument from the load function</li>
5831      * <li>A boolean success indicator</li>
5832      * </ul>
5833      * @param {Object} scope The scope in which to call the callback
5834      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5835      */
5836     load : function(params, reader, callback, scope, arg){
5837         if(this.fireEvent("beforeload", this, params) !== false){
5838             var  o = {
5839                 params : params || {},
5840                 request: {
5841                     callback : callback,
5842                     scope : scope,
5843                     arg : arg
5844                 },
5845                 reader: reader,
5846                 callback : this.loadResponse,
5847                 scope: this
5848             };
5849             if(this.useAjax){
5850                 Roo.applyIf(o, this.conn);
5851                 if(this.activeRequest){
5852                     Roo.Ajax.abort(this.activeRequest);
5853                 }
5854                 this.activeRequest = Roo.Ajax.request(o);
5855             }else{
5856                 this.conn.request(o);
5857             }
5858         }else{
5859             callback.call(scope||this, null, arg, false);
5860         }
5861     },
5862
5863     // private
5864     loadResponse : function(o, success, response){
5865         delete this.activeRequest;
5866         if(!success){
5867             this.fireEvent("loadexception", this, o, response);
5868             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5869             return;
5870         }
5871         var result;
5872         try {
5873             result = o.reader.read(response);
5874         }catch(e){
5875             this.fireEvent("loadexception", this, o, response, e);
5876             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5877             return;
5878         }
5879         
5880         this.fireEvent("load", this, o, o.request.arg);
5881         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5882     },
5883
5884     // private
5885     update : function(dataSet){
5886
5887     },
5888
5889     // private
5890     updateResponse : function(dataSet){
5891
5892     }
5893 });/*
5894  * Based on:
5895  * Ext JS Library 1.1.1
5896  * Copyright(c) 2006-2007, Ext JS, LLC.
5897  *
5898  * Originally Released Under LGPL - original licence link has changed is not relivant.
5899  *
5900  * Fork - LGPL
5901  * <script type="text/javascript">
5902  */
5903
5904 /**
5905  * @class Roo.data.ScriptTagProxy
5906  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5907  * other than the originating domain of the running page.<br><br>
5908  * <p>
5909  * <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
5910  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5911  * <p>
5912  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5913  * source code that is used as the source inside a &lt;script> tag.<br><br>
5914  * <p>
5915  * In order for the browser to process the returned data, the server must wrap the data object
5916  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5917  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5918  * depending on whether the callback name was passed:
5919  * <p>
5920  * <pre><code>
5921 boolean scriptTag = false;
5922 String cb = request.getParameter("callback");
5923 if (cb != null) {
5924     scriptTag = true;
5925     response.setContentType("text/javascript");
5926 } else {
5927     response.setContentType("application/x-json");
5928 }
5929 Writer out = response.getWriter();
5930 if (scriptTag) {
5931     out.write(cb + "(");
5932 }
5933 out.print(dataBlock.toJsonString());
5934 if (scriptTag) {
5935     out.write(");");
5936 }
5937 </pre></code>
5938  *
5939  * @constructor
5940  * @param {Object} config A configuration object.
5941  */
5942 Roo.data.ScriptTagProxy = function(config){
5943     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5944     Roo.apply(this, config);
5945     this.head = document.getElementsByTagName("head")[0];
5946 };
5947
5948 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5949
5950 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5951     /**
5952      * @cfg {String} url The URL from which to request the data object.
5953      */
5954     /**
5955      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5956      */
5957     timeout : 30000,
5958     /**
5959      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5960      * the server the name of the callback function set up by the load call to process the returned data object.
5961      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5962      * javascript output which calls this named function passing the data object as its only parameter.
5963      */
5964     callbackParam : "callback",
5965     /**
5966      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5967      * name to the request.
5968      */
5969     nocache : true,
5970
5971     /**
5972      * Load data from the configured URL, read the data object into
5973      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5974      * process that block using the passed callback.
5975      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5976      * for the request to the remote server.
5977      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5978      * object into a block of Roo.data.Records.
5979      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5980      * The function must be passed <ul>
5981      * <li>The Record block object</li>
5982      * <li>The "arg" argument from the load function</li>
5983      * <li>A boolean success indicator</li>
5984      * </ul>
5985      * @param {Object} scope The scope in which to call the callback
5986      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5987      */
5988     load : function(params, reader, callback, scope, arg){
5989         if(this.fireEvent("beforeload", this, params) !== false){
5990
5991             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5992
5993             var url = this.url;
5994             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5995             if(this.nocache){
5996                 url += "&_dc=" + (new Date().getTime());
5997             }
5998             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5999             var trans = {
6000                 id : transId,
6001                 cb : "stcCallback"+transId,
6002                 scriptId : "stcScript"+transId,
6003                 params : params,
6004                 arg : arg,
6005                 url : url,
6006                 callback : callback,
6007                 scope : scope,
6008                 reader : reader
6009             };
6010             var conn = this;
6011
6012             window[trans.cb] = function(o){
6013                 conn.handleResponse(o, trans);
6014             };
6015
6016             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6017
6018             if(this.autoAbort !== false){
6019                 this.abort();
6020             }
6021
6022             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6023
6024             var script = document.createElement("script");
6025             script.setAttribute("src", url);
6026             script.setAttribute("type", "text/javascript");
6027             script.setAttribute("id", trans.scriptId);
6028             this.head.appendChild(script);
6029
6030             this.trans = trans;
6031         }else{
6032             callback.call(scope||this, null, arg, false);
6033         }
6034     },
6035
6036     // private
6037     isLoading : function(){
6038         return this.trans ? true : false;
6039     },
6040
6041     /**
6042      * Abort the current server request.
6043      */
6044     abort : function(){
6045         if(this.isLoading()){
6046             this.destroyTrans(this.trans);
6047         }
6048     },
6049
6050     // private
6051     destroyTrans : function(trans, isLoaded){
6052         this.head.removeChild(document.getElementById(trans.scriptId));
6053         clearTimeout(trans.timeoutId);
6054         if(isLoaded){
6055             window[trans.cb] = undefined;
6056             try{
6057                 delete window[trans.cb];
6058             }catch(e){}
6059         }else{
6060             // if hasn't been loaded, wait for load to remove it to prevent script error
6061             window[trans.cb] = function(){
6062                 window[trans.cb] = undefined;
6063                 try{
6064                     delete window[trans.cb];
6065                 }catch(e){}
6066             };
6067         }
6068     },
6069
6070     // private
6071     handleResponse : function(o, trans){
6072         this.trans = false;
6073         this.destroyTrans(trans, true);
6074         var result;
6075         try {
6076             result = trans.reader.readRecords(o);
6077         }catch(e){
6078             this.fireEvent("loadexception", this, o, trans.arg, e);
6079             trans.callback.call(trans.scope||window, null, trans.arg, false);
6080             return;
6081         }
6082         this.fireEvent("load", this, o, trans.arg);
6083         trans.callback.call(trans.scope||window, result, trans.arg, true);
6084     },
6085
6086     // private
6087     handleFailure : function(trans){
6088         this.trans = false;
6089         this.destroyTrans(trans, false);
6090         this.fireEvent("loadexception", this, null, trans.arg);
6091         trans.callback.call(trans.scope||window, null, trans.arg, false);
6092     }
6093 });/*
6094  * Based on:
6095  * Ext JS Library 1.1.1
6096  * Copyright(c) 2006-2007, Ext JS, LLC.
6097  *
6098  * Originally Released Under LGPL - original licence link has changed is not relivant.
6099  *
6100  * Fork - LGPL
6101  * <script type="text/javascript">
6102  */
6103
6104 /**
6105  * @class Roo.data.JsonReader
6106  * @extends Roo.data.DataReader
6107  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6108  * based on mappings in a provided Roo.data.Record constructor.
6109  * 
6110  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6111  * in the reply previously. 
6112  * 
6113  * <p>
6114  * Example code:
6115  * <pre><code>
6116 var RecordDef = Roo.data.Record.create([
6117     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6118     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6119 ]);
6120 var myReader = new Roo.data.JsonReader({
6121     totalProperty: "results",    // The property which contains the total dataset size (optional)
6122     root: "rows",                // The property which contains an Array of row objects
6123     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6124 }, RecordDef);
6125 </code></pre>
6126  * <p>
6127  * This would consume a JSON file like this:
6128  * <pre><code>
6129 { 'results': 2, 'rows': [
6130     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6131     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6132 }
6133 </code></pre>
6134  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6135  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6136  * paged from the remote server.
6137  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6138  * @cfg {String} root name of the property which contains the Array of row objects.
6139  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6140  * @constructor
6141  * Create a new JsonReader
6142  * @param {Object} meta Metadata configuration options
6143  * @param {Object} recordType Either an Array of field definition objects,
6144  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6145  */
6146 Roo.data.JsonReader = function(meta, recordType){
6147     
6148     meta = meta || {};
6149     // set some defaults:
6150     Roo.applyIf(meta, {
6151         totalProperty: 'total',
6152         successProperty : 'success',
6153         root : 'data',
6154         id : 'id'
6155     });
6156     
6157     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6158 };
6159 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6160     
6161     /**
6162      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6163      * Used by Store query builder to append _requestMeta to params.
6164      * 
6165      */
6166     metaFromRemote : false,
6167     /**
6168      * This method is only used by a DataProxy which has retrieved data from a remote server.
6169      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6170      * @return {Object} data A data block which is used by an Roo.data.Store object as
6171      * a cache of Roo.data.Records.
6172      */
6173     read : function(response){
6174         var json = response.responseText;
6175        
6176         var o = /* eval:var:o */ eval("("+json+")");
6177         if(!o) {
6178             throw {message: "JsonReader.read: Json object not found"};
6179         }
6180         
6181         if(o.metaData){
6182             
6183             delete this.ef;
6184             this.metaFromRemote = true;
6185             this.meta = o.metaData;
6186             this.recordType = Roo.data.Record.create(o.metaData.fields);
6187             this.onMetaChange(this.meta, this.recordType, o);
6188         }
6189         return this.readRecords(o);
6190     },
6191
6192     // private function a store will implement
6193     onMetaChange : function(meta, recordType, o){
6194
6195     },
6196
6197     /**
6198          * @ignore
6199          */
6200     simpleAccess: function(obj, subsc) {
6201         return obj[subsc];
6202     },
6203
6204         /**
6205          * @ignore
6206          */
6207     getJsonAccessor: function(){
6208         var re = /[\[\.]/;
6209         return function(expr) {
6210             try {
6211                 return(re.test(expr))
6212                     ? new Function("obj", "return obj." + expr)
6213                     : function(obj){
6214                         return obj[expr];
6215                     };
6216             } catch(e){}
6217             return Roo.emptyFn;
6218         };
6219     }(),
6220
6221     /**
6222      * Create a data block containing Roo.data.Records from an XML document.
6223      * @param {Object} o An object which contains an Array of row objects in the property specified
6224      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6225      * which contains the total size of the dataset.
6226      * @return {Object} data A data block which is used by an Roo.data.Store object as
6227      * a cache of Roo.data.Records.
6228      */
6229     readRecords : function(o){
6230         /**
6231          * After any data loads, the raw JSON data is available for further custom processing.
6232          * @type Object
6233          */
6234         this.o = o;
6235         var s = this.meta, Record = this.recordType,
6236             f = Record.prototype.fields, fi = f.items, fl = f.length;
6237
6238 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6239         if (!this.ef) {
6240             if(s.totalProperty) {
6241                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6242                 }
6243                 if(s.successProperty) {
6244                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6245                 }
6246                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6247                 if (s.id) {
6248                         var g = this.getJsonAccessor(s.id);
6249                         this.getId = function(rec) {
6250                                 var r = g(rec);
6251                                 return (r === undefined || r === "") ? null : r;
6252                         };
6253                 } else {
6254                         this.getId = function(){return null;};
6255                 }
6256             this.ef = [];
6257             for(var jj = 0; jj < fl; jj++){
6258                 f = fi[jj];
6259                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6260                 this.ef[jj] = this.getJsonAccessor(map);
6261             }
6262         }
6263
6264         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6265         if(s.totalProperty){
6266             var vt = parseInt(this.getTotal(o), 10);
6267             if(!isNaN(vt)){
6268                 totalRecords = vt;
6269             }
6270         }
6271         if(s.successProperty){
6272             var vs = this.getSuccess(o);
6273             if(vs === false || vs === 'false'){
6274                 success = false;
6275             }
6276         }
6277         var records = [];
6278             for(var i = 0; i < c; i++){
6279                     var n = root[i];
6280                 var values = {};
6281                 var id = this.getId(n);
6282                 for(var j = 0; j < fl; j++){
6283                     f = fi[j];
6284                 var v = this.ef[j](n);
6285                 if (!f.convert) {
6286                     Roo.log('missing convert for ' + f.name);
6287                     Roo.log(f);
6288                     continue;
6289                 }
6290                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6291                 }
6292                 var record = new Record(values, id);
6293                 record.json = n;
6294                 records[i] = record;
6295             }
6296             return {
6297             raw : o,
6298                 success : success,
6299                 records : records,
6300                 totalRecords : totalRecords
6301             };
6302     }
6303 });/*
6304  * Based on:
6305  * Ext JS Library 1.1.1
6306  * Copyright(c) 2006-2007, Ext JS, LLC.
6307  *
6308  * Originally Released Under LGPL - original licence link has changed is not relivant.
6309  *
6310  * Fork - LGPL
6311  * <script type="text/javascript">
6312  */
6313
6314 /**
6315  * @class Roo.data.XmlReader
6316  * @extends Roo.data.DataReader
6317  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6318  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6319  * <p>
6320  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6321  * header in the HTTP response must be set to "text/xml".</em>
6322  * <p>
6323  * Example code:
6324  * <pre><code>
6325 var RecordDef = Roo.data.Record.create([
6326    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6327    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6328 ]);
6329 var myReader = new Roo.data.XmlReader({
6330    totalRecords: "results", // The element which contains the total dataset size (optional)
6331    record: "row",           // The repeated element which contains row information
6332    id: "id"                 // The element within the row that provides an ID for the record (optional)
6333 }, RecordDef);
6334 </code></pre>
6335  * <p>
6336  * This would consume an XML file like this:
6337  * <pre><code>
6338 &lt;?xml?>
6339 &lt;dataset>
6340  &lt;results>2&lt;/results>
6341  &lt;row>
6342    &lt;id>1&lt;/id>
6343    &lt;name>Bill&lt;/name>
6344    &lt;occupation>Gardener&lt;/occupation>
6345  &lt;/row>
6346  &lt;row>
6347    &lt;id>2&lt;/id>
6348    &lt;name>Ben&lt;/name>
6349    &lt;occupation>Horticulturalist&lt;/occupation>
6350  &lt;/row>
6351 &lt;/dataset>
6352 </code></pre>
6353  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6354  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6355  * paged from the remote server.
6356  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6357  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6358  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6359  * a record identifier value.
6360  * @constructor
6361  * Create a new XmlReader
6362  * @param {Object} meta Metadata configuration options
6363  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6364  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6365  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6366  */
6367 Roo.data.XmlReader = function(meta, recordType){
6368     meta = meta || {};
6369     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6370 };
6371 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6372     /**
6373      * This method is only used by a DataProxy which has retrieved data from a remote server.
6374          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6375          * to contain a method called 'responseXML' that returns an XML document object.
6376      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6377      * a cache of Roo.data.Records.
6378      */
6379     read : function(response){
6380         var doc = response.responseXML;
6381         if(!doc) {
6382             throw {message: "XmlReader.read: XML Document not available"};
6383         }
6384         return this.readRecords(doc);
6385     },
6386
6387     /**
6388      * Create a data block containing Roo.data.Records from an XML document.
6389          * @param {Object} doc A parsed XML document.
6390      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6391      * a cache of Roo.data.Records.
6392      */
6393     readRecords : function(doc){
6394         /**
6395          * After any data loads/reads, the raw XML Document is available for further custom processing.
6396          * @type XMLDocument
6397          */
6398         this.xmlData = doc;
6399         var root = doc.documentElement || doc;
6400         var q = Roo.DomQuery;
6401         var recordType = this.recordType, fields = recordType.prototype.fields;
6402         var sid = this.meta.id;
6403         var totalRecords = 0, success = true;
6404         if(this.meta.totalRecords){
6405             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6406         }
6407         
6408         if(this.meta.success){
6409             var sv = q.selectValue(this.meta.success, root, true);
6410             success = sv !== false && sv !== 'false';
6411         }
6412         var records = [];
6413         var ns = q.select(this.meta.record, root);
6414         for(var i = 0, len = ns.length; i < len; i++) {
6415                 var n = ns[i];
6416                 var values = {};
6417                 var id = sid ? q.selectValue(sid, n) : undefined;
6418                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6419                     var f = fields.items[j];
6420                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6421                     v = f.convert(v);
6422                     values[f.name] = v;
6423                 }
6424                 var record = new recordType(values, id);
6425                 record.node = n;
6426                 records[records.length] = record;
6427             }
6428
6429             return {
6430                 success : success,
6431                 records : records,
6432                 totalRecords : totalRecords || records.length
6433             };
6434     }
6435 });/*
6436  * Based on:
6437  * Ext JS Library 1.1.1
6438  * Copyright(c) 2006-2007, Ext JS, LLC.
6439  *
6440  * Originally Released Under LGPL - original licence link has changed is not relivant.
6441  *
6442  * Fork - LGPL
6443  * <script type="text/javascript">
6444  */
6445
6446 /**
6447  * @class Roo.data.ArrayReader
6448  * @extends Roo.data.DataReader
6449  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6450  * Each element of that Array represents a row of data fields. The
6451  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6452  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6453  * <p>
6454  * Example code:.
6455  * <pre><code>
6456 var RecordDef = Roo.data.Record.create([
6457     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6458     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6459 ]);
6460 var myReader = new Roo.data.ArrayReader({
6461     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6462 }, RecordDef);
6463 </code></pre>
6464  * <p>
6465  * This would consume an Array like this:
6466  * <pre><code>
6467 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6468   </code></pre>
6469  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6470  * @constructor
6471  * Create a new JsonReader
6472  * @param {Object} meta Metadata configuration options.
6473  * @param {Object} recordType Either an Array of field definition objects
6474  * as specified to {@link Roo.data.Record#create},
6475  * or an {@link Roo.data.Record} object
6476  * created using {@link Roo.data.Record#create}.
6477  */
6478 Roo.data.ArrayReader = function(meta, recordType){
6479     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6480 };
6481
6482 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6483     /**
6484      * Create a data block containing Roo.data.Records from an XML document.
6485      * @param {Object} o An Array of row objects which represents the dataset.
6486      * @return {Object} data A data block which is used by an Roo.data.Store object as
6487      * a cache of Roo.data.Records.
6488      */
6489     readRecords : function(o){
6490         var sid = this.meta ? this.meta.id : null;
6491         var recordType = this.recordType, fields = recordType.prototype.fields;
6492         var records = [];
6493         var root = o;
6494             for(var i = 0; i < root.length; i++){
6495                     var n = root[i];
6496                 var values = {};
6497                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6498                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6499                 var f = fields.items[j];
6500                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6501                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6502                 v = f.convert(v);
6503                 values[f.name] = v;
6504             }
6505                 var record = new recordType(values, id);
6506                 record.json = n;
6507                 records[records.length] = record;
6508             }
6509             return {
6510                 records : records,
6511                 totalRecords : records.length
6512             };
6513     }
6514 });/*
6515  * Based on:
6516  * Ext JS Library 1.1.1
6517  * Copyright(c) 2006-2007, Ext JS, LLC.
6518  *
6519  * Originally Released Under LGPL - original licence link has changed is not relivant.
6520  *
6521  * Fork - LGPL
6522  * <script type="text/javascript">
6523  */
6524
6525
6526 /**
6527  * @class Roo.data.Tree
6528  * @extends Roo.util.Observable
6529  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6530  * in the tree have most standard DOM functionality.
6531  * @constructor
6532  * @param {Node} root (optional) The root node
6533  */
6534 Roo.data.Tree = function(root){
6535    this.nodeHash = {};
6536    /**
6537     * The root node for this tree
6538     * @type Node
6539     */
6540    this.root = null;
6541    if(root){
6542        this.setRootNode(root);
6543    }
6544    this.addEvents({
6545        /**
6546         * @event append
6547         * Fires when a new child node is appended to a node in this tree.
6548         * @param {Tree} tree The owner tree
6549         * @param {Node} parent The parent node
6550         * @param {Node} node The newly appended node
6551         * @param {Number} index The index of the newly appended node
6552         */
6553        "append" : true,
6554        /**
6555         * @event remove
6556         * Fires when a child node is removed from a node in this tree.
6557         * @param {Tree} tree The owner tree
6558         * @param {Node} parent The parent node
6559         * @param {Node} node The child node removed
6560         */
6561        "remove" : true,
6562        /**
6563         * @event move
6564         * Fires when a node is moved to a new location in the tree
6565         * @param {Tree} tree The owner tree
6566         * @param {Node} node The node moved
6567         * @param {Node} oldParent The old parent of this node
6568         * @param {Node} newParent The new parent of this node
6569         * @param {Number} index The index it was moved to
6570         */
6571        "move" : true,
6572        /**
6573         * @event insert
6574         * Fires when a new child node is inserted in a node in this tree.
6575         * @param {Tree} tree The owner tree
6576         * @param {Node} parent The parent node
6577         * @param {Node} node The child node inserted
6578         * @param {Node} refNode The child node the node was inserted before
6579         */
6580        "insert" : true,
6581        /**
6582         * @event beforeappend
6583         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be appended
6587         */
6588        "beforeappend" : true,
6589        /**
6590         * @event beforeremove
6591         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6592         * @param {Tree} tree The owner tree
6593         * @param {Node} parent The parent node
6594         * @param {Node} node The child node to be removed
6595         */
6596        "beforeremove" : true,
6597        /**
6598         * @event beforemove
6599         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6600         * @param {Tree} tree The owner tree
6601         * @param {Node} node The node being moved
6602         * @param {Node} oldParent The parent of the node
6603         * @param {Node} newParent The new parent the node is moving to
6604         * @param {Number} index The index it is being moved to
6605         */
6606        "beforemove" : true,
6607        /**
6608         * @event beforeinsert
6609         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} parent The parent node
6612         * @param {Node} node The child node to be inserted
6613         * @param {Node} refNode The child node the node is being inserted before
6614         */
6615        "beforeinsert" : true
6616    });
6617
6618     Roo.data.Tree.superclass.constructor.call(this);
6619 };
6620
6621 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6622     pathSeparator: "/",
6623
6624     proxyNodeEvent : function(){
6625         return this.fireEvent.apply(this, arguments);
6626     },
6627
6628     /**
6629      * Returns the root node for this tree.
6630      * @return {Node}
6631      */
6632     getRootNode : function(){
6633         return this.root;
6634     },
6635
6636     /**
6637      * Sets the root node for this tree.
6638      * @param {Node} node
6639      * @return {Node}
6640      */
6641     setRootNode : function(node){
6642         this.root = node;
6643         node.ownerTree = this;
6644         node.isRoot = true;
6645         this.registerNode(node);
6646         return node;
6647     },
6648
6649     /**
6650      * Gets a node in this tree by its id.
6651      * @param {String} id
6652      * @return {Node}
6653      */
6654     getNodeById : function(id){
6655         return this.nodeHash[id];
6656     },
6657
6658     registerNode : function(node){
6659         this.nodeHash[node.id] = node;
6660     },
6661
6662     unregisterNode : function(node){
6663         delete this.nodeHash[node.id];
6664     },
6665
6666     toString : function(){
6667         return "[Tree"+(this.id?" "+this.id:"")+"]";
6668     }
6669 });
6670
6671 /**
6672  * @class Roo.data.Node
6673  * @extends Roo.util.Observable
6674  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6675  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6676  * @constructor
6677  * @param {Object} attributes The attributes/config for the node
6678  */
6679 Roo.data.Node = function(attributes){
6680     /**
6681      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6682      * @type {Object}
6683      */
6684     this.attributes = attributes || {};
6685     this.leaf = this.attributes.leaf;
6686     /**
6687      * The node id. @type String
6688      */
6689     this.id = this.attributes.id;
6690     if(!this.id){
6691         this.id = Roo.id(null, "ynode-");
6692         this.attributes.id = this.id;
6693     }
6694      
6695     
6696     /**
6697      * All child nodes of this node. @type Array
6698      */
6699     this.childNodes = [];
6700     if(!this.childNodes.indexOf){ // indexOf is a must
6701         this.childNodes.indexOf = function(o){
6702             for(var i = 0, len = this.length; i < len; i++){
6703                 if(this[i] == o) {
6704                     return i;
6705                 }
6706             }
6707             return -1;
6708         };
6709     }
6710     /**
6711      * The parent node for this node. @type Node
6712      */
6713     this.parentNode = null;
6714     /**
6715      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6716      */
6717     this.firstChild = null;
6718     /**
6719      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6720      */
6721     this.lastChild = null;
6722     /**
6723      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6724      */
6725     this.previousSibling = null;
6726     /**
6727      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6728      */
6729     this.nextSibling = null;
6730
6731     this.addEvents({
6732        /**
6733         * @event append
6734         * Fires when a new child node is appended
6735         * @param {Tree} tree The owner tree
6736         * @param {Node} this This node
6737         * @param {Node} node The newly appended node
6738         * @param {Number} index The index of the newly appended node
6739         */
6740        "append" : true,
6741        /**
6742         * @event remove
6743         * Fires when a child node is removed
6744         * @param {Tree} tree The owner tree
6745         * @param {Node} this This node
6746         * @param {Node} node The removed node
6747         */
6748        "remove" : true,
6749        /**
6750         * @event move
6751         * Fires when this node is moved to a new location in the tree
6752         * @param {Tree} tree The owner tree
6753         * @param {Node} this This node
6754         * @param {Node} oldParent The old parent of this node
6755         * @param {Node} newParent The new parent of this node
6756         * @param {Number} index The index it was moved to
6757         */
6758        "move" : true,
6759        /**
6760         * @event insert
6761         * Fires when a new child node is inserted.
6762         * @param {Tree} tree The owner tree
6763         * @param {Node} this This node
6764         * @param {Node} node The child node inserted
6765         * @param {Node} refNode The child node the node was inserted before
6766         */
6767        "insert" : true,
6768        /**
6769         * @event beforeappend
6770         * Fires before a new child is appended, return false to cancel the append.
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The child node to be appended
6774         */
6775        "beforeappend" : true,
6776        /**
6777         * @event beforeremove
6778         * Fires before a child is removed, return false to cancel the remove.
6779         * @param {Tree} tree The owner tree
6780         * @param {Node} this This node
6781         * @param {Node} node The child node to be removed
6782         */
6783        "beforeremove" : true,
6784        /**
6785         * @event beforemove
6786         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6787         * @param {Tree} tree The owner tree
6788         * @param {Node} this This node
6789         * @param {Node} oldParent The parent of this node
6790         * @param {Node} newParent The new parent this node is moving to
6791         * @param {Number} index The index it is being moved to
6792         */
6793        "beforemove" : true,
6794        /**
6795         * @event beforeinsert
6796         * Fires before a new child is inserted, return false to cancel the insert.
6797         * @param {Tree} tree The owner tree
6798         * @param {Node} this This node
6799         * @param {Node} node The child node to be inserted
6800         * @param {Node} refNode The child node the node is being inserted before
6801         */
6802        "beforeinsert" : true
6803    });
6804     this.listeners = this.attributes.listeners;
6805     Roo.data.Node.superclass.constructor.call(this);
6806 };
6807
6808 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6809     fireEvent : function(evtName){
6810         // first do standard event for this node
6811         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6812             return false;
6813         }
6814         // then bubble it up to the tree if the event wasn't cancelled
6815         var ot = this.getOwnerTree();
6816         if(ot){
6817             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6818                 return false;
6819             }
6820         }
6821         return true;
6822     },
6823
6824     /**
6825      * Returns true if this node is a leaf
6826      * @return {Boolean}
6827      */
6828     isLeaf : function(){
6829         return this.leaf === true;
6830     },
6831
6832     // private
6833     setFirstChild : function(node){
6834         this.firstChild = node;
6835     },
6836
6837     //private
6838     setLastChild : function(node){
6839         this.lastChild = node;
6840     },
6841
6842
6843     /**
6844      * Returns true if this node is the last child of its parent
6845      * @return {Boolean}
6846      */
6847     isLast : function(){
6848        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6849     },
6850
6851     /**
6852      * Returns true if this node is the first child of its parent
6853      * @return {Boolean}
6854      */
6855     isFirst : function(){
6856        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6857     },
6858
6859     hasChildNodes : function(){
6860         return !this.isLeaf() && this.childNodes.length > 0;
6861     },
6862
6863     /**
6864      * Insert node(s) as the last child node of this node.
6865      * @param {Node/Array} node The node or Array of nodes to append
6866      * @return {Node} The appended node if single append, or null if an array was passed
6867      */
6868     appendChild : function(node){
6869         var multi = false;
6870         if(node instanceof Array){
6871             multi = node;
6872         }else if(arguments.length > 1){
6873             multi = arguments;
6874         }
6875         // if passed an array or multiple args do them one by one
6876         if(multi){
6877             for(var i = 0, len = multi.length; i < len; i++) {
6878                 this.appendChild(multi[i]);
6879             }
6880         }else{
6881             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6882                 return false;
6883             }
6884             var index = this.childNodes.length;
6885             var oldParent = node.parentNode;
6886             // it's a move, make sure we move it cleanly
6887             if(oldParent){
6888                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6889                     return false;
6890                 }
6891                 oldParent.removeChild(node);
6892             }
6893             index = this.childNodes.length;
6894             if(index == 0){
6895                 this.setFirstChild(node);
6896             }
6897             this.childNodes.push(node);
6898             node.parentNode = this;
6899             var ps = this.childNodes[index-1];
6900             if(ps){
6901                 node.previousSibling = ps;
6902                 ps.nextSibling = node;
6903             }else{
6904                 node.previousSibling = null;
6905             }
6906             node.nextSibling = null;
6907             this.setLastChild(node);
6908             node.setOwnerTree(this.getOwnerTree());
6909             this.fireEvent("append", this.ownerTree, this, node, index);
6910             if(oldParent){
6911                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6912             }
6913             return node;
6914         }
6915     },
6916
6917     /**
6918      * Removes a child node from this node.
6919      * @param {Node} node The node to remove
6920      * @return {Node} The removed node
6921      */
6922     removeChild : function(node){
6923         var index = this.childNodes.indexOf(node);
6924         if(index == -1){
6925             return false;
6926         }
6927         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6928             return false;
6929         }
6930
6931         // remove it from childNodes collection
6932         this.childNodes.splice(index, 1);
6933
6934         // update siblings
6935         if(node.previousSibling){
6936             node.previousSibling.nextSibling = node.nextSibling;
6937         }
6938         if(node.nextSibling){
6939             node.nextSibling.previousSibling = node.previousSibling;
6940         }
6941
6942         // update child refs
6943         if(this.firstChild == node){
6944             this.setFirstChild(node.nextSibling);
6945         }
6946         if(this.lastChild == node){
6947             this.setLastChild(node.previousSibling);
6948         }
6949
6950         node.setOwnerTree(null);
6951         // clear any references from the node
6952         node.parentNode = null;
6953         node.previousSibling = null;
6954         node.nextSibling = null;
6955         this.fireEvent("remove", this.ownerTree, this, node);
6956         return node;
6957     },
6958
6959     /**
6960      * Inserts the first node before the second node in this nodes childNodes collection.
6961      * @param {Node} node The node to insert
6962      * @param {Node} refNode The node to insert before (if null the node is appended)
6963      * @return {Node} The inserted node
6964      */
6965     insertBefore : function(node, refNode){
6966         if(!refNode){ // like standard Dom, refNode can be null for append
6967             return this.appendChild(node);
6968         }
6969         // nothing to do
6970         if(node == refNode){
6971             return false;
6972         }
6973
6974         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6975             return false;
6976         }
6977         var index = this.childNodes.indexOf(refNode);
6978         var oldParent = node.parentNode;
6979         var refIndex = index;
6980
6981         // when moving internally, indexes will change after remove
6982         if(oldParent == this && this.childNodes.indexOf(node) < index){
6983             refIndex--;
6984         }
6985
6986         // it's a move, make sure we move it cleanly
6987         if(oldParent){
6988             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6989                 return false;
6990             }
6991             oldParent.removeChild(node);
6992         }
6993         if(refIndex == 0){
6994             this.setFirstChild(node);
6995         }
6996         this.childNodes.splice(refIndex, 0, node);
6997         node.parentNode = this;
6998         var ps = this.childNodes[refIndex-1];
6999         if(ps){
7000             node.previousSibling = ps;
7001             ps.nextSibling = node;
7002         }else{
7003             node.previousSibling = null;
7004         }
7005         node.nextSibling = refNode;
7006         refNode.previousSibling = node;
7007         node.setOwnerTree(this.getOwnerTree());
7008         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7009         if(oldParent){
7010             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7011         }
7012         return node;
7013     },
7014
7015     /**
7016      * Returns the child node at the specified index.
7017      * @param {Number} index
7018      * @return {Node}
7019      */
7020     item : function(index){
7021         return this.childNodes[index];
7022     },
7023
7024     /**
7025      * Replaces one child node in this node with another.
7026      * @param {Node} newChild The replacement node
7027      * @param {Node} oldChild The node to replace
7028      * @return {Node} The replaced node
7029      */
7030     replaceChild : function(newChild, oldChild){
7031         this.insertBefore(newChild, oldChild);
7032         this.removeChild(oldChild);
7033         return oldChild;
7034     },
7035
7036     /**
7037      * Returns the index of a child node
7038      * @param {Node} node
7039      * @return {Number} The index of the node or -1 if it was not found
7040      */
7041     indexOf : function(child){
7042         return this.childNodes.indexOf(child);
7043     },
7044
7045     /**
7046      * Returns the tree this node is in.
7047      * @return {Tree}
7048      */
7049     getOwnerTree : function(){
7050         // if it doesn't have one, look for one
7051         if(!this.ownerTree){
7052             var p = this;
7053             while(p){
7054                 if(p.ownerTree){
7055                     this.ownerTree = p.ownerTree;
7056                     break;
7057                 }
7058                 p = p.parentNode;
7059             }
7060         }
7061         return this.ownerTree;
7062     },
7063
7064     /**
7065      * Returns depth of this node (the root node has a depth of 0)
7066      * @return {Number}
7067      */
7068     getDepth : function(){
7069         var depth = 0;
7070         var p = this;
7071         while(p.parentNode){
7072             ++depth;
7073             p = p.parentNode;
7074         }
7075         return depth;
7076     },
7077
7078     // private
7079     setOwnerTree : function(tree){
7080         // if it's move, we need to update everyone
7081         if(tree != this.ownerTree){
7082             if(this.ownerTree){
7083                 this.ownerTree.unregisterNode(this);
7084             }
7085             this.ownerTree = tree;
7086             var cs = this.childNodes;
7087             for(var i = 0, len = cs.length; i < len; i++) {
7088                 cs[i].setOwnerTree(tree);
7089             }
7090             if(tree){
7091                 tree.registerNode(this);
7092             }
7093         }
7094     },
7095
7096     /**
7097      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7098      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7099      * @return {String} The path
7100      */
7101     getPath : function(attr){
7102         attr = attr || "id";
7103         var p = this.parentNode;
7104         var b = [this.attributes[attr]];
7105         while(p){
7106             b.unshift(p.attributes[attr]);
7107             p = p.parentNode;
7108         }
7109         var sep = this.getOwnerTree().pathSeparator;
7110         return sep + b.join(sep);
7111     },
7112
7113     /**
7114      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7115      * function call will be the scope provided or the current node. The arguments to the function
7116      * will be the args provided or the current node. If the function returns false at any point,
7117      * the bubble is stopped.
7118      * @param {Function} fn The function to call
7119      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7120      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7121      */
7122     bubble : function(fn, scope, args){
7123         var p = this;
7124         while(p){
7125             if(fn.call(scope || p, args || p) === false){
7126                 break;
7127             }
7128             p = p.parentNode;
7129         }
7130     },
7131
7132     /**
7133      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7134      * function call will be the scope provided or the current node. The arguments to the function
7135      * will be the args provided or the current node. If the function returns false at any point,
7136      * the cascade is stopped on that branch.
7137      * @param {Function} fn The function to call
7138      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7139      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7140      */
7141     cascade : function(fn, scope, args){
7142         if(fn.call(scope || this, args || this) !== false){
7143             var cs = this.childNodes;
7144             for(var i = 0, len = cs.length; i < len; i++) {
7145                 cs[i].cascade(fn, scope, args);
7146             }
7147         }
7148     },
7149
7150     /**
7151      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7152      * function call will be the scope provided or the current node. The arguments to the function
7153      * will be the args provided or the current node. If the function returns false at any point,
7154      * the iteration stops.
7155      * @param {Function} fn The function to call
7156      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7157      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7158      */
7159     eachChild : function(fn, scope, args){
7160         var cs = this.childNodes;
7161         for(var i = 0, len = cs.length; i < len; i++) {
7162                 if(fn.call(scope || this, args || cs[i]) === false){
7163                     break;
7164                 }
7165         }
7166     },
7167
7168     /**
7169      * Finds the first child that has the attribute with the specified value.
7170      * @param {String} attribute The attribute name
7171      * @param {Mixed} value The value to search for
7172      * @return {Node} The found child or null if none was found
7173      */
7174     findChild : function(attribute, value){
7175         var cs = this.childNodes;
7176         for(var i = 0, len = cs.length; i < len; i++) {
7177                 if(cs[i].attributes[attribute] == value){
7178                     return cs[i];
7179                 }
7180         }
7181         return null;
7182     },
7183
7184     /**
7185      * Finds the first child by a custom function. The child matches if the function passed
7186      * returns true.
7187      * @param {Function} fn
7188      * @param {Object} scope (optional)
7189      * @return {Node} The found child or null if none was found
7190      */
7191     findChildBy : function(fn, scope){
7192         var cs = this.childNodes;
7193         for(var i = 0, len = cs.length; i < len; i++) {
7194                 if(fn.call(scope||cs[i], cs[i]) === true){
7195                     return cs[i];
7196                 }
7197         }
7198         return null;
7199     },
7200
7201     /**
7202      * Sorts this nodes children using the supplied sort function
7203      * @param {Function} fn
7204      * @param {Object} scope (optional)
7205      */
7206     sort : function(fn, scope){
7207         var cs = this.childNodes;
7208         var len = cs.length;
7209         if(len > 0){
7210             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7211             cs.sort(sortFn);
7212             for(var i = 0; i < len; i++){
7213                 var n = cs[i];
7214                 n.previousSibling = cs[i-1];
7215                 n.nextSibling = cs[i+1];
7216                 if(i == 0){
7217                     this.setFirstChild(n);
7218                 }
7219                 if(i == len-1){
7220                     this.setLastChild(n);
7221                 }
7222             }
7223         }
7224     },
7225
7226     /**
7227      * Returns true if this node is an ancestor (at any point) of the passed node.
7228      * @param {Node} node
7229      * @return {Boolean}
7230      */
7231     contains : function(node){
7232         return node.isAncestor(this);
7233     },
7234
7235     /**
7236      * Returns true if the passed node is an ancestor (at any point) of this node.
7237      * @param {Node} node
7238      * @return {Boolean}
7239      */
7240     isAncestor : function(node){
7241         var p = this.parentNode;
7242         while(p){
7243             if(p == node){
7244                 return true;
7245             }
7246             p = p.parentNode;
7247         }
7248         return false;
7249     },
7250
7251     toString : function(){
7252         return "[Node"+(this.id?" "+this.id:"")+"]";
7253     }
7254 });/*
7255  * Based on:
7256  * Ext JS Library 1.1.1
7257  * Copyright(c) 2006-2007, Ext JS, LLC.
7258  *
7259  * Originally Released Under LGPL - original licence link has changed is not relivant.
7260  *
7261  * Fork - LGPL
7262  * <script type="text/javascript">
7263  */
7264  
7265
7266 /**
7267  * @class Roo.ComponentMgr
7268  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7269  * @singleton
7270  */
7271 Roo.ComponentMgr = function(){
7272     var all = new Roo.util.MixedCollection();
7273
7274     return {
7275         /**
7276          * Registers a component.
7277          * @param {Roo.Component} c The component
7278          */
7279         register : function(c){
7280             all.add(c);
7281         },
7282
7283         /**
7284          * Unregisters a component.
7285          * @param {Roo.Component} c The component
7286          */
7287         unregister : function(c){
7288             all.remove(c);
7289         },
7290
7291         /**
7292          * Returns a component by id
7293          * @param {String} id The component id
7294          */
7295         get : function(id){
7296             return all.get(id);
7297         },
7298
7299         /**
7300          * Registers a function that will be called when a specified component is added to ComponentMgr
7301          * @param {String} id The component id
7302          * @param {Funtction} fn The callback function
7303          * @param {Object} scope The scope of the callback
7304          */
7305         onAvailable : function(id, fn, scope){
7306             all.on("add", function(index, o){
7307                 if(o.id == id){
7308                     fn.call(scope || o, o);
7309                     all.un("add", fn, scope);
7310                 }
7311             });
7312         }
7313     };
7314 }();/*
7315  * Based on:
7316  * Ext JS Library 1.1.1
7317  * Copyright(c) 2006-2007, Ext JS, LLC.
7318  *
7319  * Originally Released Under LGPL - original licence link has changed is not relivant.
7320  *
7321  * Fork - LGPL
7322  * <script type="text/javascript">
7323  */
7324  
7325 /**
7326  * @class Roo.Component
7327  * @extends Roo.util.Observable
7328  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7329  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7330  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7331  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7332  * All visual components (widgets) that require rendering into a layout should subclass Component.
7333  * @constructor
7334  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7335  * 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
7336  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7337  */
7338 Roo.Component = function(config){
7339     config = config || {};
7340     if(config.tagName || config.dom || typeof config == "string"){ // element object
7341         config = {el: config, id: config.id || config};
7342     }
7343     this.initialConfig = config;
7344
7345     Roo.apply(this, config);
7346     this.addEvents({
7347         /**
7348          * @event disable
7349          * Fires after the component is disabled.
7350              * @param {Roo.Component} this
7351              */
7352         disable : true,
7353         /**
7354          * @event enable
7355          * Fires after the component is enabled.
7356              * @param {Roo.Component} this
7357              */
7358         enable : true,
7359         /**
7360          * @event beforeshow
7361          * Fires before the component is shown.  Return false to stop the show.
7362              * @param {Roo.Component} this
7363              */
7364         beforeshow : true,
7365         /**
7366          * @event show
7367          * Fires after the component is shown.
7368              * @param {Roo.Component} this
7369              */
7370         show : true,
7371         /**
7372          * @event beforehide
7373          * Fires before the component is hidden. Return false to stop the hide.
7374              * @param {Roo.Component} this
7375              */
7376         beforehide : true,
7377         /**
7378          * @event hide
7379          * Fires after the component is hidden.
7380              * @param {Roo.Component} this
7381              */
7382         hide : true,
7383         /**
7384          * @event beforerender
7385          * Fires before the component is rendered. Return false to stop the render.
7386              * @param {Roo.Component} this
7387              */
7388         beforerender : true,
7389         /**
7390          * @event render
7391          * Fires after the component is rendered.
7392              * @param {Roo.Component} this
7393              */
7394         render : true,
7395         /**
7396          * @event beforedestroy
7397          * Fires before the component is destroyed. Return false to stop the destroy.
7398              * @param {Roo.Component} this
7399              */
7400         beforedestroy : true,
7401         /**
7402          * @event destroy
7403          * Fires after the component is destroyed.
7404              * @param {Roo.Component} this
7405              */
7406         destroy : true
7407     });
7408     if(!this.id){
7409         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7410     }
7411     Roo.ComponentMgr.register(this);
7412     Roo.Component.superclass.constructor.call(this);
7413     this.initComponent();
7414     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7415         this.render(this.renderTo);
7416         delete this.renderTo;
7417     }
7418 };
7419
7420 /** @private */
7421 Roo.Component.AUTO_ID = 1000;
7422
7423 Roo.extend(Roo.Component, Roo.util.Observable, {
7424     /**
7425      * @scope Roo.Component.prototype
7426      * @type {Boolean}
7427      * true if this component is hidden. Read-only.
7428      */
7429     hidden : false,
7430     /**
7431      * @type {Boolean}
7432      * true if this component is disabled. Read-only.
7433      */
7434     disabled : false,
7435     /**
7436      * @type {Boolean}
7437      * true if this component has been rendered. Read-only.
7438      */
7439     rendered : false,
7440     
7441     /** @cfg {String} disableClass
7442      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7443      */
7444     disabledClass : "x-item-disabled",
7445         /** @cfg {Boolean} allowDomMove
7446          * Whether the component can move the Dom node when rendering (defaults to true).
7447          */
7448     allowDomMove : true,
7449     /** @cfg {String} hideMode
7450      * How this component should hidden. Supported values are
7451      * "visibility" (css visibility), "offsets" (negative offset position) and
7452      * "display" (css display) - defaults to "display".
7453      */
7454     hideMode: 'display',
7455
7456     /** @private */
7457     ctype : "Roo.Component",
7458
7459     /**
7460      * @cfg {String} actionMode 
7461      * which property holds the element that used for  hide() / show() / disable() / enable()
7462      * default is 'el' 
7463      */
7464     actionMode : "el",
7465
7466     /** @private */
7467     getActionEl : function(){
7468         return this[this.actionMode];
7469     },
7470
7471     initComponent : Roo.emptyFn,
7472     /**
7473      * If this is a lazy rendering component, render it to its container element.
7474      * @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.
7475      */
7476     render : function(container, position){
7477         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7478             if(!container && this.el){
7479                 this.el = Roo.get(this.el);
7480                 container = this.el.dom.parentNode;
7481                 this.allowDomMove = false;
7482             }
7483             this.container = Roo.get(container);
7484             this.rendered = true;
7485             if(position !== undefined){
7486                 if(typeof position == 'number'){
7487                     position = this.container.dom.childNodes[position];
7488                 }else{
7489                     position = Roo.getDom(position);
7490                 }
7491             }
7492             this.onRender(this.container, position || null);
7493             if(this.cls){
7494                 this.el.addClass(this.cls);
7495                 delete this.cls;
7496             }
7497             if(this.style){
7498                 this.el.applyStyles(this.style);
7499                 delete this.style;
7500             }
7501             this.fireEvent("render", this);
7502             this.afterRender(this.container);
7503             if(this.hidden){
7504                 this.hide();
7505             }
7506             if(this.disabled){
7507                 this.disable();
7508             }
7509         }
7510         return this;
7511     },
7512
7513     /** @private */
7514     // default function is not really useful
7515     onRender : function(ct, position){
7516         if(this.el){
7517             this.el = Roo.get(this.el);
7518             if(this.allowDomMove !== false){
7519                 ct.dom.insertBefore(this.el.dom, position);
7520             }
7521         }
7522     },
7523
7524     /** @private */
7525     getAutoCreate : function(){
7526         var cfg = typeof this.autoCreate == "object" ?
7527                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7528         if(this.id && !cfg.id){
7529             cfg.id = this.id;
7530         }
7531         return cfg;
7532     },
7533
7534     /** @private */
7535     afterRender : Roo.emptyFn,
7536
7537     /**
7538      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7539      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7540      */
7541     destroy : function(){
7542         if(this.fireEvent("beforedestroy", this) !== false){
7543             this.purgeListeners();
7544             this.beforeDestroy();
7545             if(this.rendered){
7546                 this.el.removeAllListeners();
7547                 this.el.remove();
7548                 if(this.actionMode == "container"){
7549                     this.container.remove();
7550                 }
7551             }
7552             this.onDestroy();
7553             Roo.ComponentMgr.unregister(this);
7554             this.fireEvent("destroy", this);
7555         }
7556     },
7557
7558         /** @private */
7559     beforeDestroy : function(){
7560
7561     },
7562
7563         /** @private */
7564         onDestroy : function(){
7565
7566     },
7567
7568     /**
7569      * Returns the underlying {@link Roo.Element}.
7570      * @return {Roo.Element} The element
7571      */
7572     getEl : function(){
7573         return this.el;
7574     },
7575
7576     /**
7577      * Returns the id of this component.
7578      * @return {String}
7579      */
7580     getId : function(){
7581         return this.id;
7582     },
7583
7584     /**
7585      * Try to focus this component.
7586      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7587      * @return {Roo.Component} this
7588      */
7589     focus : function(selectText){
7590         if(this.rendered){
7591             this.el.focus();
7592             if(selectText === true){
7593                 this.el.dom.select();
7594             }
7595         }
7596         return this;
7597     },
7598
7599     /** @private */
7600     blur : function(){
7601         if(this.rendered){
7602             this.el.blur();
7603         }
7604         return this;
7605     },
7606
7607     /**
7608      * Disable this component.
7609      * @return {Roo.Component} this
7610      */
7611     disable : function(){
7612         if(this.rendered){
7613             this.onDisable();
7614         }
7615         this.disabled = true;
7616         this.fireEvent("disable", this);
7617         return this;
7618     },
7619
7620         // private
7621     onDisable : function(){
7622         this.getActionEl().addClass(this.disabledClass);
7623         this.el.dom.disabled = true;
7624     },
7625
7626     /**
7627      * Enable this component.
7628      * @return {Roo.Component} this
7629      */
7630     enable : function(){
7631         if(this.rendered){
7632             this.onEnable();
7633         }
7634         this.disabled = false;
7635         this.fireEvent("enable", this);
7636         return this;
7637     },
7638
7639         // private
7640     onEnable : function(){
7641         this.getActionEl().removeClass(this.disabledClass);
7642         this.el.dom.disabled = false;
7643     },
7644
7645     /**
7646      * Convenience function for setting disabled/enabled by boolean.
7647      * @param {Boolean} disabled
7648      */
7649     setDisabled : function(disabled){
7650         this[disabled ? "disable" : "enable"]();
7651     },
7652
7653     /**
7654      * Show this component.
7655      * @return {Roo.Component} this
7656      */
7657     show: function(){
7658         if(this.fireEvent("beforeshow", this) !== false){
7659             this.hidden = false;
7660             if(this.rendered){
7661                 this.onShow();
7662             }
7663             this.fireEvent("show", this);
7664         }
7665         return this;
7666     },
7667
7668     // private
7669     onShow : function(){
7670         var ae = this.getActionEl();
7671         if(this.hideMode == 'visibility'){
7672             ae.dom.style.visibility = "visible";
7673         }else if(this.hideMode == 'offsets'){
7674             ae.removeClass('x-hidden');
7675         }else{
7676             ae.dom.style.display = "";
7677         }
7678     },
7679
7680     /**
7681      * Hide this component.
7682      * @return {Roo.Component} this
7683      */
7684     hide: function(){
7685         if(this.fireEvent("beforehide", this) !== false){
7686             this.hidden = true;
7687             if(this.rendered){
7688                 this.onHide();
7689             }
7690             this.fireEvent("hide", this);
7691         }
7692         return this;
7693     },
7694
7695     // private
7696     onHide : function(){
7697         var ae = this.getActionEl();
7698         if(this.hideMode == 'visibility'){
7699             ae.dom.style.visibility = "hidden";
7700         }else if(this.hideMode == 'offsets'){
7701             ae.addClass('x-hidden');
7702         }else{
7703             ae.dom.style.display = "none";
7704         }
7705     },
7706
7707     /**
7708      * Convenience function to hide or show this component by boolean.
7709      * @param {Boolean} visible True to show, false to hide
7710      * @return {Roo.Component} this
7711      */
7712     setVisible: function(visible){
7713         if(visible) {
7714             this.show();
7715         }else{
7716             this.hide();
7717         }
7718         return this;
7719     },
7720
7721     /**
7722      * Returns true if this component is visible.
7723      */
7724     isVisible : function(){
7725         return this.getActionEl().isVisible();
7726     },
7727
7728     cloneConfig : function(overrides){
7729         overrides = overrides || {};
7730         var id = overrides.id || Roo.id();
7731         var cfg = Roo.applyIf(overrides, this.initialConfig);
7732         cfg.id = id; // prevent dup id
7733         return new this.constructor(cfg);
7734     }
7735 });/*
7736  * Based on:
7737  * Ext JS Library 1.1.1
7738  * Copyright(c) 2006-2007, Ext JS, LLC.
7739  *
7740  * Originally Released Under LGPL - original licence link has changed is not relivant.
7741  *
7742  * Fork - LGPL
7743  * <script type="text/javascript">
7744  */
7745  (function(){ 
7746 /**
7747  * @class Roo.Layer
7748  * @extends Roo.Element
7749  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7750  * automatic maintaining of shadow/shim positions.
7751  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7752  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7753  * you can pass a string with a CSS class name. False turns off the shadow.
7754  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7755  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7756  * @cfg {String} cls CSS class to add to the element
7757  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7758  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7759  * @constructor
7760  * @param {Object} config An object with config options.
7761  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7762  */
7763
7764 Roo.Layer = function(config, existingEl){
7765     config = config || {};
7766     var dh = Roo.DomHelper;
7767     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7768     if(existingEl){
7769         this.dom = Roo.getDom(existingEl);
7770     }
7771     if(!this.dom){
7772         var o = config.dh || {tag: "div", cls: "x-layer"};
7773         this.dom = dh.append(pel, o);
7774     }
7775     if(config.cls){
7776         this.addClass(config.cls);
7777     }
7778     this.constrain = config.constrain !== false;
7779     this.visibilityMode = Roo.Element.VISIBILITY;
7780     if(config.id){
7781         this.id = this.dom.id = config.id;
7782     }else{
7783         this.id = Roo.id(this.dom);
7784     }
7785     this.zindex = config.zindex || this.getZIndex();
7786     this.position("absolute", this.zindex);
7787     if(config.shadow){
7788         this.shadowOffset = config.shadowOffset || 4;
7789         this.shadow = new Roo.Shadow({
7790             offset : this.shadowOffset,
7791             mode : config.shadow
7792         });
7793     }else{
7794         this.shadowOffset = 0;
7795     }
7796     this.useShim = config.shim !== false && Roo.useShims;
7797     this.useDisplay = config.useDisplay;
7798     this.hide();
7799 };
7800
7801 var supr = Roo.Element.prototype;
7802
7803 // shims are shared among layer to keep from having 100 iframes
7804 var shims = [];
7805
7806 Roo.extend(Roo.Layer, Roo.Element, {
7807
7808     getZIndex : function(){
7809         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7810     },
7811
7812     getShim : function(){
7813         if(!this.useShim){
7814             return null;
7815         }
7816         if(this.shim){
7817             return this.shim;
7818         }
7819         var shim = shims.shift();
7820         if(!shim){
7821             shim = this.createShim();
7822             shim.enableDisplayMode('block');
7823             shim.dom.style.display = 'none';
7824             shim.dom.style.visibility = 'visible';
7825         }
7826         var pn = this.dom.parentNode;
7827         if(shim.dom.parentNode != pn){
7828             pn.insertBefore(shim.dom, this.dom);
7829         }
7830         shim.setStyle('z-index', this.getZIndex()-2);
7831         this.shim = shim;
7832         return shim;
7833     },
7834
7835     hideShim : function(){
7836         if(this.shim){
7837             this.shim.setDisplayed(false);
7838             shims.push(this.shim);
7839             delete this.shim;
7840         }
7841     },
7842
7843     disableShadow : function(){
7844         if(this.shadow){
7845             this.shadowDisabled = true;
7846             this.shadow.hide();
7847             this.lastShadowOffset = this.shadowOffset;
7848             this.shadowOffset = 0;
7849         }
7850     },
7851
7852     enableShadow : function(show){
7853         if(this.shadow){
7854             this.shadowDisabled = false;
7855             this.shadowOffset = this.lastShadowOffset;
7856             delete this.lastShadowOffset;
7857             if(show){
7858                 this.sync(true);
7859             }
7860         }
7861     },
7862
7863     // private
7864     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7865     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7866     sync : function(doShow){
7867         var sw = this.shadow;
7868         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7869             var sh = this.getShim();
7870
7871             var w = this.getWidth(),
7872                 h = this.getHeight();
7873
7874             var l = this.getLeft(true),
7875                 t = this.getTop(true);
7876
7877             if(sw && !this.shadowDisabled){
7878                 if(doShow && !sw.isVisible()){
7879                     sw.show(this);
7880                 }else{
7881                     sw.realign(l, t, w, h);
7882                 }
7883                 if(sh){
7884                     if(doShow){
7885                        sh.show();
7886                     }
7887                     // fit the shim behind the shadow, so it is shimmed too
7888                     var a = sw.adjusts, s = sh.dom.style;
7889                     s.left = (Math.min(l, l+a.l))+"px";
7890                     s.top = (Math.min(t, t+a.t))+"px";
7891                     s.width = (w+a.w)+"px";
7892                     s.height = (h+a.h)+"px";
7893                 }
7894             }else if(sh){
7895                 if(doShow){
7896                    sh.show();
7897                 }
7898                 sh.setSize(w, h);
7899                 sh.setLeftTop(l, t);
7900             }
7901             
7902         }
7903     },
7904
7905     // private
7906     destroy : function(){
7907         this.hideShim();
7908         if(this.shadow){
7909             this.shadow.hide();
7910         }
7911         this.removeAllListeners();
7912         var pn = this.dom.parentNode;
7913         if(pn){
7914             pn.removeChild(this.dom);
7915         }
7916         Roo.Element.uncache(this.id);
7917     },
7918
7919     remove : function(){
7920         this.destroy();
7921     },
7922
7923     // private
7924     beginUpdate : function(){
7925         this.updating = true;
7926     },
7927
7928     // private
7929     endUpdate : function(){
7930         this.updating = false;
7931         this.sync(true);
7932     },
7933
7934     // private
7935     hideUnders : function(negOffset){
7936         if(this.shadow){
7937             this.shadow.hide();
7938         }
7939         this.hideShim();
7940     },
7941
7942     // private
7943     constrainXY : function(){
7944         if(this.constrain){
7945             var vw = Roo.lib.Dom.getViewWidth(),
7946                 vh = Roo.lib.Dom.getViewHeight();
7947             var s = Roo.get(document).getScroll();
7948
7949             var xy = this.getXY();
7950             var x = xy[0], y = xy[1];   
7951             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7952             // only move it if it needs it
7953             var moved = false;
7954             // first validate right/bottom
7955             if((x + w) > vw+s.left){
7956                 x = vw - w - this.shadowOffset;
7957                 moved = true;
7958             }
7959             if((y + h) > vh+s.top){
7960                 y = vh - h - this.shadowOffset;
7961                 moved = true;
7962             }
7963             // then make sure top/left isn't negative
7964             if(x < s.left){
7965                 x = s.left;
7966                 moved = true;
7967             }
7968             if(y < s.top){
7969                 y = s.top;
7970                 moved = true;
7971             }
7972             if(moved){
7973                 if(this.avoidY){
7974                     var ay = this.avoidY;
7975                     if(y <= ay && (y+h) >= ay){
7976                         y = ay-h-5;   
7977                     }
7978                 }
7979                 xy = [x, y];
7980                 this.storeXY(xy);
7981                 supr.setXY.call(this, xy);
7982                 this.sync();
7983             }
7984         }
7985     },
7986
7987     isVisible : function(){
7988         return this.visible;    
7989     },
7990
7991     // private
7992     showAction : function(){
7993         this.visible = true; // track visibility to prevent getStyle calls
7994         if(this.useDisplay === true){
7995             this.setDisplayed("");
7996         }else if(this.lastXY){
7997             supr.setXY.call(this, this.lastXY);
7998         }else if(this.lastLT){
7999             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8000         }
8001     },
8002
8003     // private
8004     hideAction : function(){
8005         this.visible = false;
8006         if(this.useDisplay === true){
8007             this.setDisplayed(false);
8008         }else{
8009             this.setLeftTop(-10000,-10000);
8010         }
8011     },
8012
8013     // overridden Element method
8014     setVisible : function(v, a, d, c, e){
8015         if(v){
8016             this.showAction();
8017         }
8018         if(a && v){
8019             var cb = function(){
8020                 this.sync(true);
8021                 if(c){
8022                     c();
8023                 }
8024             }.createDelegate(this);
8025             supr.setVisible.call(this, true, true, d, cb, e);
8026         }else{
8027             if(!v){
8028                 this.hideUnders(true);
8029             }
8030             var cb = c;
8031             if(a){
8032                 cb = function(){
8033                     this.hideAction();
8034                     if(c){
8035                         c();
8036                     }
8037                 }.createDelegate(this);
8038             }
8039             supr.setVisible.call(this, v, a, d, cb, e);
8040             if(v){
8041                 this.sync(true);
8042             }else if(!a){
8043                 this.hideAction();
8044             }
8045         }
8046     },
8047
8048     storeXY : function(xy){
8049         delete this.lastLT;
8050         this.lastXY = xy;
8051     },
8052
8053     storeLeftTop : function(left, top){
8054         delete this.lastXY;
8055         this.lastLT = [left, top];
8056     },
8057
8058     // private
8059     beforeFx : function(){
8060         this.beforeAction();
8061         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8062     },
8063
8064     // private
8065     afterFx : function(){
8066         Roo.Layer.superclass.afterFx.apply(this, arguments);
8067         this.sync(this.isVisible());
8068     },
8069
8070     // private
8071     beforeAction : function(){
8072         if(!this.updating && this.shadow){
8073             this.shadow.hide();
8074         }
8075     },
8076
8077     // overridden Element method
8078     setLeft : function(left){
8079         this.storeLeftTop(left, this.getTop(true));
8080         supr.setLeft.apply(this, arguments);
8081         this.sync();
8082     },
8083
8084     setTop : function(top){
8085         this.storeLeftTop(this.getLeft(true), top);
8086         supr.setTop.apply(this, arguments);
8087         this.sync();
8088     },
8089
8090     setLeftTop : function(left, top){
8091         this.storeLeftTop(left, top);
8092         supr.setLeftTop.apply(this, arguments);
8093         this.sync();
8094     },
8095
8096     setXY : function(xy, a, d, c, e){
8097         this.fixDisplay();
8098         this.beforeAction();
8099         this.storeXY(xy);
8100         var cb = this.createCB(c);
8101         supr.setXY.call(this, xy, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // private
8108     createCB : function(c){
8109         var el = this;
8110         return function(){
8111             el.constrainXY();
8112             el.sync(true);
8113             if(c){
8114                 c();
8115             }
8116         };
8117     },
8118
8119     // overridden Element method
8120     setX : function(x, a, d, c, e){
8121         this.setXY([x, this.getY()], a, d, c, e);
8122     },
8123
8124     // overridden Element method
8125     setY : function(y, a, d, c, e){
8126         this.setXY([this.getX(), y], a, d, c, e);
8127     },
8128
8129     // overridden Element method
8130     setSize : function(w, h, a, d, c, e){
8131         this.beforeAction();
8132         var cb = this.createCB(c);
8133         supr.setSize.call(this, w, h, a, d, cb, e);
8134         if(!a){
8135             cb();
8136         }
8137     },
8138
8139     // overridden Element method
8140     setWidth : function(w, a, d, c, e){
8141         this.beforeAction();
8142         var cb = this.createCB(c);
8143         supr.setWidth.call(this, w, a, d, cb, e);
8144         if(!a){
8145             cb();
8146         }
8147     },
8148
8149     // overridden Element method
8150     setHeight : function(h, a, d, c, e){
8151         this.beforeAction();
8152         var cb = this.createCB(c);
8153         supr.setHeight.call(this, h, a, d, cb, e);
8154         if(!a){
8155             cb();
8156         }
8157     },
8158
8159     // overridden Element method
8160     setBounds : function(x, y, w, h, a, d, c, e){
8161         this.beforeAction();
8162         var cb = this.createCB(c);
8163         if(!a){
8164             this.storeXY([x, y]);
8165             supr.setXY.call(this, [x, y]);
8166             supr.setSize.call(this, w, h, a, d, cb, e);
8167             cb();
8168         }else{
8169             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8170         }
8171         return this;
8172     },
8173     
8174     /**
8175      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8176      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8177      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8178      * @param {Number} zindex The new z-index to set
8179      * @return {this} The Layer
8180      */
8181     setZIndex : function(zindex){
8182         this.zindex = zindex;
8183         this.setStyle("z-index", zindex + 2);
8184         if(this.shadow){
8185             this.shadow.setZIndex(zindex + 1);
8186         }
8187         if(this.shim){
8188             this.shim.setStyle("z-index", zindex);
8189         }
8190     }
8191 });
8192 })();/*
8193  * Based on:
8194  * Ext JS Library 1.1.1
8195  * Copyright(c) 2006-2007, Ext JS, LLC.
8196  *
8197  * Originally Released Under LGPL - original licence link has changed is not relivant.
8198  *
8199  * Fork - LGPL
8200  * <script type="text/javascript">
8201  */
8202
8203
8204 /**
8205  * @class Roo.Shadow
8206  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8207  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8208  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8209  * @constructor
8210  * Create a new Shadow
8211  * @param {Object} config The config object
8212  */
8213 Roo.Shadow = function(config){
8214     Roo.apply(this, config);
8215     if(typeof this.mode != "string"){
8216         this.mode = this.defaultMode;
8217     }
8218     var o = this.offset, a = {h: 0};
8219     var rad = Math.floor(this.offset/2);
8220     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8221         case "drop":
8222             a.w = 0;
8223             a.l = a.t = o;
8224             a.t -= 1;
8225             if(Roo.isIE){
8226                 a.l -= this.offset + rad;
8227                 a.t -= this.offset + rad;
8228                 a.w -= rad;
8229                 a.h -= rad;
8230                 a.t += 1;
8231             }
8232         break;
8233         case "sides":
8234             a.w = (o*2);
8235             a.l = -o;
8236             a.t = o-1;
8237             if(Roo.isIE){
8238                 a.l -= (this.offset - rad);
8239                 a.t -= this.offset + rad;
8240                 a.l += 1;
8241                 a.w -= (this.offset - rad)*2;
8242                 a.w -= rad + 1;
8243                 a.h -= 1;
8244             }
8245         break;
8246         case "frame":
8247             a.w = a.h = (o*2);
8248             a.l = a.t = -o;
8249             a.t += 1;
8250             a.h -= 2;
8251             if(Roo.isIE){
8252                 a.l -= (this.offset - rad);
8253                 a.t -= (this.offset - rad);
8254                 a.l += 1;
8255                 a.w -= (this.offset + rad + 1);
8256                 a.h -= (this.offset + rad);
8257                 a.h += 1;
8258             }
8259         break;
8260     };
8261
8262     this.adjusts = a;
8263 };
8264
8265 Roo.Shadow.prototype = {
8266     /**
8267      * @cfg {String} mode
8268      * The shadow display mode.  Supports the following options:<br />
8269      * sides: Shadow displays on both sides and bottom only<br />
8270      * frame: Shadow displays equally on all four sides<br />
8271      * drop: Traditional bottom-right drop shadow (default)
8272      */
8273     /**
8274      * @cfg {String} offset
8275      * The number of pixels to offset the shadow from the element (defaults to 4)
8276      */
8277     offset: 4,
8278
8279     // private
8280     defaultMode: "drop",
8281
8282     /**
8283      * Displays the shadow under the target element
8284      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8285      */
8286     show : function(target){
8287         target = Roo.get(target);
8288         if(!this.el){
8289             this.el = Roo.Shadow.Pool.pull();
8290             if(this.el.dom.nextSibling != target.dom){
8291                 this.el.insertBefore(target);
8292             }
8293         }
8294         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8295         if(Roo.isIE){
8296             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8297         }
8298         this.realign(
8299             target.getLeft(true),
8300             target.getTop(true),
8301             target.getWidth(),
8302             target.getHeight()
8303         );
8304         this.el.dom.style.display = "block";
8305     },
8306
8307     /**
8308      * Returns true if the shadow is visible, else false
8309      */
8310     isVisible : function(){
8311         return this.el ? true : false;  
8312     },
8313
8314     /**
8315      * Direct alignment when values are already available. Show must be called at least once before
8316      * calling this method to ensure it is initialized.
8317      * @param {Number} left The target element left position
8318      * @param {Number} top The target element top position
8319      * @param {Number} width The target element width
8320      * @param {Number} height The target element height
8321      */
8322     realign : function(l, t, w, h){
8323         if(!this.el){
8324             return;
8325         }
8326         var a = this.adjusts, d = this.el.dom, s = d.style;
8327         var iea = 0;
8328         s.left = (l+a.l)+"px";
8329         s.top = (t+a.t)+"px";
8330         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8331  
8332         if(s.width != sws || s.height != shs){
8333             s.width = sws;
8334             s.height = shs;
8335             if(!Roo.isIE){
8336                 var cn = d.childNodes;
8337                 var sww = Math.max(0, (sw-12))+"px";
8338                 cn[0].childNodes[1].style.width = sww;
8339                 cn[1].childNodes[1].style.width = sww;
8340                 cn[2].childNodes[1].style.width = sww;
8341                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8342             }
8343         }
8344     },
8345
8346     /**
8347      * Hides this shadow
8348      */
8349     hide : function(){
8350         if(this.el){
8351             this.el.dom.style.display = "none";
8352             Roo.Shadow.Pool.push(this.el);
8353             delete this.el;
8354         }
8355     },
8356
8357     /**
8358      * Adjust the z-index of this shadow
8359      * @param {Number} zindex The new z-index
8360      */
8361     setZIndex : function(z){
8362         this.zIndex = z;
8363         if(this.el){
8364             this.el.setStyle("z-index", z);
8365         }
8366     }
8367 };
8368
8369 // Private utility class that manages the internal Shadow cache
8370 Roo.Shadow.Pool = function(){
8371     var p = [];
8372     var markup = Roo.isIE ?
8373                  '<div class="x-ie-shadow"></div>' :
8374                  '<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>';
8375     return {
8376         pull : function(){
8377             var sh = p.shift();
8378             if(!sh){
8379                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8380                 sh.autoBoxAdjust = false;
8381             }
8382             return sh;
8383         },
8384
8385         push : function(sh){
8386             p.push(sh);
8387         }
8388     };
8389 }();/*
8390  * Based on:
8391  * Ext JS Library 1.1.1
8392  * Copyright(c) 2006-2007, Ext JS, LLC.
8393  *
8394  * Originally Released Under LGPL - original licence link has changed is not relivant.
8395  *
8396  * Fork - LGPL
8397  * <script type="text/javascript">
8398  */
8399
8400 /**
8401  * @class Roo.BoxComponent
8402  * @extends Roo.Component
8403  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8404  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8405  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8406  * layout containers.
8407  * @constructor
8408  * @param {Roo.Element/String/Object} config The configuration options.
8409  */
8410 Roo.BoxComponent = function(config){
8411     Roo.Component.call(this, config);
8412     this.addEvents({
8413         /**
8414          * @event resize
8415          * Fires after the component is resized.
8416              * @param {Roo.Component} this
8417              * @param {Number} adjWidth The box-adjusted width that was set
8418              * @param {Number} adjHeight The box-adjusted height that was set
8419              * @param {Number} rawWidth The width that was originally specified
8420              * @param {Number} rawHeight The height that was originally specified
8421              */
8422         resize : true,
8423         /**
8424          * @event move
8425          * Fires after the component is moved.
8426              * @param {Roo.Component} this
8427              * @param {Number} x The new x position
8428              * @param {Number} y The new y position
8429              */
8430         move : true
8431     });
8432 };
8433
8434 Roo.extend(Roo.BoxComponent, Roo.Component, {
8435     // private, set in afterRender to signify that the component has been rendered
8436     boxReady : false,
8437     // private, used to defer height settings to subclasses
8438     deferHeight: false,
8439     /** @cfg {Number} width
8440      * width (optional) size of component
8441      */
8442      /** @cfg {Number} height
8443      * height (optional) size of component
8444      */
8445      
8446     /**
8447      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8448      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8449      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8450      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8451      * @return {Roo.BoxComponent} this
8452      */
8453     setSize : function(w, h){
8454         // support for standard size objects
8455         if(typeof w == 'object'){
8456             h = w.height;
8457             w = w.width;
8458         }
8459         // not rendered
8460         if(!this.boxReady){
8461             this.width = w;
8462             this.height = h;
8463             return this;
8464         }
8465
8466         // prevent recalcs when not needed
8467         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8468             return this;
8469         }
8470         this.lastSize = {width: w, height: h};
8471
8472         var adj = this.adjustSize(w, h);
8473         var aw = adj.width, ah = adj.height;
8474         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8475             var rz = this.getResizeEl();
8476             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8477                 rz.setSize(aw, ah);
8478             }else if(!this.deferHeight && ah !== undefined){
8479                 rz.setHeight(ah);
8480             }else if(aw !== undefined){
8481                 rz.setWidth(aw);
8482             }
8483             this.onResize(aw, ah, w, h);
8484             this.fireEvent('resize', this, aw, ah, w, h);
8485         }
8486         return this;
8487     },
8488
8489     /**
8490      * Gets the current size of the component's underlying element.
8491      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8492      */
8493     getSize : function(){
8494         return this.el.getSize();
8495     },
8496
8497     /**
8498      * Gets the current XY position of the component's underlying element.
8499      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8500      * @return {Array} The XY position of the element (e.g., [100, 200])
8501      */
8502     getPosition : function(local){
8503         if(local === true){
8504             return [this.el.getLeft(true), this.el.getTop(true)];
8505         }
8506         return this.xy || this.el.getXY();
8507     },
8508
8509     /**
8510      * Gets the current box measurements of the component's underlying element.
8511      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8512      * @returns {Object} box An object in the format {x, y, width, height}
8513      */
8514     getBox : function(local){
8515         var s = this.el.getSize();
8516         if(local){
8517             s.x = this.el.getLeft(true);
8518             s.y = this.el.getTop(true);
8519         }else{
8520             var xy = this.xy || this.el.getXY();
8521             s.x = xy[0];
8522             s.y = xy[1];
8523         }
8524         return s;
8525     },
8526
8527     /**
8528      * Sets the current box measurements of the component's underlying element.
8529      * @param {Object} box An object in the format {x, y, width, height}
8530      * @returns {Roo.BoxComponent} this
8531      */
8532     updateBox : function(box){
8533         this.setSize(box.width, box.height);
8534         this.setPagePosition(box.x, box.y);
8535         return this;
8536     },
8537
8538     // protected
8539     getResizeEl : function(){
8540         return this.resizeEl || this.el;
8541     },
8542
8543     // protected
8544     getPositionEl : function(){
8545         return this.positionEl || this.el;
8546     },
8547
8548     /**
8549      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8550      * This method fires the move event.
8551      * @param {Number} left The new left
8552      * @param {Number} top The new top
8553      * @returns {Roo.BoxComponent} this
8554      */
8555     setPosition : function(x, y){
8556         this.x = x;
8557         this.y = y;
8558         if(!this.boxReady){
8559             return this;
8560         }
8561         var adj = this.adjustPosition(x, y);
8562         var ax = adj.x, ay = adj.y;
8563
8564         var el = this.getPositionEl();
8565         if(ax !== undefined || ay !== undefined){
8566             if(ax !== undefined && ay !== undefined){
8567                 el.setLeftTop(ax, ay);
8568             }else if(ax !== undefined){
8569                 el.setLeft(ax);
8570             }else if(ay !== undefined){
8571                 el.setTop(ay);
8572             }
8573             this.onPosition(ax, ay);
8574             this.fireEvent('move', this, ax, ay);
8575         }
8576         return this;
8577     },
8578
8579     /**
8580      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8581      * This method fires the move event.
8582      * @param {Number} x The new x position
8583      * @param {Number} y The new y position
8584      * @returns {Roo.BoxComponent} this
8585      */
8586     setPagePosition : function(x, y){
8587         this.pageX = x;
8588         this.pageY = y;
8589         if(!this.boxReady){
8590             return;
8591         }
8592         if(x === undefined || y === undefined){ // cannot translate undefined points
8593             return;
8594         }
8595         var p = this.el.translatePoints(x, y);
8596         this.setPosition(p.left, p.top);
8597         return this;
8598     },
8599
8600     // private
8601     onRender : function(ct, position){
8602         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8603         if(this.resizeEl){
8604             this.resizeEl = Roo.get(this.resizeEl);
8605         }
8606         if(this.positionEl){
8607             this.positionEl = Roo.get(this.positionEl);
8608         }
8609     },
8610
8611     // private
8612     afterRender : function(){
8613         Roo.BoxComponent.superclass.afterRender.call(this);
8614         this.boxReady = true;
8615         this.setSize(this.width, this.height);
8616         if(this.x || this.y){
8617             this.setPosition(this.x, this.y);
8618         }
8619         if(this.pageX || this.pageY){
8620             this.setPagePosition(this.pageX, this.pageY);
8621         }
8622     },
8623
8624     /**
8625      * Force the component's size to recalculate based on the underlying element's current height and width.
8626      * @returns {Roo.BoxComponent} this
8627      */
8628     syncSize : function(){
8629         delete this.lastSize;
8630         this.setSize(this.el.getWidth(), this.el.getHeight());
8631         return this;
8632     },
8633
8634     /**
8635      * Called after the component is resized, this method is empty by default but can be implemented by any
8636      * subclass that needs to perform custom logic after a resize occurs.
8637      * @param {Number} adjWidth The box-adjusted width that was set
8638      * @param {Number} adjHeight The box-adjusted height that was set
8639      * @param {Number} rawWidth The width that was originally specified
8640      * @param {Number} rawHeight The height that was originally specified
8641      */
8642     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8643
8644     },
8645
8646     /**
8647      * Called after the component is moved, this method is empty by default but can be implemented by any
8648      * subclass that needs to perform custom logic after a move occurs.
8649      * @param {Number} x The new x position
8650      * @param {Number} y The new y position
8651      */
8652     onPosition : function(x, y){
8653
8654     },
8655
8656     // private
8657     adjustSize : function(w, h){
8658         if(this.autoWidth){
8659             w = 'auto';
8660         }
8661         if(this.autoHeight){
8662             h = 'auto';
8663         }
8664         return {width : w, height: h};
8665     },
8666
8667     // private
8668     adjustPosition : function(x, y){
8669         return {x : x, y: y};
8670     }
8671 });/*
8672  * Based on:
8673  * Ext JS Library 1.1.1
8674  * Copyright(c) 2006-2007, Ext JS, LLC.
8675  *
8676  * Originally Released Under LGPL - original licence link has changed is not relivant.
8677  *
8678  * Fork - LGPL
8679  * <script type="text/javascript">
8680  */
8681
8682
8683 /**
8684  * @class Roo.SplitBar
8685  * @extends Roo.util.Observable
8686  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8687  * <br><br>
8688  * Usage:
8689  * <pre><code>
8690 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8691                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8692 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8693 split.minSize = 100;
8694 split.maxSize = 600;
8695 split.animate = true;
8696 split.on('moved', splitterMoved);
8697 </code></pre>
8698  * @constructor
8699  * Create a new SplitBar
8700  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8701  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8702  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8703  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8704                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8705                         position of the SplitBar).
8706  */
8707 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8708     
8709     /** @private */
8710     this.el = Roo.get(dragElement, true);
8711     this.el.dom.unselectable = "on";
8712     /** @private */
8713     this.resizingEl = Roo.get(resizingElement, true);
8714
8715     /**
8716      * @private
8717      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8718      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8719      * @type Number
8720      */
8721     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8722     
8723     /**
8724      * The minimum size of the resizing element. (Defaults to 0)
8725      * @type Number
8726      */
8727     this.minSize = 0;
8728     
8729     /**
8730      * The maximum size of the resizing element. (Defaults to 2000)
8731      * @type Number
8732      */
8733     this.maxSize = 2000;
8734     
8735     /**
8736      * Whether to animate the transition to the new size
8737      * @type Boolean
8738      */
8739     this.animate = false;
8740     
8741     /**
8742      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8743      * @type Boolean
8744      */
8745     this.useShim = false;
8746     
8747     /** @private */
8748     this.shim = null;
8749     
8750     if(!existingProxy){
8751         /** @private */
8752         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8753     }else{
8754         this.proxy = Roo.get(existingProxy).dom;
8755     }
8756     /** @private */
8757     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8758     
8759     /** @private */
8760     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8761     
8762     /** @private */
8763     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8764     
8765     /** @private */
8766     this.dragSpecs = {};
8767     
8768     /**
8769      * @private The adapter to use to positon and resize elements
8770      */
8771     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8772     this.adapter.init(this);
8773     
8774     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8775         /** @private */
8776         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8777         this.el.addClass("x-splitbar-h");
8778     }else{
8779         /** @private */
8780         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8781         this.el.addClass("x-splitbar-v");
8782     }
8783     
8784     this.addEvents({
8785         /**
8786          * @event resize
8787          * Fires when the splitter is moved (alias for {@link #event-moved})
8788          * @param {Roo.SplitBar} this
8789          * @param {Number} newSize the new width or height
8790          */
8791         "resize" : true,
8792         /**
8793          * @event moved
8794          * Fires when the splitter is moved
8795          * @param {Roo.SplitBar} this
8796          * @param {Number} newSize the new width or height
8797          */
8798         "moved" : true,
8799         /**
8800          * @event beforeresize
8801          * Fires before the splitter is dragged
8802          * @param {Roo.SplitBar} this
8803          */
8804         "beforeresize" : true,
8805
8806         "beforeapply" : true
8807     });
8808
8809     Roo.util.Observable.call(this);
8810 };
8811
8812 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8813     onStartProxyDrag : function(x, y){
8814         this.fireEvent("beforeresize", this);
8815         if(!this.overlay){
8816             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8817             o.unselectable();
8818             o.enableDisplayMode("block");
8819             // all splitbars share the same overlay
8820             Roo.SplitBar.prototype.overlay = o;
8821         }
8822         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8823         this.overlay.show();
8824         Roo.get(this.proxy).setDisplayed("block");
8825         var size = this.adapter.getElementSize(this);
8826         this.activeMinSize = this.getMinimumSize();;
8827         this.activeMaxSize = this.getMaximumSize();;
8828         var c1 = size - this.activeMinSize;
8829         var c2 = Math.max(this.activeMaxSize - size, 0);
8830         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8831             this.dd.resetConstraints();
8832             this.dd.setXConstraint(
8833                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8834                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8835             );
8836             this.dd.setYConstraint(0, 0);
8837         }else{
8838             this.dd.resetConstraints();
8839             this.dd.setXConstraint(0, 0);
8840             this.dd.setYConstraint(
8841                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8842                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8843             );
8844          }
8845         this.dragSpecs.startSize = size;
8846         this.dragSpecs.startPoint = [x, y];
8847         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8848     },
8849     
8850     /** 
8851      * @private Called after the drag operation by the DDProxy
8852      */
8853     onEndProxyDrag : function(e){
8854         Roo.get(this.proxy).setDisplayed(false);
8855         var endPoint = Roo.lib.Event.getXY(e);
8856         if(this.overlay){
8857             this.overlay.hide();
8858         }
8859         var newSize;
8860         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8861             newSize = this.dragSpecs.startSize + 
8862                 (this.placement == Roo.SplitBar.LEFT ?
8863                     endPoint[0] - this.dragSpecs.startPoint[0] :
8864                     this.dragSpecs.startPoint[0] - endPoint[0]
8865                 );
8866         }else{
8867             newSize = this.dragSpecs.startSize + 
8868                 (this.placement == Roo.SplitBar.TOP ?
8869                     endPoint[1] - this.dragSpecs.startPoint[1] :
8870                     this.dragSpecs.startPoint[1] - endPoint[1]
8871                 );
8872         }
8873         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8874         if(newSize != this.dragSpecs.startSize){
8875             if(this.fireEvent('beforeapply', this, newSize) !== false){
8876                 this.adapter.setElementSize(this, newSize);
8877                 this.fireEvent("moved", this, newSize);
8878                 this.fireEvent("resize", this, newSize);
8879             }
8880         }
8881     },
8882     
8883     /**
8884      * Get the adapter this SplitBar uses
8885      * @return The adapter object
8886      */
8887     getAdapter : function(){
8888         return this.adapter;
8889     },
8890     
8891     /**
8892      * Set the adapter this SplitBar uses
8893      * @param {Object} adapter A SplitBar adapter object
8894      */
8895     setAdapter : function(adapter){
8896         this.adapter = adapter;
8897         this.adapter.init(this);
8898     },
8899     
8900     /**
8901      * Gets the minimum size for the resizing element
8902      * @return {Number} The minimum size
8903      */
8904     getMinimumSize : function(){
8905         return this.minSize;
8906     },
8907     
8908     /**
8909      * Sets the minimum size for the resizing element
8910      * @param {Number} minSize The minimum size
8911      */
8912     setMinimumSize : function(minSize){
8913         this.minSize = minSize;
8914     },
8915     
8916     /**
8917      * Gets the maximum size for the resizing element
8918      * @return {Number} The maximum size
8919      */
8920     getMaximumSize : function(){
8921         return this.maxSize;
8922     },
8923     
8924     /**
8925      * Sets the maximum size for the resizing element
8926      * @param {Number} maxSize The maximum size
8927      */
8928     setMaximumSize : function(maxSize){
8929         this.maxSize = maxSize;
8930     },
8931     
8932     /**
8933      * Sets the initialize size for the resizing element
8934      * @param {Number} size The initial size
8935      */
8936     setCurrentSize : function(size){
8937         var oldAnimate = this.animate;
8938         this.animate = false;
8939         this.adapter.setElementSize(this, size);
8940         this.animate = oldAnimate;
8941     },
8942     
8943     /**
8944      * Destroy this splitbar. 
8945      * @param {Boolean} removeEl True to remove the element
8946      */
8947     destroy : function(removeEl){
8948         if(this.shim){
8949             this.shim.remove();
8950         }
8951         this.dd.unreg();
8952         this.proxy.parentNode.removeChild(this.proxy);
8953         if(removeEl){
8954             this.el.remove();
8955         }
8956     }
8957 });
8958
8959 /**
8960  * @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.
8961  */
8962 Roo.SplitBar.createProxy = function(dir){
8963     var proxy = new Roo.Element(document.createElement("div"));
8964     proxy.unselectable();
8965     var cls = 'x-splitbar-proxy';
8966     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8967     document.body.appendChild(proxy.dom);
8968     return proxy.dom;
8969 };
8970
8971 /** 
8972  * @class Roo.SplitBar.BasicLayoutAdapter
8973  * Default Adapter. It assumes the splitter and resizing element are not positioned
8974  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8975  */
8976 Roo.SplitBar.BasicLayoutAdapter = function(){
8977 };
8978
8979 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8980     // do nothing for now
8981     init : function(s){
8982     
8983     },
8984     /**
8985      * Called before drag operations to get the current size of the resizing element. 
8986      * @param {Roo.SplitBar} s The SplitBar using this adapter
8987      */
8988      getElementSize : function(s){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             return s.resizingEl.getWidth();
8991         }else{
8992             return s.resizingEl.getHeight();
8993         }
8994     },
8995     
8996     /**
8997      * Called after drag operations to set the size of the resizing element.
8998      * @param {Roo.SplitBar} s The SplitBar using this adapter
8999      * @param {Number} newSize The new size to set
9000      * @param {Function} onComplete A function to be invoked when resizing is complete
9001      */
9002     setElementSize : function(s, newSize, onComplete){
9003         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9004             if(!s.animate){
9005                 s.resizingEl.setWidth(newSize);
9006                 if(onComplete){
9007                     onComplete(s, newSize);
9008                 }
9009             }else{
9010                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9011             }
9012         }else{
9013             
9014             if(!s.animate){
9015                 s.resizingEl.setHeight(newSize);
9016                 if(onComplete){
9017                     onComplete(s, newSize);
9018                 }
9019             }else{
9020                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9021             }
9022         }
9023     }
9024 };
9025
9026 /** 
9027  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9028  * @extends Roo.SplitBar.BasicLayoutAdapter
9029  * Adapter that  moves the splitter element to align with the resized sizing element. 
9030  * Used with an absolute positioned SplitBar.
9031  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9032  * document.body, make sure you assign an id to the body element.
9033  */
9034 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9035     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9036     this.container = Roo.get(container);
9037 };
9038
9039 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9040     init : function(s){
9041         this.basic.init(s);
9042     },
9043     
9044     getElementSize : function(s){
9045         return this.basic.getElementSize(s);
9046     },
9047     
9048     setElementSize : function(s, newSize, onComplete){
9049         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9050     },
9051     
9052     moveSplitter : function(s){
9053         var yes = Roo.SplitBar;
9054         switch(s.placement){
9055             case yes.LEFT:
9056                 s.el.setX(s.resizingEl.getRight());
9057                 break;
9058             case yes.RIGHT:
9059                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9060                 break;
9061             case yes.TOP:
9062                 s.el.setY(s.resizingEl.getBottom());
9063                 break;
9064             case yes.BOTTOM:
9065                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9066                 break;
9067         }
9068     }
9069 };
9070
9071 /**
9072  * Orientation constant - Create a vertical SplitBar
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.VERTICAL = 1;
9077
9078 /**
9079  * Orientation constant - Create a horizontal SplitBar
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.HORIZONTAL = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is to the left of the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.LEFT = 1;
9091
9092 /**
9093  * Placement constant - The resizing element is to the right of the splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.RIGHT = 2;
9098
9099 /**
9100  * Placement constant - The resizing element is positioned above the splitter element
9101  * @static
9102  * @type Number
9103  */
9104 Roo.SplitBar.TOP = 3;
9105
9106 /**
9107  * Placement constant - The resizing element is positioned under splitter element
9108  * @static
9109  * @type Number
9110  */
9111 Roo.SplitBar.BOTTOM = 4;
9112 /*
9113  * Based on:
9114  * Ext JS Library 1.1.1
9115  * Copyright(c) 2006-2007, Ext JS, LLC.
9116  *
9117  * Originally Released Under LGPL - original licence link has changed is not relivant.
9118  *
9119  * Fork - LGPL
9120  * <script type="text/javascript">
9121  */
9122
9123 /**
9124  * @class Roo.View
9125  * @extends Roo.util.Observable
9126  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9127  * This class also supports single and multi selection modes. <br>
9128  * Create a data model bound view:
9129  <pre><code>
9130  var store = new Roo.data.Store(...);
9131
9132  var view = new Roo.View({
9133     el : "my-element",
9134     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9135  
9136     singleSelect: true,
9137     selectedClass: "ydataview-selected",
9138     store: store
9139  });
9140
9141  // listen for node click?
9142  view.on("click", function(vw, index, node, e){
9143  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9144  });
9145
9146  // load XML data
9147  dataModel.load("foobar.xml");
9148  </code></pre>
9149  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9150  * <br><br>
9151  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9152  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9153  * 
9154  * Note: old style constructor is still suported (container, template, config)
9155  * 
9156  * @constructor
9157  * Create a new View
9158  * @param {Object} config The config object
9159  * 
9160  */
9161 Roo.View = function(config, depreciated_tpl, depreciated_config){
9162     
9163     if (typeof(depreciated_tpl) == 'undefined') {
9164         // new way.. - universal constructor.
9165         Roo.apply(this, config);
9166         this.el  = Roo.get(this.el);
9167     } else {
9168         // old format..
9169         this.el  = Roo.get(config);
9170         this.tpl = depreciated_tpl;
9171         Roo.apply(this, depreciated_config);
9172     }
9173      
9174     
9175     if(typeof(this.tpl) == "string"){
9176         this.tpl = new Roo.Template(this.tpl);
9177     } else {
9178         // support xtype ctors..
9179         this.tpl = new Roo.factory(this.tpl, Roo);
9180     }
9181     
9182     
9183     this.tpl.compile();
9184    
9185
9186      
9187     /** @private */
9188     this.addEvents({
9189         /**
9190          * @event beforeclick
9191          * Fires before a click is processed. Returns false to cancel the default action.
9192          * @param {Roo.View} this
9193          * @param {Number} index The index of the target node
9194          * @param {HTMLElement} node The target node
9195          * @param {Roo.EventObject} e The raw event object
9196          */
9197             "beforeclick" : true,
9198         /**
9199          * @event click
9200          * Fires when a template node is clicked.
9201          * @param {Roo.View} this
9202          * @param {Number} index The index of the target node
9203          * @param {HTMLElement} node The target node
9204          * @param {Roo.EventObject} e The raw event object
9205          */
9206             "click" : true,
9207         /**
9208          * @event dblclick
9209          * Fires when a template node is double clicked.
9210          * @param {Roo.View} this
9211          * @param {Number} index The index of the target node
9212          * @param {HTMLElement} node The target node
9213          * @param {Roo.EventObject} e The raw event object
9214          */
9215             "dblclick" : true,
9216         /**
9217          * @event contextmenu
9218          * Fires when a template node is right clicked.
9219          * @param {Roo.View} this
9220          * @param {Number} index The index of the target node
9221          * @param {HTMLElement} node The target node
9222          * @param {Roo.EventObject} e The raw event object
9223          */
9224             "contextmenu" : true,
9225         /**
9226          * @event selectionchange
9227          * Fires when the selected nodes change.
9228          * @param {Roo.View} this
9229          * @param {Array} selections Array of the selected nodes
9230          */
9231             "selectionchange" : true,
9232     
9233         /**
9234          * @event beforeselect
9235          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9236          * @param {Roo.View} this
9237          * @param {HTMLElement} node The node to be selected
9238          * @param {Array} selections Array of currently selected nodes
9239          */
9240             "beforeselect" : true,
9241         /**
9242          * @event preparedata
9243          * Fires on every row to render, to allow you to change the data.
9244          * @param {Roo.View} this
9245          * @param {Object} data to be rendered (change this)
9246          */
9247           "preparedata" : true
9248         });
9249
9250     this.el.on({
9251         "click": this.onClick,
9252         "dblclick": this.onDblClick,
9253         "contextmenu": this.onContextMenu,
9254         scope:this
9255     });
9256
9257     this.selections = [];
9258     this.nodes = [];
9259     this.cmp = new Roo.CompositeElementLite([]);
9260     if(this.store){
9261         this.store = Roo.factory(this.store, Roo.data);
9262         this.setStore(this.store, true);
9263     }
9264     Roo.View.superclass.constructor.call(this);
9265 };
9266
9267 Roo.extend(Roo.View, Roo.util.Observable, {
9268     
9269      /**
9270      * @cfg {Roo.data.Store} store Data store to load data from.
9271      */
9272     store : false,
9273     
9274     /**
9275      * @cfg {String|Roo.Element} el The container element.
9276      */
9277     el : '',
9278     
9279     /**
9280      * @cfg {String|Roo.Template} tpl The template used by this View 
9281      */
9282     tpl : false,
9283     /**
9284      * @cfg {String} dataName the named area of the template to use as the data area
9285      *                          Works with domtemplates roo-name="name"
9286      */
9287     dataName: false,
9288     /**
9289      * @cfg {String} selectedClass The css class to add to selected nodes
9290      */
9291     selectedClass : "x-view-selected",
9292      /**
9293      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9294      */
9295     emptyText : "",
9296     
9297     /**
9298      * @cfg {String} text to display on mask (default Loading)
9299      */
9300     mask : false,
9301     /**
9302      * @cfg {Boolean} multiSelect Allow multiple selection
9303      */
9304     multiSelect : false,
9305     /**
9306      * @cfg {Boolean} singleSelect Allow single selection
9307      */
9308     singleSelect:  false,
9309     
9310     /**
9311      * @cfg {Boolean} toggleSelect - selecting 
9312      */
9313     toggleSelect : false,
9314     
9315     /**
9316      * Returns the element this view is bound to.
9317      * @return {Roo.Element}
9318      */
9319     getEl : function(){
9320         return this.el;
9321     },
9322
9323     /**
9324      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9325      */
9326     refresh : function(){
9327         var t = this.tpl;
9328         
9329         // if we are using something like 'domtemplate', then
9330         // the what gets used is:
9331         // t.applySubtemplate(NAME, data, wrapping data..)
9332         // the outer template then get' applied with
9333         //     the store 'extra data'
9334         // and the body get's added to the
9335         //      roo-name="data" node?
9336         //      <span class='roo-tpl-{name}'></span> ?????
9337         
9338         
9339         
9340         this.clearSelections();
9341         this.el.update("");
9342         var html = [];
9343         var records = this.store.getRange();
9344         if(records.length < 1) {
9345             
9346             // is this valid??  = should it render a template??
9347             
9348             this.el.update(this.emptyText);
9349             return;
9350         }
9351         var el = this.el;
9352         if (this.dataName) {
9353             this.el.update(t.apply(this.store.meta)); //????
9354             el = this.el.child('.roo-tpl-' + this.dataName);
9355         }
9356         
9357         for(var i = 0, len = records.length; i < len; i++){
9358             var data = this.prepareData(records[i].data, i, records[i]);
9359             this.fireEvent("preparedata", this, data, i, records[i]);
9360             html[html.length] = Roo.util.Format.trim(
9361                 this.dataName ?
9362                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9363                     t.apply(data)
9364             );
9365         }
9366         
9367         
9368         
9369         el.update(html.join(""));
9370         this.nodes = el.dom.childNodes;
9371         this.updateIndexes(0);
9372     },
9373
9374     /**
9375      * Function to override to reformat the data that is sent to
9376      * the template for each node.
9377      * DEPRICATED - use the preparedata event handler.
9378      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9379      * a JSON object for an UpdateManager bound view).
9380      */
9381     prepareData : function(data, index, record)
9382     {
9383         this.fireEvent("preparedata", this, data, index, record);
9384         return data;
9385     },
9386
9387     onUpdate : function(ds, record){
9388         this.clearSelections();
9389         var index = this.store.indexOf(record);
9390         var n = this.nodes[index];
9391         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9392         n.parentNode.removeChild(n);
9393         this.updateIndexes(index, index);
9394     },
9395
9396     
9397     
9398 // --------- FIXME     
9399     onAdd : function(ds, records, index)
9400     {
9401         this.clearSelections();
9402         if(this.nodes.length == 0){
9403             this.refresh();
9404             return;
9405         }
9406         var n = this.nodes[index];
9407         for(var i = 0, len = records.length; i < len; i++){
9408             var d = this.prepareData(records[i].data, i, records[i]);
9409             if(n){
9410                 this.tpl.insertBefore(n, d);
9411             }else{
9412                 
9413                 this.tpl.append(this.el, d);
9414             }
9415         }
9416         this.updateIndexes(index);
9417     },
9418
9419     onRemove : function(ds, record, index){
9420         this.clearSelections();
9421         var el = this.dataName  ?
9422             this.el.child('.roo-tpl-' + this.dataName) :
9423             this.el; 
9424         el.dom.removeChild(this.nodes[index]);
9425         this.updateIndexes(index);
9426     },
9427
9428     /**
9429      * Refresh an individual node.
9430      * @param {Number} index
9431      */
9432     refreshNode : function(index){
9433         this.onUpdate(this.store, this.store.getAt(index));
9434     },
9435
9436     updateIndexes : function(startIndex, endIndex){
9437         var ns = this.nodes;
9438         startIndex = startIndex || 0;
9439         endIndex = endIndex || ns.length - 1;
9440         for(var i = startIndex; i <= endIndex; i++){
9441             ns[i].nodeIndex = i;
9442         }
9443     },
9444
9445     /**
9446      * Changes the data store this view uses and refresh the view.
9447      * @param {Store} store
9448      */
9449     setStore : function(store, initial){
9450         if(!initial && this.store){
9451             this.store.un("datachanged", this.refresh);
9452             this.store.un("add", this.onAdd);
9453             this.store.un("remove", this.onRemove);
9454             this.store.un("update", this.onUpdate);
9455             this.store.un("clear", this.refresh);
9456             this.store.un("beforeload", this.onBeforeLoad);
9457             this.store.un("load", this.onLoad);
9458             this.store.un("loadexception", this.onLoad);
9459         }
9460         if(store){
9461           
9462             store.on("datachanged", this.refresh, this);
9463             store.on("add", this.onAdd, this);
9464             store.on("remove", this.onRemove, this);
9465             store.on("update", this.onUpdate, this);
9466             store.on("clear", this.refresh, this);
9467             store.on("beforeload", this.onBeforeLoad, this);
9468             store.on("load", this.onLoad, this);
9469             store.on("loadexception", this.onLoad, this);
9470         }
9471         
9472         if(store){
9473             this.refresh();
9474         }
9475     },
9476     /**
9477      * onbeforeLoad - masks the loading area.
9478      *
9479      */
9480     onBeforeLoad : function()
9481     {
9482         this.el.update("");
9483         this.el.mask(this.mask ? this.mask : "Loading" ); 
9484     },
9485     onLoad : function ()
9486     {
9487         this.el.unmask();
9488     },
9489     
9490
9491     /**
9492      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9493      * @param {HTMLElement} node
9494      * @return {HTMLElement} The template node
9495      */
9496     findItemFromChild : function(node){
9497         var el = this.dataName  ?
9498             this.el.child('.roo-tpl-' + this.dataName,true) :
9499             this.el.dom; 
9500         
9501         if(!node || node.parentNode == el){
9502                     return node;
9503             }
9504             var p = node.parentNode;
9505             while(p && p != el){
9506             if(p.parentNode == el){
9507                 return p;
9508             }
9509             p = p.parentNode;
9510         }
9511             return null;
9512     },
9513
9514     /** @ignore */
9515     onClick : function(e){
9516         var item = this.findItemFromChild(e.getTarget());
9517         if(item){
9518             var index = this.indexOf(item);
9519             if(this.onItemClick(item, index, e) !== false){
9520                 this.fireEvent("click", this, index, item, e);
9521             }
9522         }else{
9523             this.clearSelections();
9524         }
9525     },
9526
9527     /** @ignore */
9528     onContextMenu : function(e){
9529         var item = this.findItemFromChild(e.getTarget());
9530         if(item){
9531             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9532         }
9533     },
9534
9535     /** @ignore */
9536     onDblClick : function(e){
9537         var item = this.findItemFromChild(e.getTarget());
9538         if(item){
9539             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9540         }
9541     },
9542
9543     onItemClick : function(item, index, e)
9544     {
9545         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9546             return false;
9547         }
9548         if (this.toggleSelect) {
9549             var m = this.isSelected(item) ? 'unselect' : 'select';
9550             Roo.log(m);
9551             var _t = this;
9552             _t[m](item, true, false);
9553             return true;
9554         }
9555         if(this.multiSelect || this.singleSelect){
9556             if(this.multiSelect && e.shiftKey && this.lastSelection){
9557                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9558             }else{
9559                 this.select(item, this.multiSelect && e.ctrlKey);
9560                 this.lastSelection = item;
9561             }
9562             e.preventDefault();
9563         }
9564         return true;
9565     },
9566
9567     /**
9568      * Get the number of selected nodes.
9569      * @return {Number}
9570      */
9571     getSelectionCount : function(){
9572         return this.selections.length;
9573     },
9574
9575     /**
9576      * Get the currently selected nodes.
9577      * @return {Array} An array of HTMLElements
9578      */
9579     getSelectedNodes : function(){
9580         return this.selections;
9581     },
9582
9583     /**
9584      * Get the indexes of the selected nodes.
9585      * @return {Array}
9586      */
9587     getSelectedIndexes : function(){
9588         var indexes = [], s = this.selections;
9589         for(var i = 0, len = s.length; i < len; i++){
9590             indexes.push(s[i].nodeIndex);
9591         }
9592         return indexes;
9593     },
9594
9595     /**
9596      * Clear all selections
9597      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9598      */
9599     clearSelections : function(suppressEvent){
9600         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9601             this.cmp.elements = this.selections;
9602             this.cmp.removeClass(this.selectedClass);
9603             this.selections = [];
9604             if(!suppressEvent){
9605                 this.fireEvent("selectionchange", this, this.selections);
9606             }
9607         }
9608     },
9609
9610     /**
9611      * Returns true if the passed node is selected
9612      * @param {HTMLElement/Number} node The node or node index
9613      * @return {Boolean}
9614      */
9615     isSelected : function(node){
9616         var s = this.selections;
9617         if(s.length < 1){
9618             return false;
9619         }
9620         node = this.getNode(node);
9621         return s.indexOf(node) !== -1;
9622     },
9623
9624     /**
9625      * Selects nodes.
9626      * @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
9627      * @param {Boolean} keepExisting (optional) true to keep existing selections
9628      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9629      */
9630     select : function(nodeInfo, keepExisting, suppressEvent){
9631         if(nodeInfo instanceof Array){
9632             if(!keepExisting){
9633                 this.clearSelections(true);
9634             }
9635             for(var i = 0, len = nodeInfo.length; i < len; i++){
9636                 this.select(nodeInfo[i], true, true);
9637             }
9638             return;
9639         } 
9640         var node = this.getNode(nodeInfo);
9641         if(!node || this.isSelected(node)){
9642             return; // already selected.
9643         }
9644         if(!keepExisting){
9645             this.clearSelections(true);
9646         }
9647         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9648             Roo.fly(node).addClass(this.selectedClass);
9649             this.selections.push(node);
9650             if(!suppressEvent){
9651                 this.fireEvent("selectionchange", this, this.selections);
9652             }
9653         }
9654         
9655         
9656     },
9657       /**
9658      * Unselects nodes.
9659      * @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
9660      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9661      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9662      */
9663     unselect : function(nodeInfo, keepExisting, suppressEvent)
9664     {
9665         if(nodeInfo instanceof Array){
9666             Roo.each(this.selections, function(s) {
9667                 this.unselect(s, nodeInfo);
9668             }, this);
9669             return;
9670         }
9671         var node = this.getNode(nodeInfo);
9672         if(!node || !this.isSelected(node)){
9673             Roo.log("not selected");
9674             return; // not selected.
9675         }
9676         // fireevent???
9677         var ns = [];
9678         Roo.each(this.selections, function(s) {
9679             if (s == node ) {
9680                 Roo.fly(node).removeClass(this.selectedClass);
9681
9682                 return;
9683             }
9684             ns.push(s);
9685         },this);
9686         
9687         this.selections= ns;
9688         this.fireEvent("selectionchange", this, this.selections);
9689     },
9690
9691     /**
9692      * Gets a template node.
9693      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9694      * @return {HTMLElement} The node or null if it wasn't found
9695      */
9696     getNode : function(nodeInfo){
9697         if(typeof nodeInfo == "string"){
9698             return document.getElementById(nodeInfo);
9699         }else if(typeof nodeInfo == "number"){
9700             return this.nodes[nodeInfo];
9701         }
9702         return nodeInfo;
9703     },
9704
9705     /**
9706      * Gets a range template nodes.
9707      * @param {Number} startIndex
9708      * @param {Number} endIndex
9709      * @return {Array} An array of nodes
9710      */
9711     getNodes : function(start, end){
9712         var ns = this.nodes;
9713         start = start || 0;
9714         end = typeof end == "undefined" ? ns.length - 1 : end;
9715         var nodes = [];
9716         if(start <= end){
9717             for(var i = start; i <= end; i++){
9718                 nodes.push(ns[i]);
9719             }
9720         } else{
9721             for(var i = start; i >= end; i--){
9722                 nodes.push(ns[i]);
9723             }
9724         }
9725         return nodes;
9726     },
9727
9728     /**
9729      * Finds the index of the passed node
9730      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9731      * @return {Number} The index of the node or -1
9732      */
9733     indexOf : function(node){
9734         node = this.getNode(node);
9735         if(typeof node.nodeIndex == "number"){
9736             return node.nodeIndex;
9737         }
9738         var ns = this.nodes;
9739         for(var i = 0, len = ns.length; i < len; i++){
9740             if(ns[i] == node){
9741                 return i;
9742             }
9743         }
9744         return -1;
9745     }
9746 });
9747 /*
9748  * Based on:
9749  * Ext JS Library 1.1.1
9750  * Copyright(c) 2006-2007, Ext JS, LLC.
9751  *
9752  * Originally Released Under LGPL - original licence link has changed is not relivant.
9753  *
9754  * Fork - LGPL
9755  * <script type="text/javascript">
9756  */
9757
9758 /**
9759  * @class Roo.JsonView
9760  * @extends Roo.View
9761  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9762 <pre><code>
9763 var view = new Roo.JsonView({
9764     container: "my-element",
9765     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9766     multiSelect: true, 
9767     jsonRoot: "data" 
9768 });
9769
9770 // listen for node click?
9771 view.on("click", function(vw, index, node, e){
9772     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9773 });
9774
9775 // direct load of JSON data
9776 view.load("foobar.php");
9777
9778 // Example from my blog list
9779 var tpl = new Roo.Template(
9780     '&lt;div class="entry"&gt;' +
9781     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9782     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9783     "&lt;/div&gt;&lt;hr /&gt;"
9784 );
9785
9786 var moreView = new Roo.JsonView({
9787     container :  "entry-list", 
9788     template : tpl,
9789     jsonRoot: "posts"
9790 });
9791 moreView.on("beforerender", this.sortEntries, this);
9792 moreView.load({
9793     url: "/blog/get-posts.php",
9794     params: "allposts=true",
9795     text: "Loading Blog Entries..."
9796 });
9797 </code></pre>
9798
9799 * Note: old code is supported with arguments : (container, template, config)
9800
9801
9802  * @constructor
9803  * Create a new JsonView
9804  * 
9805  * @param {Object} config The config object
9806  * 
9807  */
9808 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9809     
9810     
9811     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9812
9813     var um = this.el.getUpdateManager();
9814     um.setRenderer(this);
9815     um.on("update", this.onLoad, this);
9816     um.on("failure", this.onLoadException, this);
9817
9818     /**
9819      * @event beforerender
9820      * Fires before rendering of the downloaded JSON data.
9821      * @param {Roo.JsonView} this
9822      * @param {Object} data The JSON data loaded
9823      */
9824     /**
9825      * @event load
9826      * Fires when data is loaded.
9827      * @param {Roo.JsonView} this
9828      * @param {Object} data The JSON data loaded
9829      * @param {Object} response The raw Connect response object
9830      */
9831     /**
9832      * @event loadexception
9833      * Fires when loading fails.
9834      * @param {Roo.JsonView} this
9835      * @param {Object} response The raw Connect response object
9836      */
9837     this.addEvents({
9838         'beforerender' : true,
9839         'load' : true,
9840         'loadexception' : true
9841     });
9842 };
9843 Roo.extend(Roo.JsonView, Roo.View, {
9844     /**
9845      * @type {String} The root property in the loaded JSON object that contains the data
9846      */
9847     jsonRoot : "",
9848
9849     /**
9850      * Refreshes the view.
9851      */
9852     refresh : function(){
9853         this.clearSelections();
9854         this.el.update("");
9855         var html = [];
9856         var o = this.jsonData;
9857         if(o && o.length > 0){
9858             for(var i = 0, len = o.length; i < len; i++){
9859                 var data = this.prepareData(o[i], i, o);
9860                 html[html.length] = this.tpl.apply(data);
9861             }
9862         }else{
9863             html.push(this.emptyText);
9864         }
9865         this.el.update(html.join(""));
9866         this.nodes = this.el.dom.childNodes;
9867         this.updateIndexes(0);
9868     },
9869
9870     /**
9871      * 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.
9872      * @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:
9873      <pre><code>
9874      view.load({
9875          url: "your-url.php",
9876          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9877          callback: yourFunction,
9878          scope: yourObject, //(optional scope)
9879          discardUrl: false,
9880          nocache: false,
9881          text: "Loading...",
9882          timeout: 30,
9883          scripts: false
9884      });
9885      </code></pre>
9886      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9887      * 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.
9888      * @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}
9889      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9890      * @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.
9891      */
9892     load : function(){
9893         var um = this.el.getUpdateManager();
9894         um.update.apply(um, arguments);
9895     },
9896
9897     render : function(el, response){
9898         this.clearSelections();
9899         this.el.update("");
9900         var o;
9901         try{
9902             o = Roo.util.JSON.decode(response.responseText);
9903             if(this.jsonRoot){
9904                 
9905                 o = o[this.jsonRoot];
9906             }
9907         } catch(e){
9908         }
9909         /**
9910          * The current JSON data or null
9911          */
9912         this.jsonData = o;
9913         this.beforeRender();
9914         this.refresh();
9915     },
9916
9917 /**
9918  * Get the number of records in the current JSON dataset
9919  * @return {Number}
9920  */
9921     getCount : function(){
9922         return this.jsonData ? this.jsonData.length : 0;
9923     },
9924
9925 /**
9926  * Returns the JSON object for the specified node(s)
9927  * @param {HTMLElement/Array} node The node or an array of nodes
9928  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9929  * you get the JSON object for the node
9930  */
9931     getNodeData : function(node){
9932         if(node instanceof Array){
9933             var data = [];
9934             for(var i = 0, len = node.length; i < len; i++){
9935                 data.push(this.getNodeData(node[i]));
9936             }
9937             return data;
9938         }
9939         return this.jsonData[this.indexOf(node)] || null;
9940     },
9941
9942     beforeRender : function(){
9943         this.snapshot = this.jsonData;
9944         if(this.sortInfo){
9945             this.sort.apply(this, this.sortInfo);
9946         }
9947         this.fireEvent("beforerender", this, this.jsonData);
9948     },
9949
9950     onLoad : function(el, o){
9951         this.fireEvent("load", this, this.jsonData, o);
9952     },
9953
9954     onLoadException : function(el, o){
9955         this.fireEvent("loadexception", this, o);
9956     },
9957
9958 /**
9959  * Filter the data by a specific property.
9960  * @param {String} property A property on your JSON objects
9961  * @param {String/RegExp} value Either string that the property values
9962  * should start with, or a RegExp to test against the property
9963  */
9964     filter : function(property, value){
9965         if(this.jsonData){
9966             var data = [];
9967             var ss = this.snapshot;
9968             if(typeof value == "string"){
9969                 var vlen = value.length;
9970                 if(vlen == 0){
9971                     this.clearFilter();
9972                     return;
9973                 }
9974                 value = value.toLowerCase();
9975                 for(var i = 0, len = ss.length; i < len; i++){
9976                     var o = ss[i];
9977                     if(o[property].substr(0, vlen).toLowerCase() == value){
9978                         data.push(o);
9979                     }
9980                 }
9981             } else if(value.exec){ // regex?
9982                 for(var i = 0, len = ss.length; i < len; i++){
9983                     var o = ss[i];
9984                     if(value.test(o[property])){
9985                         data.push(o);
9986                     }
9987                 }
9988             } else{
9989                 return;
9990             }
9991             this.jsonData = data;
9992             this.refresh();
9993         }
9994     },
9995
9996 /**
9997  * Filter by a function. The passed function will be called with each
9998  * object in the current dataset. If the function returns true the value is kept,
9999  * otherwise it is filtered.
10000  * @param {Function} fn
10001  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10002  */
10003     filterBy : function(fn, scope){
10004         if(this.jsonData){
10005             var data = [];
10006             var ss = this.snapshot;
10007             for(var i = 0, len = ss.length; i < len; i++){
10008                 var o = ss[i];
10009                 if(fn.call(scope || this, o)){
10010                     data.push(o);
10011                 }
10012             }
10013             this.jsonData = data;
10014             this.refresh();
10015         }
10016     },
10017
10018 /**
10019  * Clears the current filter.
10020  */
10021     clearFilter : function(){
10022         if(this.snapshot && this.jsonData != this.snapshot){
10023             this.jsonData = this.snapshot;
10024             this.refresh();
10025         }
10026     },
10027
10028
10029 /**
10030  * Sorts the data for this view and refreshes it.
10031  * @param {String} property A property on your JSON objects to sort on
10032  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10033  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10034  */
10035     sort : function(property, dir, sortType){
10036         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10037         if(this.jsonData){
10038             var p = property;
10039             var dsc = dir && dir.toLowerCase() == "desc";
10040             var f = function(o1, o2){
10041                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10042                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10043                 ;
10044                 if(v1 < v2){
10045                     return dsc ? +1 : -1;
10046                 } else if(v1 > v2){
10047                     return dsc ? -1 : +1;
10048                 } else{
10049                     return 0;
10050                 }
10051             };
10052             this.jsonData.sort(f);
10053             this.refresh();
10054             if(this.jsonData != this.snapshot){
10055                 this.snapshot.sort(f);
10056             }
10057         }
10058     }
10059 });/*
10060  * Based on:
10061  * Ext JS Library 1.1.1
10062  * Copyright(c) 2006-2007, Ext JS, LLC.
10063  *
10064  * Originally Released Under LGPL - original licence link has changed is not relivant.
10065  *
10066  * Fork - LGPL
10067  * <script type="text/javascript">
10068  */
10069  
10070
10071 /**
10072  * @class Roo.ColorPalette
10073  * @extends Roo.Component
10074  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10075  * Here's an example of typical usage:
10076  * <pre><code>
10077 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10078 cp.render('my-div');
10079
10080 cp.on('select', function(palette, selColor){
10081     // do something with selColor
10082 });
10083 </code></pre>
10084  * @constructor
10085  * Create a new ColorPalette
10086  * @param {Object} config The config object
10087  */
10088 Roo.ColorPalette = function(config){
10089     Roo.ColorPalette.superclass.constructor.call(this, config);
10090     this.addEvents({
10091         /**
10092              * @event select
10093              * Fires when a color is selected
10094              * @param {ColorPalette} this
10095              * @param {String} color The 6-digit color hex code (without the # symbol)
10096              */
10097         select: true
10098     });
10099
10100     if(this.handler){
10101         this.on("select", this.handler, this.scope, true);
10102     }
10103 };
10104 Roo.extend(Roo.ColorPalette, Roo.Component, {
10105     /**
10106      * @cfg {String} itemCls
10107      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10108      */
10109     itemCls : "x-color-palette",
10110     /**
10111      * @cfg {String} value
10112      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10113      * the hex codes are case-sensitive.
10114      */
10115     value : null,
10116     clickEvent:'click',
10117     // private
10118     ctype: "Roo.ColorPalette",
10119
10120     /**
10121      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10122      */
10123     allowReselect : false,
10124
10125     /**
10126      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10127      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10128      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10129      * of colors with the width setting until the box is symmetrical.</p>
10130      * <p>You can override individual colors if needed:</p>
10131      * <pre><code>
10132 var cp = new Roo.ColorPalette();
10133 cp.colors[0] = "FF0000";  // change the first box to red
10134 </code></pre>
10135
10136 Or you can provide a custom array of your own for complete control:
10137 <pre><code>
10138 var cp = new Roo.ColorPalette();
10139 cp.colors = ["000000", "993300", "333300"];
10140 </code></pre>
10141      * @type Array
10142      */
10143     colors : [
10144         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10145         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10146         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10147         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10148         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10149     ],
10150
10151     // private
10152     onRender : function(container, position){
10153         var t = new Roo.MasterTemplate(
10154             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10155         );
10156         var c = this.colors;
10157         for(var i = 0, len = c.length; i < len; i++){
10158             t.add([c[i]]);
10159         }
10160         var el = document.createElement("div");
10161         el.className = this.itemCls;
10162         t.overwrite(el);
10163         container.dom.insertBefore(el, position);
10164         this.el = Roo.get(el);
10165         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10166         if(this.clickEvent != 'click'){
10167             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10168         }
10169     },
10170
10171     // private
10172     afterRender : function(){
10173         Roo.ColorPalette.superclass.afterRender.call(this);
10174         if(this.value){
10175             var s = this.value;
10176             this.value = null;
10177             this.select(s);
10178         }
10179     },
10180
10181     // private
10182     handleClick : function(e, t){
10183         e.preventDefault();
10184         if(!this.disabled){
10185             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10186             this.select(c.toUpperCase());
10187         }
10188     },
10189
10190     /**
10191      * Selects the specified color in the palette (fires the select event)
10192      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10193      */
10194     select : function(color){
10195         color = color.replace("#", "");
10196         if(color != this.value || this.allowReselect){
10197             var el = this.el;
10198             if(this.value){
10199                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10200             }
10201             el.child("a.color-"+color).addClass("x-color-palette-sel");
10202             this.value = color;
10203             this.fireEvent("select", this, color);
10204         }
10205     }
10206 });/*
10207  * Based on:
10208  * Ext JS Library 1.1.1
10209  * Copyright(c) 2006-2007, Ext JS, LLC.
10210  *
10211  * Originally Released Under LGPL - original licence link has changed is not relivant.
10212  *
10213  * Fork - LGPL
10214  * <script type="text/javascript">
10215  */
10216  
10217 /**
10218  * @class Roo.DatePicker
10219  * @extends Roo.Component
10220  * Simple date picker class.
10221  * @constructor
10222  * Create a new DatePicker
10223  * @param {Object} config The config object
10224  */
10225 Roo.DatePicker = function(config){
10226     Roo.DatePicker.superclass.constructor.call(this, config);
10227
10228     this.value = config && config.value ?
10229                  config.value.clearTime() : new Date().clearTime();
10230
10231     this.addEvents({
10232         /**
10233              * @event select
10234              * Fires when a date is selected
10235              * @param {DatePicker} this
10236              * @param {Date} date The selected date
10237              */
10238         'select': true,
10239         /**
10240              * @event monthchange
10241              * Fires when the displayed month changes 
10242              * @param {DatePicker} this
10243              * @param {Date} date The selected month
10244              */
10245         'monthchange': true
10246     });
10247
10248     if(this.handler){
10249         this.on("select", this.handler,  this.scope || this);
10250     }
10251     // build the disabledDatesRE
10252     if(!this.disabledDatesRE && this.disabledDates){
10253         var dd = this.disabledDates;
10254         var re = "(?:";
10255         for(var i = 0; i < dd.length; i++){
10256             re += dd[i];
10257             if(i != dd.length-1) re += "|";
10258         }
10259         this.disabledDatesRE = new RegExp(re + ")");
10260     }
10261 };
10262
10263 Roo.extend(Roo.DatePicker, Roo.Component, {
10264     /**
10265      * @cfg {String} todayText
10266      * The text to display on the button that selects the current date (defaults to "Today")
10267      */
10268     todayText : "Today",
10269     /**
10270      * @cfg {String} okText
10271      * The text to display on the ok button
10272      */
10273     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10274     /**
10275      * @cfg {String} cancelText
10276      * The text to display on the cancel button
10277      */
10278     cancelText : "Cancel",
10279     /**
10280      * @cfg {String} todayTip
10281      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10282      */
10283     todayTip : "{0} (Spacebar)",
10284     /**
10285      * @cfg {Date} minDate
10286      * Minimum allowable date (JavaScript date object, defaults to null)
10287      */
10288     minDate : null,
10289     /**
10290      * @cfg {Date} maxDate
10291      * Maximum allowable date (JavaScript date object, defaults to null)
10292      */
10293     maxDate : null,
10294     /**
10295      * @cfg {String} minText
10296      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10297      */
10298     minText : "This date is before the minimum date",
10299     /**
10300      * @cfg {String} maxText
10301      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10302      */
10303     maxText : "This date is after the maximum date",
10304     /**
10305      * @cfg {String} format
10306      * The default date format string which can be overriden for localization support.  The format must be
10307      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10308      */
10309     format : "m/d/y",
10310     /**
10311      * @cfg {Array} disabledDays
10312      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10313      */
10314     disabledDays : null,
10315     /**
10316      * @cfg {String} disabledDaysText
10317      * The tooltip to display when the date falls on a disabled day (defaults to "")
10318      */
10319     disabledDaysText : "",
10320     /**
10321      * @cfg {RegExp} disabledDatesRE
10322      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10323      */
10324     disabledDatesRE : null,
10325     /**
10326      * @cfg {String} disabledDatesText
10327      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10328      */
10329     disabledDatesText : "",
10330     /**
10331      * @cfg {Boolean} constrainToViewport
10332      * True to constrain the date picker to the viewport (defaults to true)
10333      */
10334     constrainToViewport : true,
10335     /**
10336      * @cfg {Array} monthNames
10337      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10338      */
10339     monthNames : Date.monthNames,
10340     /**
10341      * @cfg {Array} dayNames
10342      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10343      */
10344     dayNames : Date.dayNames,
10345     /**
10346      * @cfg {String} nextText
10347      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10348      */
10349     nextText: 'Next Month (Control+Right)',
10350     /**
10351      * @cfg {String} prevText
10352      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10353      */
10354     prevText: 'Previous Month (Control+Left)',
10355     /**
10356      * @cfg {String} monthYearText
10357      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10358      */
10359     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10360     /**
10361      * @cfg {Number} startDay
10362      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10363      */
10364     startDay : 0,
10365     /**
10366      * @cfg {Bool} showClear
10367      * Show a clear button (usefull for date form elements that can be blank.)
10368      */
10369     
10370     showClear: false,
10371     
10372     /**
10373      * Sets the value of the date field
10374      * @param {Date} value The date to set
10375      */
10376     setValue : function(value){
10377         var old = this.value;
10378         
10379         if (typeof(value) == 'string') {
10380          
10381             value = Date.parseDate(value, this.format);
10382         }
10383         if (!value) {
10384             value = new Date();
10385         }
10386         
10387         this.value = value.clearTime(true);
10388         if(this.el){
10389             this.update(this.value);
10390         }
10391     },
10392
10393     /**
10394      * Gets the current selected value of the date field
10395      * @return {Date} The selected date
10396      */
10397     getValue : function(){
10398         return this.value;
10399     },
10400
10401     // private
10402     focus : function(){
10403         if(this.el){
10404             this.update(this.activeDate);
10405         }
10406     },
10407
10408     // privateval
10409     onRender : function(container, position){
10410         
10411         var m = [
10412              '<table cellspacing="0">',
10413                 '<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>',
10414                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10415         var dn = this.dayNames;
10416         for(var i = 0; i < 7; i++){
10417             var d = this.startDay+i;
10418             if(d > 6){
10419                 d = d-7;
10420             }
10421             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10422         }
10423         m[m.length] = "</tr></thead><tbody><tr>";
10424         for(var i = 0; i < 42; i++) {
10425             if(i % 7 == 0 && i != 0){
10426                 m[m.length] = "</tr><tr>";
10427             }
10428             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10429         }
10430         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10431             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10432
10433         var el = document.createElement("div");
10434         el.className = "x-date-picker";
10435         el.innerHTML = m.join("");
10436
10437         container.dom.insertBefore(el, position);
10438
10439         this.el = Roo.get(el);
10440         this.eventEl = Roo.get(el.firstChild);
10441
10442         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10443             handler: this.showPrevMonth,
10444             scope: this,
10445             preventDefault:true,
10446             stopDefault:true
10447         });
10448
10449         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10450             handler: this.showNextMonth,
10451             scope: this,
10452             preventDefault:true,
10453             stopDefault:true
10454         });
10455
10456         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10457
10458         this.monthPicker = this.el.down('div.x-date-mp');
10459         this.monthPicker.enableDisplayMode('block');
10460         
10461         var kn = new Roo.KeyNav(this.eventEl, {
10462             "left" : function(e){
10463                 e.ctrlKey ?
10464                     this.showPrevMonth() :
10465                     this.update(this.activeDate.add("d", -1));
10466             },
10467
10468             "right" : function(e){
10469                 e.ctrlKey ?
10470                     this.showNextMonth() :
10471                     this.update(this.activeDate.add("d", 1));
10472             },
10473
10474             "up" : function(e){
10475                 e.ctrlKey ?
10476                     this.showNextYear() :
10477                     this.update(this.activeDate.add("d", -7));
10478             },
10479
10480             "down" : function(e){
10481                 e.ctrlKey ?
10482                     this.showPrevYear() :
10483                     this.update(this.activeDate.add("d", 7));
10484             },
10485
10486             "pageUp" : function(e){
10487                 this.showNextMonth();
10488             },
10489
10490             "pageDown" : function(e){
10491                 this.showPrevMonth();
10492             },
10493
10494             "enter" : function(e){
10495                 e.stopPropagation();
10496                 return true;
10497             },
10498
10499             scope : this
10500         });
10501
10502         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10503
10504         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10505
10506         this.el.unselectable();
10507         
10508         this.cells = this.el.select("table.x-date-inner tbody td");
10509         this.textNodes = this.el.query("table.x-date-inner tbody span");
10510
10511         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10512             text: "&#160;",
10513             tooltip: this.monthYearText
10514         });
10515
10516         this.mbtn.on('click', this.showMonthPicker, this);
10517         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10518
10519
10520         var today = (new Date()).dateFormat(this.format);
10521         
10522         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10523         if (this.showClear) {
10524             baseTb.add( new Roo.Toolbar.Fill());
10525         }
10526         baseTb.add({
10527             text: String.format(this.todayText, today),
10528             tooltip: String.format(this.todayTip, today),
10529             handler: this.selectToday,
10530             scope: this
10531         });
10532         
10533         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10534             
10535         //});
10536         if (this.showClear) {
10537             
10538             baseTb.add( new Roo.Toolbar.Fill());
10539             baseTb.add({
10540                 text: '&#160;',
10541                 cls: 'x-btn-icon x-btn-clear',
10542                 handler: function() {
10543                     //this.value = '';
10544                     this.fireEvent("select", this, '');
10545                 },
10546                 scope: this
10547             });
10548         }
10549         
10550         
10551         if(Roo.isIE){
10552             this.el.repaint();
10553         }
10554         this.update(this.value);
10555     },
10556
10557     createMonthPicker : function(){
10558         if(!this.monthPicker.dom.firstChild){
10559             var buf = ['<table border="0" cellspacing="0">'];
10560             for(var i = 0; i < 6; i++){
10561                 buf.push(
10562                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10563                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10564                     i == 0 ?
10565                     '<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>' :
10566                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10567                 );
10568             }
10569             buf.push(
10570                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10571                     this.okText,
10572                     '</button><button type="button" class="x-date-mp-cancel">',
10573                     this.cancelText,
10574                     '</button></td></tr>',
10575                 '</table>'
10576             );
10577             this.monthPicker.update(buf.join(''));
10578             this.monthPicker.on('click', this.onMonthClick, this);
10579             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10580
10581             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10582             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10583
10584             this.mpMonths.each(function(m, a, i){
10585                 i += 1;
10586                 if((i%2) == 0){
10587                     m.dom.xmonth = 5 + Math.round(i * .5);
10588                 }else{
10589                     m.dom.xmonth = Math.round((i-1) * .5);
10590                 }
10591             });
10592         }
10593     },
10594
10595     showMonthPicker : function(){
10596         this.createMonthPicker();
10597         var size = this.el.getSize();
10598         this.monthPicker.setSize(size);
10599         this.monthPicker.child('table').setSize(size);
10600
10601         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10602         this.updateMPMonth(this.mpSelMonth);
10603         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10604         this.updateMPYear(this.mpSelYear);
10605
10606         this.monthPicker.slideIn('t', {duration:.2});
10607     },
10608
10609     updateMPYear : function(y){
10610         this.mpyear = y;
10611         var ys = this.mpYears.elements;
10612         for(var i = 1; i <= 10; i++){
10613             var td = ys[i-1], y2;
10614             if((i%2) == 0){
10615                 y2 = y + Math.round(i * .5);
10616                 td.firstChild.innerHTML = y2;
10617                 td.xyear = y2;
10618             }else{
10619                 y2 = y - (5-Math.round(i * .5));
10620                 td.firstChild.innerHTML = y2;
10621                 td.xyear = y2;
10622             }
10623             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10624         }
10625     },
10626
10627     updateMPMonth : function(sm){
10628         this.mpMonths.each(function(m, a, i){
10629             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10630         });
10631     },
10632
10633     selectMPMonth: function(m){
10634         
10635     },
10636
10637     onMonthClick : function(e, t){
10638         e.stopEvent();
10639         var el = new Roo.Element(t), pn;
10640         if(el.is('button.x-date-mp-cancel')){
10641             this.hideMonthPicker();
10642         }
10643         else if(el.is('button.x-date-mp-ok')){
10644             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10645             this.hideMonthPicker();
10646         }
10647         else if(pn = el.up('td.x-date-mp-month', 2)){
10648             this.mpMonths.removeClass('x-date-mp-sel');
10649             pn.addClass('x-date-mp-sel');
10650             this.mpSelMonth = pn.dom.xmonth;
10651         }
10652         else if(pn = el.up('td.x-date-mp-year', 2)){
10653             this.mpYears.removeClass('x-date-mp-sel');
10654             pn.addClass('x-date-mp-sel');
10655             this.mpSelYear = pn.dom.xyear;
10656         }
10657         else if(el.is('a.x-date-mp-prev')){
10658             this.updateMPYear(this.mpyear-10);
10659         }
10660         else if(el.is('a.x-date-mp-next')){
10661             this.updateMPYear(this.mpyear+10);
10662         }
10663     },
10664
10665     onMonthDblClick : function(e, t){
10666         e.stopEvent();
10667         var el = new Roo.Element(t), pn;
10668         if(pn = el.up('td.x-date-mp-month', 2)){
10669             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10670             this.hideMonthPicker();
10671         }
10672         else if(pn = el.up('td.x-date-mp-year', 2)){
10673             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10674             this.hideMonthPicker();
10675         }
10676     },
10677
10678     hideMonthPicker : function(disableAnim){
10679         if(this.monthPicker){
10680             if(disableAnim === true){
10681                 this.monthPicker.hide();
10682             }else{
10683                 this.monthPicker.slideOut('t', {duration:.2});
10684             }
10685         }
10686     },
10687
10688     // private
10689     showPrevMonth : function(e){
10690         this.update(this.activeDate.add("mo", -1));
10691     },
10692
10693     // private
10694     showNextMonth : function(e){
10695         this.update(this.activeDate.add("mo", 1));
10696     },
10697
10698     // private
10699     showPrevYear : function(){
10700         this.update(this.activeDate.add("y", -1));
10701     },
10702
10703     // private
10704     showNextYear : function(){
10705         this.update(this.activeDate.add("y", 1));
10706     },
10707
10708     // private
10709     handleMouseWheel : function(e){
10710         var delta = e.getWheelDelta();
10711         if(delta > 0){
10712             this.showPrevMonth();
10713             e.stopEvent();
10714         } else if(delta < 0){
10715             this.showNextMonth();
10716             e.stopEvent();
10717         }
10718     },
10719
10720     // private
10721     handleDateClick : function(e, t){
10722         e.stopEvent();
10723         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10724             this.setValue(new Date(t.dateValue));
10725             this.fireEvent("select", this, this.value);
10726         }
10727     },
10728
10729     // private
10730     selectToday : function(){
10731         this.setValue(new Date().clearTime());
10732         this.fireEvent("select", this, this.value);
10733     },
10734
10735     // private
10736     update : function(date)
10737     {
10738         var vd = this.activeDate;
10739         this.activeDate = date;
10740         if(vd && this.el){
10741             var t = date.getTime();
10742             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10743                 this.cells.removeClass("x-date-selected");
10744                 this.cells.each(function(c){
10745                    if(c.dom.firstChild.dateValue == t){
10746                        c.addClass("x-date-selected");
10747                        setTimeout(function(){
10748                             try{c.dom.firstChild.focus();}catch(e){}
10749                        }, 50);
10750                        return false;
10751                    }
10752                 });
10753                 return;
10754             }
10755         }
10756         
10757         var days = date.getDaysInMonth();
10758         var firstOfMonth = date.getFirstDateOfMonth();
10759         var startingPos = firstOfMonth.getDay()-this.startDay;
10760
10761         if(startingPos <= this.startDay){
10762             startingPos += 7;
10763         }
10764
10765         var pm = date.add("mo", -1);
10766         var prevStart = pm.getDaysInMonth()-startingPos;
10767
10768         var cells = this.cells.elements;
10769         var textEls = this.textNodes;
10770         days += startingPos;
10771
10772         // convert everything to numbers so it's fast
10773         var day = 86400000;
10774         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10775         var today = new Date().clearTime().getTime();
10776         var sel = date.clearTime().getTime();
10777         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10778         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10779         var ddMatch = this.disabledDatesRE;
10780         var ddText = this.disabledDatesText;
10781         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10782         var ddaysText = this.disabledDaysText;
10783         var format = this.format;
10784
10785         var setCellClass = function(cal, cell){
10786             cell.title = "";
10787             var t = d.getTime();
10788             cell.firstChild.dateValue = t;
10789             if(t == today){
10790                 cell.className += " x-date-today";
10791                 cell.title = cal.todayText;
10792             }
10793             if(t == sel){
10794                 cell.className += " x-date-selected";
10795                 setTimeout(function(){
10796                     try{cell.firstChild.focus();}catch(e){}
10797                 }, 50);
10798             }
10799             // disabling
10800             if(t < min) {
10801                 cell.className = " x-date-disabled";
10802                 cell.title = cal.minText;
10803                 return;
10804             }
10805             if(t > max) {
10806                 cell.className = " x-date-disabled";
10807                 cell.title = cal.maxText;
10808                 return;
10809             }
10810             if(ddays){
10811                 if(ddays.indexOf(d.getDay()) != -1){
10812                     cell.title = ddaysText;
10813                     cell.className = " x-date-disabled";
10814                 }
10815             }
10816             if(ddMatch && format){
10817                 var fvalue = d.dateFormat(format);
10818                 if(ddMatch.test(fvalue)){
10819                     cell.title = ddText.replace("%0", fvalue);
10820                     cell.className = " x-date-disabled";
10821                 }
10822             }
10823         };
10824
10825         var i = 0;
10826         for(; i < startingPos; i++) {
10827             textEls[i].innerHTML = (++prevStart);
10828             d.setDate(d.getDate()+1);
10829             cells[i].className = "x-date-prevday";
10830             setCellClass(this, cells[i]);
10831         }
10832         for(; i < days; i++){
10833             intDay = i - startingPos + 1;
10834             textEls[i].innerHTML = (intDay);
10835             d.setDate(d.getDate()+1);
10836             cells[i].className = "x-date-active";
10837             setCellClass(this, cells[i]);
10838         }
10839         var extraDays = 0;
10840         for(; i < 42; i++) {
10841              textEls[i].innerHTML = (++extraDays);
10842              d.setDate(d.getDate()+1);
10843              cells[i].className = "x-date-nextday";
10844              setCellClass(this, cells[i]);
10845         }
10846
10847         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10848         this.fireEvent('monthchange', this, date);
10849         
10850         if(!this.internalRender){
10851             var main = this.el.dom.firstChild;
10852             var w = main.offsetWidth;
10853             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10854             Roo.fly(main).setWidth(w);
10855             this.internalRender = true;
10856             // opera does not respect the auto grow header center column
10857             // then, after it gets a width opera refuses to recalculate
10858             // without a second pass
10859             if(Roo.isOpera && !this.secondPass){
10860                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10861                 this.secondPass = true;
10862                 this.update.defer(10, this, [date]);
10863             }
10864         }
10865         
10866         
10867     }
10868 });        /*
10869  * Based on:
10870  * Ext JS Library 1.1.1
10871  * Copyright(c) 2006-2007, Ext JS, LLC.
10872  *
10873  * Originally Released Under LGPL - original licence link has changed is not relivant.
10874  *
10875  * Fork - LGPL
10876  * <script type="text/javascript">
10877  */
10878 /**
10879  * @class Roo.TabPanel
10880  * @extends Roo.util.Observable
10881  * A lightweight tab container.
10882  * <br><br>
10883  * Usage:
10884  * <pre><code>
10885 // basic tabs 1, built from existing content
10886 var tabs = new Roo.TabPanel("tabs1");
10887 tabs.addTab("script", "View Script");
10888 tabs.addTab("markup", "View Markup");
10889 tabs.activate("script");
10890
10891 // more advanced tabs, built from javascript
10892 var jtabs = new Roo.TabPanel("jtabs");
10893 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10894
10895 // set up the UpdateManager
10896 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10897 var updater = tab2.getUpdateManager();
10898 updater.setDefaultUrl("ajax1.htm");
10899 tab2.on('activate', updater.refresh, updater, true);
10900
10901 // Use setUrl for Ajax loading
10902 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10903 tab3.setUrl("ajax2.htm", null, true);
10904
10905 // Disabled tab
10906 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10907 tab4.disable();
10908
10909 jtabs.activate("jtabs-1");
10910  * </code></pre>
10911  * @constructor
10912  * Create a new TabPanel.
10913  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10914  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10915  */
10916 Roo.TabPanel = function(container, config){
10917     /**
10918     * The container element for this TabPanel.
10919     * @type Roo.Element
10920     */
10921     this.el = Roo.get(container, true);
10922     if(config){
10923         if(typeof config == "boolean"){
10924             this.tabPosition = config ? "bottom" : "top";
10925         }else{
10926             Roo.apply(this, config);
10927         }
10928     }
10929     if(this.tabPosition == "bottom"){
10930         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10931         this.el.addClass("x-tabs-bottom");
10932     }
10933     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10934     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10935     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10936     if(Roo.isIE){
10937         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10938     }
10939     if(this.tabPosition != "bottom"){
10940         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10941          * @type Roo.Element
10942          */
10943         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10944         this.el.addClass("x-tabs-top");
10945     }
10946     this.items = [];
10947
10948     this.bodyEl.setStyle("position", "relative");
10949
10950     this.active = null;
10951     this.activateDelegate = this.activate.createDelegate(this);
10952
10953     this.addEvents({
10954         /**
10955          * @event tabchange
10956          * Fires when the active tab changes
10957          * @param {Roo.TabPanel} this
10958          * @param {Roo.TabPanelItem} activePanel The new active tab
10959          */
10960         "tabchange": true,
10961         /**
10962          * @event beforetabchange
10963          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10964          * @param {Roo.TabPanel} this
10965          * @param {Object} e Set cancel to true on this object to cancel the tab change
10966          * @param {Roo.TabPanelItem} tab The tab being changed to
10967          */
10968         "beforetabchange" : true
10969     });
10970
10971     Roo.EventManager.onWindowResize(this.onResize, this);
10972     this.cpad = this.el.getPadding("lr");
10973     this.hiddenCount = 0;
10974
10975
10976     // toolbar on the tabbar support...
10977     if (this.toolbar) {
10978         var tcfg = this.toolbar;
10979         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10980         this.toolbar = new Roo.Toolbar(tcfg);
10981         if (Roo.isSafari) {
10982             var tbl = tcfg.container.child('table', true);
10983             tbl.setAttribute('width', '100%');
10984         }
10985         
10986     }
10987    
10988
10989
10990     Roo.TabPanel.superclass.constructor.call(this);
10991 };
10992
10993 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10994     /*
10995      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10996      */
10997     tabPosition : "top",
10998     /*
10999      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11000      */
11001     currentTabWidth : 0,
11002     /*
11003      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11004      */
11005     minTabWidth : 40,
11006     /*
11007      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11008      */
11009     maxTabWidth : 250,
11010     /*
11011      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11012      */
11013     preferredTabWidth : 175,
11014     /*
11015      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11016      */
11017     resizeTabs : false,
11018     /*
11019      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11020      */
11021     monitorResize : true,
11022     /*
11023      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11024      */
11025     toolbar : false,
11026
11027     /**
11028      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11029      * @param {String} id The id of the div to use <b>or create</b>
11030      * @param {String} text The text for the tab
11031      * @param {String} content (optional) Content to put in the TabPanelItem body
11032      * @param {Boolean} closable (optional) True to create a close icon on the tab
11033      * @return {Roo.TabPanelItem} The created TabPanelItem
11034      */
11035     addTab : function(id, text, content, closable){
11036         var item = new Roo.TabPanelItem(this, id, text, closable);
11037         this.addTabItem(item);
11038         if(content){
11039             item.setContent(content);
11040         }
11041         return item;
11042     },
11043
11044     /**
11045      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11046      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11047      * @return {Roo.TabPanelItem}
11048      */
11049     getTab : function(id){
11050         return this.items[id];
11051     },
11052
11053     /**
11054      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11055      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11056      */
11057     hideTab : function(id){
11058         var t = this.items[id];
11059         if(!t.isHidden()){
11060            t.setHidden(true);
11061            this.hiddenCount++;
11062            this.autoSizeTabs();
11063         }
11064     },
11065
11066     /**
11067      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11068      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11069      */
11070     unhideTab : function(id){
11071         var t = this.items[id];
11072         if(t.isHidden()){
11073            t.setHidden(false);
11074            this.hiddenCount--;
11075            this.autoSizeTabs();
11076         }
11077     },
11078
11079     /**
11080      * Adds an existing {@link Roo.TabPanelItem}.
11081      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11082      */
11083     addTabItem : function(item){
11084         this.items[item.id] = item;
11085         this.items.push(item);
11086         if(this.resizeTabs){
11087            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11088            this.autoSizeTabs();
11089         }else{
11090             item.autoSize();
11091         }
11092     },
11093
11094     /**
11095      * Removes a {@link Roo.TabPanelItem}.
11096      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11097      */
11098     removeTab : function(id){
11099         var items = this.items;
11100         var tab = items[id];
11101         if(!tab) { return; }
11102         var index = items.indexOf(tab);
11103         if(this.active == tab && items.length > 1){
11104             var newTab = this.getNextAvailable(index);
11105             if(newTab) {
11106                 newTab.activate();
11107             }
11108         }
11109         this.stripEl.dom.removeChild(tab.pnode.dom);
11110         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11111             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11112         }
11113         items.splice(index, 1);
11114         delete this.items[tab.id];
11115         tab.fireEvent("close", tab);
11116         tab.purgeListeners();
11117         this.autoSizeTabs();
11118     },
11119
11120     getNextAvailable : function(start){
11121         var items = this.items;
11122         var index = start;
11123         // look for a next tab that will slide over to
11124         // replace the one being removed
11125         while(index < items.length){
11126             var item = items[++index];
11127             if(item && !item.isHidden()){
11128                 return item;
11129             }
11130         }
11131         // if one isn't found select the previous tab (on the left)
11132         index = start;
11133         while(index >= 0){
11134             var item = items[--index];
11135             if(item && !item.isHidden()){
11136                 return item;
11137             }
11138         }
11139         return null;
11140     },
11141
11142     /**
11143      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11144      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11145      */
11146     disableTab : function(id){
11147         var tab = this.items[id];
11148         if(tab && this.active != tab){
11149             tab.disable();
11150         }
11151     },
11152
11153     /**
11154      * Enables a {@link Roo.TabPanelItem} that is disabled.
11155      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11156      */
11157     enableTab : function(id){
11158         var tab = this.items[id];
11159         tab.enable();
11160     },
11161
11162     /**
11163      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11164      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11165      * @return {Roo.TabPanelItem} The TabPanelItem.
11166      */
11167     activate : function(id){
11168         var tab = this.items[id];
11169         if(!tab){
11170             return null;
11171         }
11172         if(tab == this.active || tab.disabled){
11173             return tab;
11174         }
11175         var e = {};
11176         this.fireEvent("beforetabchange", this, e, tab);
11177         if(e.cancel !== true && !tab.disabled){
11178             if(this.active){
11179                 this.active.hide();
11180             }
11181             this.active = this.items[id];
11182             this.active.show();
11183             this.fireEvent("tabchange", this, this.active);
11184         }
11185         return tab;
11186     },
11187
11188     /**
11189      * Gets the active {@link Roo.TabPanelItem}.
11190      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11191      */
11192     getActiveTab : function(){
11193         return this.active;
11194     },
11195
11196     /**
11197      * Updates the tab body element to fit the height of the container element
11198      * for overflow scrolling
11199      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11200      */
11201     syncHeight : function(targetHeight){
11202         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11203         var bm = this.bodyEl.getMargins();
11204         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11205         this.bodyEl.setHeight(newHeight);
11206         return newHeight;
11207     },
11208
11209     onResize : function(){
11210         if(this.monitorResize){
11211             this.autoSizeTabs();
11212         }
11213     },
11214
11215     /**
11216      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11217      */
11218     beginUpdate : function(){
11219         this.updating = true;
11220     },
11221
11222     /**
11223      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11224      */
11225     endUpdate : function(){
11226         this.updating = false;
11227         this.autoSizeTabs();
11228     },
11229
11230     /**
11231      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11232      */
11233     autoSizeTabs : function(){
11234         var count = this.items.length;
11235         var vcount = count - this.hiddenCount;
11236         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11237         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11238         var availWidth = Math.floor(w / vcount);
11239         var b = this.stripBody;
11240         if(b.getWidth() > w){
11241             var tabs = this.items;
11242             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11243             if(availWidth < this.minTabWidth){
11244                 /*if(!this.sleft){    // incomplete scrolling code
11245                     this.createScrollButtons();
11246                 }
11247                 this.showScroll();
11248                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11249             }
11250         }else{
11251             if(this.currentTabWidth < this.preferredTabWidth){
11252                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11253             }
11254         }
11255     },
11256
11257     /**
11258      * Returns the number of tabs in this TabPanel.
11259      * @return {Number}
11260      */
11261      getCount : function(){
11262          return this.items.length;
11263      },
11264
11265     /**
11266      * Resizes all the tabs to the passed width
11267      * @param {Number} The new width
11268      */
11269     setTabWidth : function(width){
11270         this.currentTabWidth = width;
11271         for(var i = 0, len = this.items.length; i < len; i++) {
11272                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11273         }
11274     },
11275
11276     /**
11277      * Destroys this TabPanel
11278      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11279      */
11280     destroy : function(removeEl){
11281         Roo.EventManager.removeResizeListener(this.onResize, this);
11282         for(var i = 0, len = this.items.length; i < len; i++){
11283             this.items[i].purgeListeners();
11284         }
11285         if(removeEl === true){
11286             this.el.update("");
11287             this.el.remove();
11288         }
11289     }
11290 });
11291
11292 /**
11293  * @class Roo.TabPanelItem
11294  * @extends Roo.util.Observable
11295  * Represents an individual item (tab plus body) in a TabPanel.
11296  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11297  * @param {String} id The id of this TabPanelItem
11298  * @param {String} text The text for the tab of this TabPanelItem
11299  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11300  */
11301 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11302     /**
11303      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11304      * @type Roo.TabPanel
11305      */
11306     this.tabPanel = tabPanel;
11307     /**
11308      * The id for this TabPanelItem
11309      * @type String
11310      */
11311     this.id = id;
11312     /** @private */
11313     this.disabled = false;
11314     /** @private */
11315     this.text = text;
11316     /** @private */
11317     this.loaded = false;
11318     this.closable = closable;
11319
11320     /**
11321      * The body element for this TabPanelItem.
11322      * @type Roo.Element
11323      */
11324     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11325     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11326     this.bodyEl.setStyle("display", "block");
11327     this.bodyEl.setStyle("zoom", "1");
11328     this.hideAction();
11329
11330     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11331     /** @private */
11332     this.el = Roo.get(els.el, true);
11333     this.inner = Roo.get(els.inner, true);
11334     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11335     this.pnode = Roo.get(els.el.parentNode, true);
11336     this.el.on("mousedown", this.onTabMouseDown, this);
11337     this.el.on("click", this.onTabClick, this);
11338     /** @private */
11339     if(closable){
11340         var c = Roo.get(els.close, true);
11341         c.dom.title = this.closeText;
11342         c.addClassOnOver("close-over");
11343         c.on("click", this.closeClick, this);
11344      }
11345
11346     this.addEvents({
11347          /**
11348          * @event activate
11349          * Fires when this tab becomes the active tab.
11350          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11351          * @param {Roo.TabPanelItem} this
11352          */
11353         "activate": true,
11354         /**
11355          * @event beforeclose
11356          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11357          * @param {Roo.TabPanelItem} this
11358          * @param {Object} e Set cancel to true on this object to cancel the close.
11359          */
11360         "beforeclose": true,
11361         /**
11362          * @event close
11363          * Fires when this tab is closed.
11364          * @param {Roo.TabPanelItem} this
11365          */
11366          "close": true,
11367         /**
11368          * @event deactivate
11369          * Fires when this tab is no longer the active tab.
11370          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11371          * @param {Roo.TabPanelItem} this
11372          */
11373          "deactivate" : true
11374     });
11375     this.hidden = false;
11376
11377     Roo.TabPanelItem.superclass.constructor.call(this);
11378 };
11379
11380 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11381     purgeListeners : function(){
11382        Roo.util.Observable.prototype.purgeListeners.call(this);
11383        this.el.removeAllListeners();
11384     },
11385     /**
11386      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11387      */
11388     show : function(){
11389         this.pnode.addClass("on");
11390         this.showAction();
11391         if(Roo.isOpera){
11392             this.tabPanel.stripWrap.repaint();
11393         }
11394         this.fireEvent("activate", this.tabPanel, this);
11395     },
11396
11397     /**
11398      * Returns true if this tab is the active tab.
11399      * @return {Boolean}
11400      */
11401     isActive : function(){
11402         return this.tabPanel.getActiveTab() == this;
11403     },
11404
11405     /**
11406      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11407      */
11408     hide : function(){
11409         this.pnode.removeClass("on");
11410         this.hideAction();
11411         this.fireEvent("deactivate", this.tabPanel, this);
11412     },
11413
11414     hideAction : function(){
11415         this.bodyEl.hide();
11416         this.bodyEl.setStyle("position", "absolute");
11417         this.bodyEl.setLeft("-20000px");
11418         this.bodyEl.setTop("-20000px");
11419     },
11420
11421     showAction : function(){
11422         this.bodyEl.setStyle("position", "relative");
11423         this.bodyEl.setTop("");
11424         this.bodyEl.setLeft("");
11425         this.bodyEl.show();
11426     },
11427
11428     /**
11429      * Set the tooltip for the tab.
11430      * @param {String} tooltip The tab's tooltip
11431      */
11432     setTooltip : function(text){
11433         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11434             this.textEl.dom.qtip = text;
11435             this.textEl.dom.removeAttribute('title');
11436         }else{
11437             this.textEl.dom.title = text;
11438         }
11439     },
11440
11441     onTabClick : function(e){
11442         e.preventDefault();
11443         this.tabPanel.activate(this.id);
11444     },
11445
11446     onTabMouseDown : function(e){
11447         e.preventDefault();
11448         this.tabPanel.activate(this.id);
11449     },
11450
11451     getWidth : function(){
11452         return this.inner.getWidth();
11453     },
11454
11455     setWidth : function(width){
11456         var iwidth = width - this.pnode.getPadding("lr");
11457         this.inner.setWidth(iwidth);
11458         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11459         this.pnode.setWidth(width);
11460     },
11461
11462     /**
11463      * Show or hide the tab
11464      * @param {Boolean} hidden True to hide or false to show.
11465      */
11466     setHidden : function(hidden){
11467         this.hidden = hidden;
11468         this.pnode.setStyle("display", hidden ? "none" : "");
11469     },
11470
11471     /**
11472      * Returns true if this tab is "hidden"
11473      * @return {Boolean}
11474      */
11475     isHidden : function(){
11476         return this.hidden;
11477     },
11478
11479     /**
11480      * Returns the text for this tab
11481      * @return {String}
11482      */
11483     getText : function(){
11484         return this.text;
11485     },
11486
11487     autoSize : function(){
11488         //this.el.beginMeasure();
11489         this.textEl.setWidth(1);
11490         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11491         //this.el.endMeasure();
11492     },
11493
11494     /**
11495      * Sets the text for the tab (Note: this also sets the tooltip text)
11496      * @param {String} text The tab's text and tooltip
11497      */
11498     setText : function(text){
11499         this.text = text;
11500         this.textEl.update(text);
11501         this.setTooltip(text);
11502         if(!this.tabPanel.resizeTabs){
11503             this.autoSize();
11504         }
11505     },
11506     /**
11507      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11508      */
11509     activate : function(){
11510         this.tabPanel.activate(this.id);
11511     },
11512
11513     /**
11514      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11515      */
11516     disable : function(){
11517         if(this.tabPanel.active != this){
11518             this.disabled = true;
11519             this.pnode.addClass("disabled");
11520         }
11521     },
11522
11523     /**
11524      * Enables this TabPanelItem if it was previously disabled.
11525      */
11526     enable : function(){
11527         this.disabled = false;
11528         this.pnode.removeClass("disabled");
11529     },
11530
11531     /**
11532      * Sets the content for this TabPanelItem.
11533      * @param {String} content The content
11534      * @param {Boolean} loadScripts true to look for and load scripts
11535      */
11536     setContent : function(content, loadScripts){
11537         this.bodyEl.update(content, loadScripts);
11538     },
11539
11540     /**
11541      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11542      * @return {Roo.UpdateManager} The UpdateManager
11543      */
11544     getUpdateManager : function(){
11545         return this.bodyEl.getUpdateManager();
11546     },
11547
11548     /**
11549      * Set a URL to be used to load the content for this TabPanelItem.
11550      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11551      * @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)
11552      * @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)
11553      * @return {Roo.UpdateManager} The UpdateManager
11554      */
11555     setUrl : function(url, params, loadOnce){
11556         if(this.refreshDelegate){
11557             this.un('activate', this.refreshDelegate);
11558         }
11559         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11560         this.on("activate", this.refreshDelegate);
11561         return this.bodyEl.getUpdateManager();
11562     },
11563
11564     /** @private */
11565     _handleRefresh : function(url, params, loadOnce){
11566         if(!loadOnce || !this.loaded){
11567             var updater = this.bodyEl.getUpdateManager();
11568             updater.update(url, params, this._setLoaded.createDelegate(this));
11569         }
11570     },
11571
11572     /**
11573      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11574      *   Will fail silently if the setUrl method has not been called.
11575      *   This does not activate the panel, just updates its content.
11576      */
11577     refresh : function(){
11578         if(this.refreshDelegate){
11579            this.loaded = false;
11580            this.refreshDelegate();
11581         }
11582     },
11583
11584     /** @private */
11585     _setLoaded : function(){
11586         this.loaded = true;
11587     },
11588
11589     /** @private */
11590     closeClick : function(e){
11591         var o = {};
11592         e.stopEvent();
11593         this.fireEvent("beforeclose", this, o);
11594         if(o.cancel !== true){
11595             this.tabPanel.removeTab(this.id);
11596         }
11597     },
11598     /**
11599      * The text displayed in the tooltip for the close icon.
11600      * @type String
11601      */
11602     closeText : "Close this tab"
11603 });
11604
11605 /** @private */
11606 Roo.TabPanel.prototype.createStrip = function(container){
11607     var strip = document.createElement("div");
11608     strip.className = "x-tabs-wrap";
11609     container.appendChild(strip);
11610     return strip;
11611 };
11612 /** @private */
11613 Roo.TabPanel.prototype.createStripList = function(strip){
11614     // div wrapper for retard IE
11615     // returns the "tr" element.
11616     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11617         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11618         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11619     return strip.firstChild.firstChild.firstChild.firstChild;
11620 };
11621 /** @private */
11622 Roo.TabPanel.prototype.createBody = function(container){
11623     var body = document.createElement("div");
11624     Roo.id(body, "tab-body");
11625     Roo.fly(body).addClass("x-tabs-body");
11626     container.appendChild(body);
11627     return body;
11628 };
11629 /** @private */
11630 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11631     var body = Roo.getDom(id);
11632     if(!body){
11633         body = document.createElement("div");
11634         body.id = id;
11635     }
11636     Roo.fly(body).addClass("x-tabs-item-body");
11637     bodyEl.insertBefore(body, bodyEl.firstChild);
11638     return body;
11639 };
11640 /** @private */
11641 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11642     var td = document.createElement("td");
11643     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11644     //stripEl.appendChild(td);
11645     if(closable){
11646         td.className = "x-tabs-closable";
11647         if(!this.closeTpl){
11648             this.closeTpl = new Roo.Template(
11649                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11650                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11651                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11652             );
11653         }
11654         var el = this.closeTpl.overwrite(td, {"text": text});
11655         var close = el.getElementsByTagName("div")[0];
11656         var inner = el.getElementsByTagName("em")[0];
11657         return {"el": el, "close": close, "inner": inner};
11658     } else {
11659         if(!this.tabTpl){
11660             this.tabTpl = new Roo.Template(
11661                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11662                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11663             );
11664         }
11665         var el = this.tabTpl.overwrite(td, {"text": text});
11666         var inner = el.getElementsByTagName("em")[0];
11667         return {"el": el, "inner": inner};
11668     }
11669 };/*
11670  * Based on:
11671  * Ext JS Library 1.1.1
11672  * Copyright(c) 2006-2007, Ext JS, LLC.
11673  *
11674  * Originally Released Under LGPL - original licence link has changed is not relivant.
11675  *
11676  * Fork - LGPL
11677  * <script type="text/javascript">
11678  */
11679
11680 /**
11681  * @class Roo.Button
11682  * @extends Roo.util.Observable
11683  * Simple Button class
11684  * @cfg {String} text The button text
11685  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11686  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11687  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11688  * @cfg {Object} scope The scope of the handler
11689  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11690  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11691  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11692  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11693  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11694  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11695    applies if enableToggle = true)
11696  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11697  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11698   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11699  * @constructor
11700  * Create a new button
11701  * @param {Object} config The config object
11702  */
11703 Roo.Button = function(renderTo, config)
11704 {
11705     if (!config) {
11706         config = renderTo;
11707         renderTo = config.renderTo || false;
11708     }
11709     
11710     Roo.apply(this, config);
11711     this.addEvents({
11712         /**
11713              * @event click
11714              * Fires when this button is clicked
11715              * @param {Button} this
11716              * @param {EventObject} e The click event
11717              */
11718             "click" : true,
11719         /**
11720              * @event toggle
11721              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11722              * @param {Button} this
11723              * @param {Boolean} pressed
11724              */
11725             "toggle" : true,
11726         /**
11727              * @event mouseover
11728              * Fires when the mouse hovers over the button
11729              * @param {Button} this
11730              * @param {Event} e The event object
11731              */
11732         'mouseover' : true,
11733         /**
11734              * @event mouseout
11735              * Fires when the mouse exits the button
11736              * @param {Button} this
11737              * @param {Event} e The event object
11738              */
11739         'mouseout': true,
11740          /**
11741              * @event render
11742              * Fires when the button is rendered
11743              * @param {Button} this
11744              */
11745         'render': true
11746     });
11747     if(this.menu){
11748         this.menu = Roo.menu.MenuMgr.get(this.menu);
11749     }
11750     // register listeners first!!  - so render can be captured..
11751     Roo.util.Observable.call(this);
11752     if(renderTo){
11753         this.render(renderTo);
11754     }
11755     
11756   
11757 };
11758
11759 Roo.extend(Roo.Button, Roo.util.Observable, {
11760     /**
11761      * 
11762      */
11763     
11764     /**
11765      * Read-only. True if this button is hidden
11766      * @type Boolean
11767      */
11768     hidden : false,
11769     /**
11770      * Read-only. True if this button is disabled
11771      * @type Boolean
11772      */
11773     disabled : false,
11774     /**
11775      * Read-only. True if this button is pressed (only if enableToggle = true)
11776      * @type Boolean
11777      */
11778     pressed : false,
11779
11780     /**
11781      * @cfg {Number} tabIndex 
11782      * The DOM tabIndex for this button (defaults to undefined)
11783      */
11784     tabIndex : undefined,
11785
11786     /**
11787      * @cfg {Boolean} enableToggle
11788      * True to enable pressed/not pressed toggling (defaults to false)
11789      */
11790     enableToggle: false,
11791     /**
11792      * @cfg {Mixed} menu
11793      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11794      */
11795     menu : undefined,
11796     /**
11797      * @cfg {String} menuAlign
11798      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11799      */
11800     menuAlign : "tl-bl?",
11801
11802     /**
11803      * @cfg {String} iconCls
11804      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11805      */
11806     iconCls : undefined,
11807     /**
11808      * @cfg {String} type
11809      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11810      */
11811     type : 'button',
11812
11813     // private
11814     menuClassTarget: 'tr',
11815
11816     /**
11817      * @cfg {String} clickEvent
11818      * The type of event to map to the button's event handler (defaults to 'click')
11819      */
11820     clickEvent : 'click',
11821
11822     /**
11823      * @cfg {Boolean} handleMouseEvents
11824      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11825      */
11826     handleMouseEvents : true,
11827
11828     /**
11829      * @cfg {String} tooltipType
11830      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11831      */
11832     tooltipType : 'qtip',
11833
11834     /**
11835      * @cfg {String} cls
11836      * A CSS class to apply to the button's main element.
11837      */
11838     
11839     /**
11840      * @cfg {Roo.Template} template (Optional)
11841      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11842      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11843      * require code modifications if required elements (e.g. a button) aren't present.
11844      */
11845
11846     // private
11847     render : function(renderTo){
11848         var btn;
11849         if(this.hideParent){
11850             this.parentEl = Roo.get(renderTo);
11851         }
11852         if(!this.dhconfig){
11853             if(!this.template){
11854                 if(!Roo.Button.buttonTemplate){
11855                     // hideous table template
11856                     Roo.Button.buttonTemplate = new Roo.Template(
11857                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11858                         '<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>',
11859                         "</tr></tbody></table>");
11860                 }
11861                 this.template = Roo.Button.buttonTemplate;
11862             }
11863             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11864             var btnEl = btn.child("button:first");
11865             btnEl.on('focus', this.onFocus, this);
11866             btnEl.on('blur', this.onBlur, this);
11867             if(this.cls){
11868                 btn.addClass(this.cls);
11869             }
11870             if(this.icon){
11871                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11872             }
11873             if(this.iconCls){
11874                 btnEl.addClass(this.iconCls);
11875                 if(!this.cls){
11876                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11877                 }
11878             }
11879             if(this.tabIndex !== undefined){
11880                 btnEl.dom.tabIndex = this.tabIndex;
11881             }
11882             if(this.tooltip){
11883                 if(typeof this.tooltip == 'object'){
11884                     Roo.QuickTips.tips(Roo.apply({
11885                           target: btnEl.id
11886                     }, this.tooltip));
11887                 } else {
11888                     btnEl.dom[this.tooltipType] = this.tooltip;
11889                 }
11890             }
11891         }else{
11892             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11893         }
11894         this.el = btn;
11895         if(this.id){
11896             this.el.dom.id = this.el.id = this.id;
11897         }
11898         if(this.menu){
11899             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11900             this.menu.on("show", this.onMenuShow, this);
11901             this.menu.on("hide", this.onMenuHide, this);
11902         }
11903         btn.addClass("x-btn");
11904         if(Roo.isIE && !Roo.isIE7){
11905             this.autoWidth.defer(1, this);
11906         }else{
11907             this.autoWidth();
11908         }
11909         if(this.handleMouseEvents){
11910             btn.on("mouseover", this.onMouseOver, this);
11911             btn.on("mouseout", this.onMouseOut, this);
11912             btn.on("mousedown", this.onMouseDown, this);
11913         }
11914         btn.on(this.clickEvent, this.onClick, this);
11915         //btn.on("mouseup", this.onMouseUp, this);
11916         if(this.hidden){
11917             this.hide();
11918         }
11919         if(this.disabled){
11920             this.disable();
11921         }
11922         Roo.ButtonToggleMgr.register(this);
11923         if(this.pressed){
11924             this.el.addClass("x-btn-pressed");
11925         }
11926         if(this.repeat){
11927             var repeater = new Roo.util.ClickRepeater(btn,
11928                 typeof this.repeat == "object" ? this.repeat : {}
11929             );
11930             repeater.on("click", this.onClick,  this);
11931         }
11932         
11933         this.fireEvent('render', this);
11934         
11935     },
11936     /**
11937      * Returns the button's underlying element
11938      * @return {Roo.Element} The element
11939      */
11940     getEl : function(){
11941         return this.el;  
11942     },
11943     
11944     /**
11945      * Destroys this Button and removes any listeners.
11946      */
11947     destroy : function(){
11948         Roo.ButtonToggleMgr.unregister(this);
11949         this.el.removeAllListeners();
11950         this.purgeListeners();
11951         this.el.remove();
11952     },
11953
11954     // private
11955     autoWidth : function(){
11956         if(this.el){
11957             this.el.setWidth("auto");
11958             if(Roo.isIE7 && Roo.isStrict){
11959                 var ib = this.el.child('button');
11960                 if(ib && ib.getWidth() > 20){
11961                     ib.clip();
11962                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11963                 }
11964             }
11965             if(this.minWidth){
11966                 if(this.hidden){
11967                     this.el.beginMeasure();
11968                 }
11969                 if(this.el.getWidth() < this.minWidth){
11970                     this.el.setWidth(this.minWidth);
11971                 }
11972                 if(this.hidden){
11973                     this.el.endMeasure();
11974                 }
11975             }
11976         }
11977     },
11978
11979     /**
11980      * Assigns this button's click handler
11981      * @param {Function} handler The function to call when the button is clicked
11982      * @param {Object} scope (optional) Scope for the function passed in
11983      */
11984     setHandler : function(handler, scope){
11985         this.handler = handler;
11986         this.scope = scope;  
11987     },
11988     
11989     /**
11990      * Sets this button's text
11991      * @param {String} text The button text
11992      */
11993     setText : function(text){
11994         this.text = text;
11995         if(this.el){
11996             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11997         }
11998         this.autoWidth();
11999     },
12000     
12001     /**
12002      * Gets the text for this button
12003      * @return {String} The button text
12004      */
12005     getText : function(){
12006         return this.text;  
12007     },
12008     
12009     /**
12010      * Show this button
12011      */
12012     show: function(){
12013         this.hidden = false;
12014         if(this.el){
12015             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12016         }
12017     },
12018     
12019     /**
12020      * Hide this button
12021      */
12022     hide: function(){
12023         this.hidden = true;
12024         if(this.el){
12025             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12026         }
12027     },
12028     
12029     /**
12030      * Convenience function for boolean show/hide
12031      * @param {Boolean} visible True to show, false to hide
12032      */
12033     setVisible: function(visible){
12034         if(visible) {
12035             this.show();
12036         }else{
12037             this.hide();
12038         }
12039     },
12040     
12041     /**
12042      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12043      * @param {Boolean} state (optional) Force a particular state
12044      */
12045     toggle : function(state){
12046         state = state === undefined ? !this.pressed : state;
12047         if(state != this.pressed){
12048             if(state){
12049                 this.el.addClass("x-btn-pressed");
12050                 this.pressed = true;
12051                 this.fireEvent("toggle", this, true);
12052             }else{
12053                 this.el.removeClass("x-btn-pressed");
12054                 this.pressed = false;
12055                 this.fireEvent("toggle", this, false);
12056             }
12057             if(this.toggleHandler){
12058                 this.toggleHandler.call(this.scope || this, this, state);
12059             }
12060         }
12061     },
12062     
12063     /**
12064      * Focus the button
12065      */
12066     focus : function(){
12067         this.el.child('button:first').focus();
12068     },
12069     
12070     /**
12071      * Disable this button
12072      */
12073     disable : function(){
12074         if(this.el){
12075             this.el.addClass("x-btn-disabled");
12076         }
12077         this.disabled = true;
12078     },
12079     
12080     /**
12081      * Enable this button
12082      */
12083     enable : function(){
12084         if(this.el){
12085             this.el.removeClass("x-btn-disabled");
12086         }
12087         this.disabled = false;
12088     },
12089
12090     /**
12091      * Convenience function for boolean enable/disable
12092      * @param {Boolean} enabled True to enable, false to disable
12093      */
12094     setDisabled : function(v){
12095         this[v !== true ? "enable" : "disable"]();
12096     },
12097
12098     // private
12099     onClick : function(e){
12100         if(e){
12101             e.preventDefault();
12102         }
12103         if(e.button != 0){
12104             return;
12105         }
12106         if(!this.disabled){
12107             if(this.enableToggle){
12108                 this.toggle();
12109             }
12110             if(this.menu && !this.menu.isVisible()){
12111                 this.menu.show(this.el, this.menuAlign);
12112             }
12113             this.fireEvent("click", this, e);
12114             if(this.handler){
12115                 this.el.removeClass("x-btn-over");
12116                 this.handler.call(this.scope || this, this, e);
12117             }
12118         }
12119     },
12120     // private
12121     onMouseOver : function(e){
12122         if(!this.disabled){
12123             this.el.addClass("x-btn-over");
12124             this.fireEvent('mouseover', this, e);
12125         }
12126     },
12127     // private
12128     onMouseOut : function(e){
12129         if(!e.within(this.el,  true)){
12130             this.el.removeClass("x-btn-over");
12131             this.fireEvent('mouseout', this, e);
12132         }
12133     },
12134     // private
12135     onFocus : function(e){
12136         if(!this.disabled){
12137             this.el.addClass("x-btn-focus");
12138         }
12139     },
12140     // private
12141     onBlur : function(e){
12142         this.el.removeClass("x-btn-focus");
12143     },
12144     // private
12145     onMouseDown : function(e){
12146         if(!this.disabled && e.button == 0){
12147             this.el.addClass("x-btn-click");
12148             Roo.get(document).on('mouseup', this.onMouseUp, this);
12149         }
12150     },
12151     // private
12152     onMouseUp : function(e){
12153         if(e.button == 0){
12154             this.el.removeClass("x-btn-click");
12155             Roo.get(document).un('mouseup', this.onMouseUp, this);
12156         }
12157     },
12158     // private
12159     onMenuShow : function(e){
12160         this.el.addClass("x-btn-menu-active");
12161     },
12162     // private
12163     onMenuHide : function(e){
12164         this.el.removeClass("x-btn-menu-active");
12165     }   
12166 });
12167
12168 // Private utility class used by Button
12169 Roo.ButtonToggleMgr = function(){
12170    var groups = {};
12171    
12172    function toggleGroup(btn, state){
12173        if(state){
12174            var g = groups[btn.toggleGroup];
12175            for(var i = 0, l = g.length; i < l; i++){
12176                if(g[i] != btn){
12177                    g[i].toggle(false);
12178                }
12179            }
12180        }
12181    }
12182    
12183    return {
12184        register : function(btn){
12185            if(!btn.toggleGroup){
12186                return;
12187            }
12188            var g = groups[btn.toggleGroup];
12189            if(!g){
12190                g = groups[btn.toggleGroup] = [];
12191            }
12192            g.push(btn);
12193            btn.on("toggle", toggleGroup);
12194        },
12195        
12196        unregister : function(btn){
12197            if(!btn.toggleGroup){
12198                return;
12199            }
12200            var g = groups[btn.toggleGroup];
12201            if(g){
12202                g.remove(btn);
12203                btn.un("toggle", toggleGroup);
12204            }
12205        }
12206    };
12207 }();/*
12208  * Based on:
12209  * Ext JS Library 1.1.1
12210  * Copyright(c) 2006-2007, Ext JS, LLC.
12211  *
12212  * Originally Released Under LGPL - original licence link has changed is not relivant.
12213  *
12214  * Fork - LGPL
12215  * <script type="text/javascript">
12216  */
12217  
12218 /**
12219  * @class Roo.SplitButton
12220  * @extends Roo.Button
12221  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12222  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12223  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12224  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12225  * @cfg {String} arrowTooltip The title attribute of the arrow
12226  * @constructor
12227  * Create a new menu button
12228  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12229  * @param {Object} config The config object
12230  */
12231 Roo.SplitButton = function(renderTo, config){
12232     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12233     /**
12234      * @event arrowclick
12235      * Fires when this button's arrow is clicked
12236      * @param {SplitButton} this
12237      * @param {EventObject} e The click event
12238      */
12239     this.addEvents({"arrowclick":true});
12240 };
12241
12242 Roo.extend(Roo.SplitButton, Roo.Button, {
12243     render : function(renderTo){
12244         // this is one sweet looking template!
12245         var tpl = new Roo.Template(
12246             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12247             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12248             '<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>',
12249             "</tbody></table></td><td>",
12250             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12251             '<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>',
12252             "</tbody></table></td></tr></table>"
12253         );
12254         var btn = tpl.append(renderTo, [this.text, this.type], true);
12255         var btnEl = btn.child("button");
12256         if(this.cls){
12257             btn.addClass(this.cls);
12258         }
12259         if(this.icon){
12260             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12261         }
12262         if(this.iconCls){
12263             btnEl.addClass(this.iconCls);
12264             if(!this.cls){
12265                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12266             }
12267         }
12268         this.el = btn;
12269         if(this.handleMouseEvents){
12270             btn.on("mouseover", this.onMouseOver, this);
12271             btn.on("mouseout", this.onMouseOut, this);
12272             btn.on("mousedown", this.onMouseDown, this);
12273             btn.on("mouseup", this.onMouseUp, this);
12274         }
12275         btn.on(this.clickEvent, this.onClick, this);
12276         if(this.tooltip){
12277             if(typeof this.tooltip == 'object'){
12278                 Roo.QuickTips.tips(Roo.apply({
12279                       target: btnEl.id
12280                 }, this.tooltip));
12281             } else {
12282                 btnEl.dom[this.tooltipType] = this.tooltip;
12283             }
12284         }
12285         if(this.arrowTooltip){
12286             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12287         }
12288         if(this.hidden){
12289             this.hide();
12290         }
12291         if(this.disabled){
12292             this.disable();
12293         }
12294         if(this.pressed){
12295             this.el.addClass("x-btn-pressed");
12296         }
12297         if(Roo.isIE && !Roo.isIE7){
12298             this.autoWidth.defer(1, this);
12299         }else{
12300             this.autoWidth();
12301         }
12302         if(this.menu){
12303             this.menu.on("show", this.onMenuShow, this);
12304             this.menu.on("hide", this.onMenuHide, this);
12305         }
12306         this.fireEvent('render', this);
12307     },
12308
12309     // private
12310     autoWidth : function(){
12311         if(this.el){
12312             var tbl = this.el.child("table:first");
12313             var tbl2 = this.el.child("table:last");
12314             this.el.setWidth("auto");
12315             tbl.setWidth("auto");
12316             if(Roo.isIE7 && Roo.isStrict){
12317                 var ib = this.el.child('button:first');
12318                 if(ib && ib.getWidth() > 20){
12319                     ib.clip();
12320                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12321                 }
12322             }
12323             if(this.minWidth){
12324                 if(this.hidden){
12325                     this.el.beginMeasure();
12326                 }
12327                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12328                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12329                 }
12330                 if(this.hidden){
12331                     this.el.endMeasure();
12332                 }
12333             }
12334             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12335         } 
12336     },
12337     /**
12338      * Sets this button's click handler
12339      * @param {Function} handler The function to call when the button is clicked
12340      * @param {Object} scope (optional) Scope for the function passed above
12341      */
12342     setHandler : function(handler, scope){
12343         this.handler = handler;
12344         this.scope = scope;  
12345     },
12346     
12347     /**
12348      * Sets this button's arrow click handler
12349      * @param {Function} handler The function to call when the arrow is clicked
12350      * @param {Object} scope (optional) Scope for the function passed above
12351      */
12352     setArrowHandler : function(handler, scope){
12353         this.arrowHandler = handler;
12354         this.scope = scope;  
12355     },
12356     
12357     /**
12358      * Focus the button
12359      */
12360     focus : function(){
12361         if(this.el){
12362             this.el.child("button:first").focus();
12363         }
12364     },
12365
12366     // private
12367     onClick : function(e){
12368         e.preventDefault();
12369         if(!this.disabled){
12370             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12371                 if(this.menu && !this.menu.isVisible()){
12372                     this.menu.show(this.el, this.menuAlign);
12373                 }
12374                 this.fireEvent("arrowclick", this, e);
12375                 if(this.arrowHandler){
12376                     this.arrowHandler.call(this.scope || this, this, e);
12377                 }
12378             }else{
12379                 this.fireEvent("click", this, e);
12380                 if(this.handler){
12381                     this.handler.call(this.scope || this, this, e);
12382                 }
12383             }
12384         }
12385     },
12386     // private
12387     onMouseDown : function(e){
12388         if(!this.disabled){
12389             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12390         }
12391     },
12392     // private
12393     onMouseUp : function(e){
12394         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12395     }   
12396 });
12397
12398
12399 // backwards compat
12400 Roo.MenuButton = Roo.SplitButton;/*
12401  * Based on:
12402  * Ext JS Library 1.1.1
12403  * Copyright(c) 2006-2007, Ext JS, LLC.
12404  *
12405  * Originally Released Under LGPL - original licence link has changed is not relivant.
12406  *
12407  * Fork - LGPL
12408  * <script type="text/javascript">
12409  */
12410
12411 /**
12412  * @class Roo.Toolbar
12413  * Basic Toolbar class.
12414  * @constructor
12415  * Creates a new Toolbar
12416  * @param {Object} container The config object
12417  */ 
12418 Roo.Toolbar = function(container, buttons, config)
12419 {
12420     /// old consturctor format still supported..
12421     if(container instanceof Array){ // omit the container for later rendering
12422         buttons = container;
12423         config = buttons;
12424         container = null;
12425     }
12426     if (typeof(container) == 'object' && container.xtype) {
12427         config = container;
12428         container = config.container;
12429         buttons = config.buttons || []; // not really - use items!!
12430     }
12431     var xitems = [];
12432     if (config && config.items) {
12433         xitems = config.items;
12434         delete config.items;
12435     }
12436     Roo.apply(this, config);
12437     this.buttons = buttons;
12438     
12439     if(container){
12440         this.render(container);
12441     }
12442     this.xitems = xitems;
12443     Roo.each(xitems, function(b) {
12444         this.add(b);
12445     }, this);
12446     
12447 };
12448
12449 Roo.Toolbar.prototype = {
12450     /**
12451      * @cfg {Array} items
12452      * array of button configs or elements to add (will be converted to a MixedCollection)
12453      */
12454     
12455     /**
12456      * @cfg {String/HTMLElement/Element} container
12457      * The id or element that will contain the toolbar
12458      */
12459     // private
12460     render : function(ct){
12461         this.el = Roo.get(ct);
12462         if(this.cls){
12463             this.el.addClass(this.cls);
12464         }
12465         // using a table allows for vertical alignment
12466         // 100% width is needed by Safari...
12467         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12468         this.tr = this.el.child("tr", true);
12469         var autoId = 0;
12470         this.items = new Roo.util.MixedCollection(false, function(o){
12471             return o.id || ("item" + (++autoId));
12472         });
12473         if(this.buttons){
12474             this.add.apply(this, this.buttons);
12475             delete this.buttons;
12476         }
12477     },
12478
12479     /**
12480      * Adds element(s) to the toolbar -- this function takes a variable number of 
12481      * arguments of mixed type and adds them to the toolbar.
12482      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12483      * <ul>
12484      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12485      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12486      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12487      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12488      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12489      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12490      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12491      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12492      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12493      * </ul>
12494      * @param {Mixed} arg2
12495      * @param {Mixed} etc.
12496      */
12497     add : function(){
12498         var a = arguments, l = a.length;
12499         for(var i = 0; i < l; i++){
12500             this._add(a[i]);
12501         }
12502     },
12503     // private..
12504     _add : function(el) {
12505         
12506         if (el.xtype) {
12507             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12508         }
12509         
12510         if (el.applyTo){ // some kind of form field
12511             return this.addField(el);
12512         } 
12513         if (el.render){ // some kind of Toolbar.Item
12514             return this.addItem(el);
12515         }
12516         if (typeof el == "string"){ // string
12517             if(el == "separator" || el == "-"){
12518                 return this.addSeparator();
12519             }
12520             if (el == " "){
12521                 return this.addSpacer();
12522             }
12523             if(el == "->"){
12524                 return this.addFill();
12525             }
12526             return this.addText(el);
12527             
12528         }
12529         if(el.tagName){ // element
12530             return this.addElement(el);
12531         }
12532         if(typeof el == "object"){ // must be button config?
12533             return this.addButton(el);
12534         }
12535         // and now what?!?!
12536         return false;
12537         
12538     },
12539     
12540     /**
12541      * Add an Xtype element
12542      * @param {Object} xtype Xtype Object
12543      * @return {Object} created Object
12544      */
12545     addxtype : function(e){
12546         return this.add(e);  
12547     },
12548     
12549     /**
12550      * Returns the Element for this toolbar.
12551      * @return {Roo.Element}
12552      */
12553     getEl : function(){
12554         return this.el;  
12555     },
12556     
12557     /**
12558      * Adds a separator
12559      * @return {Roo.Toolbar.Item} The separator item
12560      */
12561     addSeparator : function(){
12562         return this.addItem(new Roo.Toolbar.Separator());
12563     },
12564
12565     /**
12566      * Adds a spacer element
12567      * @return {Roo.Toolbar.Spacer} The spacer item
12568      */
12569     addSpacer : function(){
12570         return this.addItem(new Roo.Toolbar.Spacer());
12571     },
12572
12573     /**
12574      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12575      * @return {Roo.Toolbar.Fill} The fill item
12576      */
12577     addFill : function(){
12578         return this.addItem(new Roo.Toolbar.Fill());
12579     },
12580
12581     /**
12582      * Adds any standard HTML element to the toolbar
12583      * @param {String/HTMLElement/Element} el The element or id of the element to add
12584      * @return {Roo.Toolbar.Item} The element's item
12585      */
12586     addElement : function(el){
12587         return this.addItem(new Roo.Toolbar.Item(el));
12588     },
12589     /**
12590      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12591      * @type Roo.util.MixedCollection  
12592      */
12593     items : false,
12594      
12595     /**
12596      * Adds any Toolbar.Item or subclass
12597      * @param {Roo.Toolbar.Item} item
12598      * @return {Roo.Toolbar.Item} The item
12599      */
12600     addItem : function(item){
12601         var td = this.nextBlock();
12602         item.render(td);
12603         this.items.add(item);
12604         return item;
12605     },
12606     
12607     /**
12608      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12609      * @param {Object/Array} config A button config or array of configs
12610      * @return {Roo.Toolbar.Button/Array}
12611      */
12612     addButton : function(config){
12613         if(config instanceof Array){
12614             var buttons = [];
12615             for(var i = 0, len = config.length; i < len; i++) {
12616                 buttons.push(this.addButton(config[i]));
12617             }
12618             return buttons;
12619         }
12620         var b = config;
12621         if(!(config instanceof Roo.Toolbar.Button)){
12622             b = config.split ?
12623                 new Roo.Toolbar.SplitButton(config) :
12624                 new Roo.Toolbar.Button(config);
12625         }
12626         var td = this.nextBlock();
12627         b.render(td);
12628         this.items.add(b);
12629         return b;
12630     },
12631     
12632     /**
12633      * Adds text to the toolbar
12634      * @param {String} text The text to add
12635      * @return {Roo.Toolbar.Item} The element's item
12636      */
12637     addText : function(text){
12638         return this.addItem(new Roo.Toolbar.TextItem(text));
12639     },
12640     
12641     /**
12642      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12643      * @param {Number} index The index where the item is to be inserted
12644      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12645      * @return {Roo.Toolbar.Button/Item}
12646      */
12647     insertButton : function(index, item){
12648         if(item instanceof Array){
12649             var buttons = [];
12650             for(var i = 0, len = item.length; i < len; i++) {
12651                buttons.push(this.insertButton(index + i, item[i]));
12652             }
12653             return buttons;
12654         }
12655         if (!(item instanceof Roo.Toolbar.Button)){
12656            item = new Roo.Toolbar.Button(item);
12657         }
12658         var td = document.createElement("td");
12659         this.tr.insertBefore(td, this.tr.childNodes[index]);
12660         item.render(td);
12661         this.items.insert(index, item);
12662         return item;
12663     },
12664     
12665     /**
12666      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12667      * @param {Object} config
12668      * @return {Roo.Toolbar.Item} The element's item
12669      */
12670     addDom : function(config, returnEl){
12671         var td = this.nextBlock();
12672         Roo.DomHelper.overwrite(td, config);
12673         var ti = new Roo.Toolbar.Item(td.firstChild);
12674         ti.render(td);
12675         this.items.add(ti);
12676         return ti;
12677     },
12678
12679     /**
12680      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12681      * @type Roo.util.MixedCollection  
12682      */
12683     fields : false,
12684     
12685     /**
12686      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12687      * Note: the field should not have been rendered yet. For a field that has already been
12688      * rendered, use {@link #addElement}.
12689      * @param {Roo.form.Field} field
12690      * @return {Roo.ToolbarItem}
12691      */
12692      
12693       
12694     addField : function(field) {
12695         if (!this.fields) {
12696             var autoId = 0;
12697             this.fields = new Roo.util.MixedCollection(false, function(o){
12698                 return o.id || ("item" + (++autoId));
12699             });
12700
12701         }
12702         
12703         var td = this.nextBlock();
12704         field.render(td);
12705         var ti = new Roo.Toolbar.Item(td.firstChild);
12706         ti.render(td);
12707         this.items.add(ti);
12708         this.fields.add(field);
12709         return ti;
12710     },
12711     /**
12712      * Hide the toolbar
12713      * @method hide
12714      */
12715      
12716       
12717     hide : function()
12718     {
12719         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12720         this.el.child('div').hide();
12721     },
12722     /**
12723      * Show the toolbar
12724      * @method show
12725      */
12726     show : function()
12727     {
12728         this.el.child('div').show();
12729     },
12730       
12731     // private
12732     nextBlock : function(){
12733         var td = document.createElement("td");
12734         this.tr.appendChild(td);
12735         return td;
12736     },
12737
12738     // private
12739     destroy : function(){
12740         if(this.items){ // rendered?
12741             Roo.destroy.apply(Roo, this.items.items);
12742         }
12743         if(this.fields){ // rendered?
12744             Roo.destroy.apply(Roo, this.fields.items);
12745         }
12746         Roo.Element.uncache(this.el, this.tr);
12747     }
12748 };
12749
12750 /**
12751  * @class Roo.Toolbar.Item
12752  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12753  * @constructor
12754  * Creates a new Item
12755  * @param {HTMLElement} el 
12756  */
12757 Roo.Toolbar.Item = function(el){
12758     this.el = Roo.getDom(el);
12759     this.id = Roo.id(this.el);
12760     this.hidden = false;
12761 };
12762
12763 Roo.Toolbar.Item.prototype = {
12764     
12765     /**
12766      * Get this item's HTML Element
12767      * @return {HTMLElement}
12768      */
12769     getEl : function(){
12770        return this.el;  
12771     },
12772
12773     // private
12774     render : function(td){
12775         this.td = td;
12776         td.appendChild(this.el);
12777     },
12778     
12779     /**
12780      * Removes and destroys this item.
12781      */
12782     destroy : function(){
12783         this.td.parentNode.removeChild(this.td);
12784     },
12785     
12786     /**
12787      * Shows this item.
12788      */
12789     show: function(){
12790         this.hidden = false;
12791         this.td.style.display = "";
12792     },
12793     
12794     /**
12795      * Hides this item.
12796      */
12797     hide: function(){
12798         this.hidden = true;
12799         this.td.style.display = "none";
12800     },
12801     
12802     /**
12803      * Convenience function for boolean show/hide.
12804      * @param {Boolean} visible true to show/false to hide
12805      */
12806     setVisible: function(visible){
12807         if(visible) {
12808             this.show();
12809         }else{
12810             this.hide();
12811         }
12812     },
12813     
12814     /**
12815      * Try to focus this item.
12816      */
12817     focus : function(){
12818         Roo.fly(this.el).focus();
12819     },
12820     
12821     /**
12822      * Disables this item.
12823      */
12824     disable : function(){
12825         Roo.fly(this.td).addClass("x-item-disabled");
12826         this.disabled = true;
12827         this.el.disabled = true;
12828     },
12829     
12830     /**
12831      * Enables this item.
12832      */
12833     enable : function(){
12834         Roo.fly(this.td).removeClass("x-item-disabled");
12835         this.disabled = false;
12836         this.el.disabled = false;
12837     }
12838 };
12839
12840
12841 /**
12842  * @class Roo.Toolbar.Separator
12843  * @extends Roo.Toolbar.Item
12844  * A simple toolbar separator class
12845  * @constructor
12846  * Creates a new Separator
12847  */
12848 Roo.Toolbar.Separator = function(){
12849     var s = document.createElement("span");
12850     s.className = "ytb-sep";
12851     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12852 };
12853 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12854     enable:Roo.emptyFn,
12855     disable:Roo.emptyFn,
12856     focus:Roo.emptyFn
12857 });
12858
12859 /**
12860  * @class Roo.Toolbar.Spacer
12861  * @extends Roo.Toolbar.Item
12862  * A simple element that adds extra horizontal space to a toolbar.
12863  * @constructor
12864  * Creates a new Spacer
12865  */
12866 Roo.Toolbar.Spacer = function(){
12867     var s = document.createElement("div");
12868     s.className = "ytb-spacer";
12869     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12870 };
12871 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12872     enable:Roo.emptyFn,
12873     disable:Roo.emptyFn,
12874     focus:Roo.emptyFn
12875 });
12876
12877 /**
12878  * @class Roo.Toolbar.Fill
12879  * @extends Roo.Toolbar.Spacer
12880  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12881  * @constructor
12882  * Creates a new Spacer
12883  */
12884 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12885     // private
12886     render : function(td){
12887         td.style.width = '100%';
12888         Roo.Toolbar.Fill.superclass.render.call(this, td);
12889     }
12890 });
12891
12892 /**
12893  * @class Roo.Toolbar.TextItem
12894  * @extends Roo.Toolbar.Item
12895  * A simple class that renders text directly into a toolbar.
12896  * @constructor
12897  * Creates a new TextItem
12898  * @param {String} text
12899  */
12900 Roo.Toolbar.TextItem = function(text){
12901     if (typeof(text) == 'object') {
12902         text = text.text;
12903     }
12904     var s = document.createElement("span");
12905     s.className = "ytb-text";
12906     s.innerHTML = text;
12907     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12908 };
12909 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12910     enable:Roo.emptyFn,
12911     disable:Roo.emptyFn,
12912     focus:Roo.emptyFn
12913 });
12914
12915 /**
12916  * @class Roo.Toolbar.Button
12917  * @extends Roo.Button
12918  * A button that renders into a toolbar.
12919  * @constructor
12920  * Creates a new Button
12921  * @param {Object} config A standard {@link Roo.Button} config object
12922  */
12923 Roo.Toolbar.Button = function(config){
12924     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12925 };
12926 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12927     render : function(td){
12928         this.td = td;
12929         Roo.Toolbar.Button.superclass.render.call(this, td);
12930     },
12931     
12932     /**
12933      * Removes and destroys this button
12934      */
12935     destroy : function(){
12936         Roo.Toolbar.Button.superclass.destroy.call(this);
12937         this.td.parentNode.removeChild(this.td);
12938     },
12939     
12940     /**
12941      * Shows this button
12942      */
12943     show: function(){
12944         this.hidden = false;
12945         this.td.style.display = "";
12946     },
12947     
12948     /**
12949      * Hides this button
12950      */
12951     hide: function(){
12952         this.hidden = true;
12953         this.td.style.display = "none";
12954     },
12955
12956     /**
12957      * Disables this item
12958      */
12959     disable : function(){
12960         Roo.fly(this.td).addClass("x-item-disabled");
12961         this.disabled = true;
12962     },
12963
12964     /**
12965      * Enables this item
12966      */
12967     enable : function(){
12968         Roo.fly(this.td).removeClass("x-item-disabled");
12969         this.disabled = false;
12970     }
12971 });
12972 // backwards compat
12973 Roo.ToolbarButton = Roo.Toolbar.Button;
12974
12975 /**
12976  * @class Roo.Toolbar.SplitButton
12977  * @extends Roo.SplitButton
12978  * A menu button that renders into a toolbar.
12979  * @constructor
12980  * Creates a new SplitButton
12981  * @param {Object} config A standard {@link Roo.SplitButton} config object
12982  */
12983 Roo.Toolbar.SplitButton = function(config){
12984     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12985 };
12986 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12987     render : function(td){
12988         this.td = td;
12989         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12990     },
12991     
12992     /**
12993      * Removes and destroys this button
12994      */
12995     destroy : function(){
12996         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12997         this.td.parentNode.removeChild(this.td);
12998     },
12999     
13000     /**
13001      * Shows this button
13002      */
13003     show: function(){
13004         this.hidden = false;
13005         this.td.style.display = "";
13006     },
13007     
13008     /**
13009      * Hides this button
13010      */
13011     hide: function(){
13012         this.hidden = true;
13013         this.td.style.display = "none";
13014     }
13015 });
13016
13017 // backwards compat
13018 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13019  * Based on:
13020  * Ext JS Library 1.1.1
13021  * Copyright(c) 2006-2007, Ext JS, LLC.
13022  *
13023  * Originally Released Under LGPL - original licence link has changed is not relivant.
13024  *
13025  * Fork - LGPL
13026  * <script type="text/javascript">
13027  */
13028  
13029 /**
13030  * @class Roo.PagingToolbar
13031  * @extends Roo.Toolbar
13032  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13033  * @constructor
13034  * Create a new PagingToolbar
13035  * @param {Object} config The config object
13036  */
13037 Roo.PagingToolbar = function(el, ds, config)
13038 {
13039     // old args format still supported... - xtype is prefered..
13040     if (typeof(el) == 'object' && el.xtype) {
13041         // created from xtype...
13042         config = el;
13043         ds = el.dataSource;
13044         el = config.container;
13045     }
13046     var items = [];
13047     if (config.items) {
13048         items = config.items;
13049         config.items = [];
13050     }
13051     
13052     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13053     this.ds = ds;
13054     this.cursor = 0;
13055     this.renderButtons(this.el);
13056     this.bind(ds);
13057     
13058     // supprot items array.
13059    
13060     Roo.each(items, function(e) {
13061         this.add(Roo.factory(e));
13062     },this);
13063     
13064 };
13065
13066 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13067     /**
13068      * @cfg {Roo.data.Store} dataSource
13069      * The underlying data store providing the paged data
13070      */
13071     /**
13072      * @cfg {String/HTMLElement/Element} container
13073      * container The id or element that will contain the toolbar
13074      */
13075     /**
13076      * @cfg {Boolean} displayInfo
13077      * True to display the displayMsg (defaults to false)
13078      */
13079     /**
13080      * @cfg {Number} pageSize
13081      * The number of records to display per page (defaults to 20)
13082      */
13083     pageSize: 20,
13084     /**
13085      * @cfg {String} displayMsg
13086      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13087      */
13088     displayMsg : 'Displaying {0} - {1} of {2}',
13089     /**
13090      * @cfg {String} emptyMsg
13091      * The message to display when no records are found (defaults to "No data to display")
13092      */
13093     emptyMsg : 'No data to display',
13094     /**
13095      * Customizable piece of the default paging text (defaults to "Page")
13096      * @type String
13097      */
13098     beforePageText : "Page",
13099     /**
13100      * Customizable piece of the default paging text (defaults to "of %0")
13101      * @type String
13102      */
13103     afterPageText : "of {0}",
13104     /**
13105      * Customizable piece of the default paging text (defaults to "First Page")
13106      * @type String
13107      */
13108     firstText : "First Page",
13109     /**
13110      * Customizable piece of the default paging text (defaults to "Previous Page")
13111      * @type String
13112      */
13113     prevText : "Previous Page",
13114     /**
13115      * Customizable piece of the default paging text (defaults to "Next Page")
13116      * @type String
13117      */
13118     nextText : "Next Page",
13119     /**
13120      * Customizable piece of the default paging text (defaults to "Last Page")
13121      * @type String
13122      */
13123     lastText : "Last Page",
13124     /**
13125      * Customizable piece of the default paging text (defaults to "Refresh")
13126      * @type String
13127      */
13128     refreshText : "Refresh",
13129
13130     // private
13131     renderButtons : function(el){
13132         Roo.PagingToolbar.superclass.render.call(this, el);
13133         this.first = this.addButton({
13134             tooltip: this.firstText,
13135             cls: "x-btn-icon x-grid-page-first",
13136             disabled: true,
13137             handler: this.onClick.createDelegate(this, ["first"])
13138         });
13139         this.prev = this.addButton({
13140             tooltip: this.prevText,
13141             cls: "x-btn-icon x-grid-page-prev",
13142             disabled: true,
13143             handler: this.onClick.createDelegate(this, ["prev"])
13144         });
13145         //this.addSeparator();
13146         this.add(this.beforePageText);
13147         this.field = Roo.get(this.addDom({
13148            tag: "input",
13149            type: "text",
13150            size: "3",
13151            value: "1",
13152            cls: "x-grid-page-number"
13153         }).el);
13154         this.field.on("keydown", this.onPagingKeydown, this);
13155         this.field.on("focus", function(){this.dom.select();});
13156         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13157         this.field.setHeight(18);
13158         //this.addSeparator();
13159         this.next = this.addButton({
13160             tooltip: this.nextText,
13161             cls: "x-btn-icon x-grid-page-next",
13162             disabled: true,
13163             handler: this.onClick.createDelegate(this, ["next"])
13164         });
13165         this.last = this.addButton({
13166             tooltip: this.lastText,
13167             cls: "x-btn-icon x-grid-page-last",
13168             disabled: true,
13169             handler: this.onClick.createDelegate(this, ["last"])
13170         });
13171         //this.addSeparator();
13172         this.loading = this.addButton({
13173             tooltip: this.refreshText,
13174             cls: "x-btn-icon x-grid-loading",
13175             handler: this.onClick.createDelegate(this, ["refresh"])
13176         });
13177
13178         if(this.displayInfo){
13179             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13180         }
13181     },
13182
13183     // private
13184     updateInfo : function(){
13185         if(this.displayEl){
13186             var count = this.ds.getCount();
13187             var msg = count == 0 ?
13188                 this.emptyMsg :
13189                 String.format(
13190                     this.displayMsg,
13191                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13192                 );
13193             this.displayEl.update(msg);
13194         }
13195     },
13196
13197     // private
13198     onLoad : function(ds, r, o){
13199        this.cursor = o.params ? o.params.start : 0;
13200        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13201
13202        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13203        this.field.dom.value = ap;
13204        this.first.setDisabled(ap == 1);
13205        this.prev.setDisabled(ap == 1);
13206        this.next.setDisabled(ap == ps);
13207        this.last.setDisabled(ap == ps);
13208        this.loading.enable();
13209        this.updateInfo();
13210     },
13211
13212     // private
13213     getPageData : function(){
13214         var total = this.ds.getTotalCount();
13215         return {
13216             total : total,
13217             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13218             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13219         };
13220     },
13221
13222     // private
13223     onLoadError : function(){
13224         this.loading.enable();
13225     },
13226
13227     // private
13228     onPagingKeydown : function(e){
13229         var k = e.getKey();
13230         var d = this.getPageData();
13231         if(k == e.RETURN){
13232             var v = this.field.dom.value, pageNum;
13233             if(!v || isNaN(pageNum = parseInt(v, 10))){
13234                 this.field.dom.value = d.activePage;
13235                 return;
13236             }
13237             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13238             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13239             e.stopEvent();
13240         }
13241         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))
13242         {
13243           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13244           this.field.dom.value = pageNum;
13245           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13246           e.stopEvent();
13247         }
13248         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13249         {
13250           var v = this.field.dom.value, pageNum; 
13251           var increment = (e.shiftKey) ? 10 : 1;
13252           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13253             increment *= -1;
13254           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13255             this.field.dom.value = d.activePage;
13256             return;
13257           }
13258           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13259           {
13260             this.field.dom.value = parseInt(v, 10) + increment;
13261             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13262             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13263           }
13264           e.stopEvent();
13265         }
13266     },
13267
13268     // private
13269     beforeLoad : function(){
13270         if(this.loading){
13271             this.loading.disable();
13272         }
13273     },
13274
13275     // private
13276     onClick : function(which){
13277         var ds = this.ds;
13278         switch(which){
13279             case "first":
13280                 ds.load({params:{start: 0, limit: this.pageSize}});
13281             break;
13282             case "prev":
13283                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13284             break;
13285             case "next":
13286                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13287             break;
13288             case "last":
13289                 var total = ds.getTotalCount();
13290                 var extra = total % this.pageSize;
13291                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13292                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13293             break;
13294             case "refresh":
13295                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13296             break;
13297         }
13298     },
13299
13300     /**
13301      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13302      * @param {Roo.data.Store} store The data store to unbind
13303      */
13304     unbind : function(ds){
13305         ds.un("beforeload", this.beforeLoad, this);
13306         ds.un("load", this.onLoad, this);
13307         ds.un("loadexception", this.onLoadError, this);
13308         ds.un("remove", this.updateInfo, this);
13309         ds.un("add", this.updateInfo, this);
13310         this.ds = undefined;
13311     },
13312
13313     /**
13314      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13315      * @param {Roo.data.Store} store The data store to bind
13316      */
13317     bind : function(ds){
13318         ds.on("beforeload", this.beforeLoad, this);
13319         ds.on("load", this.onLoad, this);
13320         ds.on("loadexception", this.onLoadError, this);
13321         ds.on("remove", this.updateInfo, this);
13322         ds.on("add", this.updateInfo, this);
13323         this.ds = ds;
13324     }
13325 });/*
13326  * Based on:
13327  * Ext JS Library 1.1.1
13328  * Copyright(c) 2006-2007, Ext JS, LLC.
13329  *
13330  * Originally Released Under LGPL - original licence link has changed is not relivant.
13331  *
13332  * Fork - LGPL
13333  * <script type="text/javascript">
13334  */
13335
13336 /**
13337  * @class Roo.Resizable
13338  * @extends Roo.util.Observable
13339  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13340  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13341  * 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
13342  * the element will be wrapped for you automatically.</p>
13343  * <p>Here is the list of valid resize handles:</p>
13344  * <pre>
13345 Value   Description
13346 ------  -------------------
13347  'n'     north
13348  's'     south
13349  'e'     east
13350  'w'     west
13351  'nw'    northwest
13352  'sw'    southwest
13353  'se'    southeast
13354  'ne'    northeast
13355  'hd'    horizontal drag
13356  'all'   all
13357 </pre>
13358  * <p>Here's an example showing the creation of a typical Resizable:</p>
13359  * <pre><code>
13360 var resizer = new Roo.Resizable("element-id", {
13361     handles: 'all',
13362     minWidth: 200,
13363     minHeight: 100,
13364     maxWidth: 500,
13365     maxHeight: 400,
13366     pinned: true
13367 });
13368 resizer.on("resize", myHandler);
13369 </code></pre>
13370  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13371  * resizer.east.setDisplayed(false);</p>
13372  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13373  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13374  * resize operation's new size (defaults to [0, 0])
13375  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13376  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13377  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13378  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13379  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13380  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13381  * @cfg {Number} width The width of the element in pixels (defaults to null)
13382  * @cfg {Number} height The height of the element in pixels (defaults to null)
13383  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13384  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13385  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13386  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13387  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13388  * in favor of the handles config option (defaults to false)
13389  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13390  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13391  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13392  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13393  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13394  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13395  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13396  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13397  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13398  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13399  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13400  * @constructor
13401  * Create a new resizable component
13402  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13403  * @param {Object} config configuration options
13404   */
13405 Roo.Resizable = function(el, config)
13406 {
13407     this.el = Roo.get(el);
13408
13409     if(config && config.wrap){
13410         config.resizeChild = this.el;
13411         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13412         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13413         this.el.setStyle("overflow", "hidden");
13414         this.el.setPositioning(config.resizeChild.getPositioning());
13415         config.resizeChild.clearPositioning();
13416         if(!config.width || !config.height){
13417             var csize = config.resizeChild.getSize();
13418             this.el.setSize(csize.width, csize.height);
13419         }
13420         if(config.pinned && !config.adjustments){
13421             config.adjustments = "auto";
13422         }
13423     }
13424
13425     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13426     this.proxy.unselectable();
13427     this.proxy.enableDisplayMode('block');
13428
13429     Roo.apply(this, config);
13430
13431     if(this.pinned){
13432         this.disableTrackOver = true;
13433         this.el.addClass("x-resizable-pinned");
13434     }
13435     // if the element isn't positioned, make it relative
13436     var position = this.el.getStyle("position");
13437     if(position != "absolute" && position != "fixed"){
13438         this.el.setStyle("position", "relative");
13439     }
13440     if(!this.handles){ // no handles passed, must be legacy style
13441         this.handles = 's,e,se';
13442         if(this.multiDirectional){
13443             this.handles += ',n,w';
13444         }
13445     }
13446     if(this.handles == "all"){
13447         this.handles = "n s e w ne nw se sw";
13448     }
13449     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13450     var ps = Roo.Resizable.positions;
13451     for(var i = 0, len = hs.length; i < len; i++){
13452         if(hs[i] && ps[hs[i]]){
13453             var pos = ps[hs[i]];
13454             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13455         }
13456     }
13457     // legacy
13458     this.corner = this.southeast;
13459     
13460     // updateBox = the box can move..
13461     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13462         this.updateBox = true;
13463     }
13464
13465     this.activeHandle = null;
13466
13467     if(this.resizeChild){
13468         if(typeof this.resizeChild == "boolean"){
13469             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13470         }else{
13471             this.resizeChild = Roo.get(this.resizeChild, true);
13472         }
13473     }
13474     
13475     if(this.adjustments == "auto"){
13476         var rc = this.resizeChild;
13477         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13478         if(rc && (hw || hn)){
13479             rc.position("relative");
13480             rc.setLeft(hw ? hw.el.getWidth() : 0);
13481             rc.setTop(hn ? hn.el.getHeight() : 0);
13482         }
13483         this.adjustments = [
13484             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13485             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13486         ];
13487     }
13488
13489     if(this.draggable){
13490         this.dd = this.dynamic ?
13491             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13492         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13493     }
13494
13495     // public events
13496     this.addEvents({
13497         /**
13498          * @event beforeresize
13499          * Fired before resize is allowed. Set enabled to false to cancel resize.
13500          * @param {Roo.Resizable} this
13501          * @param {Roo.EventObject} e The mousedown event
13502          */
13503         "beforeresize" : true,
13504         /**
13505          * @event resize
13506          * Fired after a resize.
13507          * @param {Roo.Resizable} this
13508          * @param {Number} width The new width
13509          * @param {Number} height The new height
13510          * @param {Roo.EventObject} e The mouseup event
13511          */
13512         "resize" : true
13513     });
13514
13515     if(this.width !== null && this.height !== null){
13516         this.resizeTo(this.width, this.height);
13517     }else{
13518         this.updateChildSize();
13519     }
13520     if(Roo.isIE){
13521         this.el.dom.style.zoom = 1;
13522     }
13523     Roo.Resizable.superclass.constructor.call(this);
13524 };
13525
13526 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13527         resizeChild : false,
13528         adjustments : [0, 0],
13529         minWidth : 5,
13530         minHeight : 5,
13531         maxWidth : 10000,
13532         maxHeight : 10000,
13533         enabled : true,
13534         animate : false,
13535         duration : .35,
13536         dynamic : false,
13537         handles : false,
13538         multiDirectional : false,
13539         disableTrackOver : false,
13540         easing : 'easeOutStrong',
13541         widthIncrement : 0,
13542         heightIncrement : 0,
13543         pinned : false,
13544         width : null,
13545         height : null,
13546         preserveRatio : false,
13547         transparent: false,
13548         minX: 0,
13549         minY: 0,
13550         draggable: false,
13551
13552         /**
13553          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13554          */
13555         constrainTo: undefined,
13556         /**
13557          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13558          */
13559         resizeRegion: undefined,
13560
13561
13562     /**
13563      * Perform a manual resize
13564      * @param {Number} width
13565      * @param {Number} height
13566      */
13567     resizeTo : function(width, height){
13568         this.el.setSize(width, height);
13569         this.updateChildSize();
13570         this.fireEvent("resize", this, width, height, null);
13571     },
13572
13573     // private
13574     startSizing : function(e, handle){
13575         this.fireEvent("beforeresize", this, e);
13576         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13577
13578             if(!this.overlay){
13579                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13580                 this.overlay.unselectable();
13581                 this.overlay.enableDisplayMode("block");
13582                 this.overlay.on("mousemove", this.onMouseMove, this);
13583                 this.overlay.on("mouseup", this.onMouseUp, this);
13584             }
13585             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13586
13587             this.resizing = true;
13588             this.startBox = this.el.getBox();
13589             this.startPoint = e.getXY();
13590             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13591                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13592
13593             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13594             this.overlay.show();
13595
13596             if(this.constrainTo) {
13597                 var ct = Roo.get(this.constrainTo);
13598                 this.resizeRegion = ct.getRegion().adjust(
13599                     ct.getFrameWidth('t'),
13600                     ct.getFrameWidth('l'),
13601                     -ct.getFrameWidth('b'),
13602                     -ct.getFrameWidth('r')
13603                 );
13604             }
13605
13606             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13607             this.proxy.show();
13608             this.proxy.setBox(this.startBox);
13609             if(!this.dynamic){
13610                 this.proxy.setStyle('visibility', 'visible');
13611             }
13612         }
13613     },
13614
13615     // private
13616     onMouseDown : function(handle, e){
13617         if(this.enabled){
13618             e.stopEvent();
13619             this.activeHandle = handle;
13620             this.startSizing(e, handle);
13621         }
13622     },
13623
13624     // private
13625     onMouseUp : function(e){
13626         var size = this.resizeElement();
13627         this.resizing = false;
13628         this.handleOut();
13629         this.overlay.hide();
13630         this.proxy.hide();
13631         this.fireEvent("resize", this, size.width, size.height, e);
13632     },
13633
13634     // private
13635     updateChildSize : function(){
13636         if(this.resizeChild){
13637             var el = this.el;
13638             var child = this.resizeChild;
13639             var adj = this.adjustments;
13640             if(el.dom.offsetWidth){
13641                 var b = el.getSize(true);
13642                 child.setSize(b.width+adj[0], b.height+adj[1]);
13643             }
13644             // Second call here for IE
13645             // The first call enables instant resizing and
13646             // the second call corrects scroll bars if they
13647             // exist
13648             if(Roo.isIE){
13649                 setTimeout(function(){
13650                     if(el.dom.offsetWidth){
13651                         var b = el.getSize(true);
13652                         child.setSize(b.width+adj[0], b.height+adj[1]);
13653                     }
13654                 }, 10);
13655             }
13656         }
13657     },
13658
13659     // private
13660     snap : function(value, inc, min){
13661         if(!inc || !value) return value;
13662         var newValue = value;
13663         var m = value % inc;
13664         if(m > 0){
13665             if(m > (inc/2)){
13666                 newValue = value + (inc-m);
13667             }else{
13668                 newValue = value - m;
13669             }
13670         }
13671         return Math.max(min, newValue);
13672     },
13673
13674     // private
13675     resizeElement : function(){
13676         var box = this.proxy.getBox();
13677         if(this.updateBox){
13678             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13679         }else{
13680             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13681         }
13682         this.updateChildSize();
13683         if(!this.dynamic){
13684             this.proxy.hide();
13685         }
13686         return box;
13687     },
13688
13689     // private
13690     constrain : function(v, diff, m, mx){
13691         if(v - diff < m){
13692             diff = v - m;
13693         }else if(v - diff > mx){
13694             diff = mx - v;
13695         }
13696         return diff;
13697     },
13698
13699     // private
13700     onMouseMove : function(e){
13701         if(this.enabled){
13702             try{// try catch so if something goes wrong the user doesn't get hung
13703
13704             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13705                 return;
13706             }
13707
13708             //var curXY = this.startPoint;
13709             var curSize = this.curSize || this.startBox;
13710             var x = this.startBox.x, y = this.startBox.y;
13711             var ox = x, oy = y;
13712             var w = curSize.width, h = curSize.height;
13713             var ow = w, oh = h;
13714             var mw = this.minWidth, mh = this.minHeight;
13715             var mxw = this.maxWidth, mxh = this.maxHeight;
13716             var wi = this.widthIncrement;
13717             var hi = this.heightIncrement;
13718
13719             var eventXY = e.getXY();
13720             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13721             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13722
13723             var pos = this.activeHandle.position;
13724
13725             switch(pos){
13726                 case "east":
13727                     w += diffX;
13728                     w = Math.min(Math.max(mw, w), mxw);
13729                     break;
13730              
13731                 case "south":
13732                     h += diffY;
13733                     h = Math.min(Math.max(mh, h), mxh);
13734                     break;
13735                 case "southeast":
13736                     w += diffX;
13737                     h += diffY;
13738                     w = Math.min(Math.max(mw, w), mxw);
13739                     h = Math.min(Math.max(mh, h), mxh);
13740                     break;
13741                 case "north":
13742                     diffY = this.constrain(h, diffY, mh, mxh);
13743                     y += diffY;
13744                     h -= diffY;
13745                     break;
13746                 case "hdrag":
13747                     
13748                     if (wi) {
13749                         var adiffX = Math.abs(diffX);
13750                         var sub = (adiffX % wi); // how much 
13751                         if (sub > (wi/2)) { // far enough to snap
13752                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13753                         } else {
13754                             // remove difference.. 
13755                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13756                         }
13757                     }
13758                     x += diffX;
13759                     x = Math.max(this.minX, x);
13760                     break;
13761                 case "west":
13762                     diffX = this.constrain(w, diffX, mw, mxw);
13763                     x += diffX;
13764                     w -= diffX;
13765                     break;
13766                 case "northeast":
13767                     w += diffX;
13768                     w = Math.min(Math.max(mw, w), mxw);
13769                     diffY = this.constrain(h, diffY, mh, mxh);
13770                     y += diffY;
13771                     h -= diffY;
13772                     break;
13773                 case "northwest":
13774                     diffX = this.constrain(w, diffX, mw, mxw);
13775                     diffY = this.constrain(h, diffY, mh, mxh);
13776                     y += diffY;
13777                     h -= diffY;
13778                     x += diffX;
13779                     w -= diffX;
13780                     break;
13781                case "southwest":
13782                     diffX = this.constrain(w, diffX, mw, mxw);
13783                     h += diffY;
13784                     h = Math.min(Math.max(mh, h), mxh);
13785                     x += diffX;
13786                     w -= diffX;
13787                     break;
13788             }
13789
13790             var sw = this.snap(w, wi, mw);
13791             var sh = this.snap(h, hi, mh);
13792             if(sw != w || sh != h){
13793                 switch(pos){
13794                     case "northeast":
13795                         y -= sh - h;
13796                     break;
13797                     case "north":
13798                         y -= sh - h;
13799                         break;
13800                     case "southwest":
13801                         x -= sw - w;
13802                     break;
13803                     case "west":
13804                         x -= sw - w;
13805                         break;
13806                     case "northwest":
13807                         x -= sw - w;
13808                         y -= sh - h;
13809                     break;
13810                 }
13811                 w = sw;
13812                 h = sh;
13813             }
13814
13815             if(this.preserveRatio){
13816                 switch(pos){
13817                     case "southeast":
13818                     case "east":
13819                         h = oh * (w/ow);
13820                         h = Math.min(Math.max(mh, h), mxh);
13821                         w = ow * (h/oh);
13822                        break;
13823                     case "south":
13824                         w = ow * (h/oh);
13825                         w = Math.min(Math.max(mw, w), mxw);
13826                         h = oh * (w/ow);
13827                         break;
13828                     case "northeast":
13829                         w = ow * (h/oh);
13830                         w = Math.min(Math.max(mw, w), mxw);
13831                         h = oh * (w/ow);
13832                     break;
13833                     case "north":
13834                         var tw = w;
13835                         w = ow * (h/oh);
13836                         w = Math.min(Math.max(mw, w), mxw);
13837                         h = oh * (w/ow);
13838                         x += (tw - w) / 2;
13839                         break;
13840                     case "southwest":
13841                         h = oh * (w/ow);
13842                         h = Math.min(Math.max(mh, h), mxh);
13843                         var tw = w;
13844                         w = ow * (h/oh);
13845                         x += tw - w;
13846                         break;
13847                     case "west":
13848                         var th = h;
13849                         h = oh * (w/ow);
13850                         h = Math.min(Math.max(mh, h), mxh);
13851                         y += (th - h) / 2;
13852                         var tw = w;
13853                         w = ow * (h/oh);
13854                         x += tw - w;
13855                        break;
13856                     case "northwest":
13857                         var tw = w;
13858                         var th = h;
13859                         h = oh * (w/ow);
13860                         h = Math.min(Math.max(mh, h), mxh);
13861                         w = ow * (h/oh);
13862                         y += th - h;
13863                         x += tw - w;
13864                        break;
13865
13866                 }
13867             }
13868             if (pos == 'hdrag') {
13869                 w = ow;
13870             }
13871             this.proxy.setBounds(x, y, w, h);
13872             if(this.dynamic){
13873                 this.resizeElement();
13874             }
13875             }catch(e){}
13876         }
13877     },
13878
13879     // private
13880     handleOver : function(){
13881         if(this.enabled){
13882             this.el.addClass("x-resizable-over");
13883         }
13884     },
13885
13886     // private
13887     handleOut : function(){
13888         if(!this.resizing){
13889             this.el.removeClass("x-resizable-over");
13890         }
13891     },
13892
13893     /**
13894      * Returns the element this component is bound to.
13895      * @return {Roo.Element}
13896      */
13897     getEl : function(){
13898         return this.el;
13899     },
13900
13901     /**
13902      * Returns the resizeChild element (or null).
13903      * @return {Roo.Element}
13904      */
13905     getResizeChild : function(){
13906         return this.resizeChild;
13907     },
13908
13909     /**
13910      * Destroys this resizable. If the element was wrapped and
13911      * removeEl is not true then the element remains.
13912      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13913      */
13914     destroy : function(removeEl){
13915         this.proxy.remove();
13916         if(this.overlay){
13917             this.overlay.removeAllListeners();
13918             this.overlay.remove();
13919         }
13920         var ps = Roo.Resizable.positions;
13921         for(var k in ps){
13922             if(typeof ps[k] != "function" && this[ps[k]]){
13923                 var h = this[ps[k]];
13924                 h.el.removeAllListeners();
13925                 h.el.remove();
13926             }
13927         }
13928         if(removeEl){
13929             this.el.update("");
13930             this.el.remove();
13931         }
13932     }
13933 });
13934
13935 // private
13936 // hash to map config positions to true positions
13937 Roo.Resizable.positions = {
13938     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13939     hd: "hdrag"
13940 };
13941
13942 // private
13943 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13944     if(!this.tpl){
13945         // only initialize the template if resizable is used
13946         var tpl = Roo.DomHelper.createTemplate(
13947             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13948         );
13949         tpl.compile();
13950         Roo.Resizable.Handle.prototype.tpl = tpl;
13951     }
13952     this.position = pos;
13953     this.rz = rz;
13954     // show north drag fro topdra
13955     var handlepos = pos == 'hdrag' ? 'north' : pos;
13956     
13957     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13958     if (pos == 'hdrag') {
13959         this.el.setStyle('cursor', 'pointer');
13960     }
13961     this.el.unselectable();
13962     if(transparent){
13963         this.el.setOpacity(0);
13964     }
13965     this.el.on("mousedown", this.onMouseDown, this);
13966     if(!disableTrackOver){
13967         this.el.on("mouseover", this.onMouseOver, this);
13968         this.el.on("mouseout", this.onMouseOut, this);
13969     }
13970 };
13971
13972 // private
13973 Roo.Resizable.Handle.prototype = {
13974     afterResize : function(rz){
13975         // do nothing
13976     },
13977     // private
13978     onMouseDown : function(e){
13979         this.rz.onMouseDown(this, e);
13980     },
13981     // private
13982     onMouseOver : function(e){
13983         this.rz.handleOver(this, e);
13984     },
13985     // private
13986     onMouseOut : function(e){
13987         this.rz.handleOut(this, e);
13988     }
13989 };/*
13990  * Based on:
13991  * Ext JS Library 1.1.1
13992  * Copyright(c) 2006-2007, Ext JS, LLC.
13993  *
13994  * Originally Released Under LGPL - original licence link has changed is not relivant.
13995  *
13996  * Fork - LGPL
13997  * <script type="text/javascript">
13998  */
13999
14000 /**
14001  * @class Roo.Editor
14002  * @extends Roo.Component
14003  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14004  * @constructor
14005  * Create a new Editor
14006  * @param {Roo.form.Field} field The Field object (or descendant)
14007  * @param {Object} config The config object
14008  */
14009 Roo.Editor = function(field, config){
14010     Roo.Editor.superclass.constructor.call(this, config);
14011     this.field = field;
14012     this.addEvents({
14013         /**
14014              * @event beforestartedit
14015              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14016              * false from the handler of this event.
14017              * @param {Editor} this
14018              * @param {Roo.Element} boundEl The underlying element bound to this editor
14019              * @param {Mixed} value The field value being set
14020              */
14021         "beforestartedit" : true,
14022         /**
14023              * @event startedit
14024              * Fires when this editor is displayed
14025              * @param {Roo.Element} boundEl The underlying element bound to this editor
14026              * @param {Mixed} value The starting field value
14027              */
14028         "startedit" : true,
14029         /**
14030              * @event beforecomplete
14031              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14032              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14033              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14034              * event will not fire since no edit actually occurred.
14035              * @param {Editor} this
14036              * @param {Mixed} value The current field value
14037              * @param {Mixed} startValue The original field value
14038              */
14039         "beforecomplete" : true,
14040         /**
14041              * @event complete
14042              * Fires after editing is complete and any changed value has been written to the underlying field.
14043              * @param {Editor} this
14044              * @param {Mixed} value The current field value
14045              * @param {Mixed} startValue The original field value
14046              */
14047         "complete" : true,
14048         /**
14049          * @event specialkey
14050          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14051          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14052          * @param {Roo.form.Field} this
14053          * @param {Roo.EventObject} e The event object
14054          */
14055         "specialkey" : true
14056     });
14057 };
14058
14059 Roo.extend(Roo.Editor, Roo.Component, {
14060     /**
14061      * @cfg {Boolean/String} autosize
14062      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14063      * or "height" to adopt the height only (defaults to false)
14064      */
14065     /**
14066      * @cfg {Boolean} revertInvalid
14067      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14068      * validation fails (defaults to true)
14069      */
14070     /**
14071      * @cfg {Boolean} ignoreNoChange
14072      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14073      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14074      * will never be ignored.
14075      */
14076     /**
14077      * @cfg {Boolean} hideEl
14078      * False to keep the bound element visible while the editor is displayed (defaults to true)
14079      */
14080     /**
14081      * @cfg {Mixed} value
14082      * The data value of the underlying field (defaults to "")
14083      */
14084     value : "",
14085     /**
14086      * @cfg {String} alignment
14087      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14088      */
14089     alignment: "c-c?",
14090     /**
14091      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14092      * for bottom-right shadow (defaults to "frame")
14093      */
14094     shadow : "frame",
14095     /**
14096      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14097      */
14098     constrain : false,
14099     /**
14100      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14101      */
14102     completeOnEnter : false,
14103     /**
14104      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14105      */
14106     cancelOnEsc : false,
14107     /**
14108      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14109      */
14110     updateEl : false,
14111
14112     // private
14113     onRender : function(ct, position){
14114         this.el = new Roo.Layer({
14115             shadow: this.shadow,
14116             cls: "x-editor",
14117             parentEl : ct,
14118             shim : this.shim,
14119             shadowOffset:4,
14120             id: this.id,
14121             constrain: this.constrain
14122         });
14123         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14124         if(this.field.msgTarget != 'title'){
14125             this.field.msgTarget = 'qtip';
14126         }
14127         this.field.render(this.el);
14128         if(Roo.isGecko){
14129             this.field.el.dom.setAttribute('autocomplete', 'off');
14130         }
14131         this.field.on("specialkey", this.onSpecialKey, this);
14132         if(this.swallowKeys){
14133             this.field.el.swallowEvent(['keydown','keypress']);
14134         }
14135         this.field.show();
14136         this.field.on("blur", this.onBlur, this);
14137         if(this.field.grow){
14138             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14139         }
14140     },
14141
14142     onSpecialKey : function(field, e)
14143     {
14144         //Roo.log('editor onSpecialKey');
14145         if(this.completeOnEnter && e.getKey() == e.ENTER){
14146             e.stopEvent();
14147             this.completeEdit();
14148             return;
14149         }
14150         // do not fire special key otherwise it might hide close the editor...
14151         if(e.getKey() == e.ENTER){    
14152             return;
14153         }
14154         if(this.cancelOnEsc && e.getKey() == e.ESC){
14155             this.cancelEdit();
14156             return;
14157         } 
14158         this.fireEvent('specialkey', field, e);
14159     
14160     },
14161
14162     /**
14163      * Starts the editing process and shows the editor.
14164      * @param {String/HTMLElement/Element} el The element to edit
14165      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14166       * to the innerHTML of el.
14167      */
14168     startEdit : function(el, value){
14169         if(this.editing){
14170             this.completeEdit();
14171         }
14172         this.boundEl = Roo.get(el);
14173         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14174         if(!this.rendered){
14175             this.render(this.parentEl || document.body);
14176         }
14177         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14178             return;
14179         }
14180         this.startValue = v;
14181         this.field.setValue(v);
14182         if(this.autoSize){
14183             var sz = this.boundEl.getSize();
14184             switch(this.autoSize){
14185                 case "width":
14186                 this.setSize(sz.width,  "");
14187                 break;
14188                 case "height":
14189                 this.setSize("",  sz.height);
14190                 break;
14191                 default:
14192                 this.setSize(sz.width,  sz.height);
14193             }
14194         }
14195         this.el.alignTo(this.boundEl, this.alignment);
14196         this.editing = true;
14197         if(Roo.QuickTips){
14198             Roo.QuickTips.disable();
14199         }
14200         this.show();
14201     },
14202
14203     /**
14204      * Sets the height and width of this editor.
14205      * @param {Number} width The new width
14206      * @param {Number} height The new height
14207      */
14208     setSize : function(w, h){
14209         this.field.setSize(w, h);
14210         if(this.el){
14211             this.el.sync();
14212         }
14213     },
14214
14215     /**
14216      * Realigns the editor to the bound field based on the current alignment config value.
14217      */
14218     realign : function(){
14219         this.el.alignTo(this.boundEl, this.alignment);
14220     },
14221
14222     /**
14223      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14224      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14225      */
14226     completeEdit : function(remainVisible){
14227         if(!this.editing){
14228             return;
14229         }
14230         var v = this.getValue();
14231         if(this.revertInvalid !== false && !this.field.isValid()){
14232             v = this.startValue;
14233             this.cancelEdit(true);
14234         }
14235         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14236             this.editing = false;
14237             this.hide();
14238             return;
14239         }
14240         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14241             this.editing = false;
14242             if(this.updateEl && this.boundEl){
14243                 this.boundEl.update(v);
14244             }
14245             if(remainVisible !== true){
14246                 this.hide();
14247             }
14248             this.fireEvent("complete", this, v, this.startValue);
14249         }
14250     },
14251
14252     // private
14253     onShow : function(){
14254         this.el.show();
14255         if(this.hideEl !== false){
14256             this.boundEl.hide();
14257         }
14258         this.field.show();
14259         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14260             this.fixIEFocus = true;
14261             this.deferredFocus.defer(50, this);
14262         }else{
14263             this.field.focus();
14264         }
14265         this.fireEvent("startedit", this.boundEl, this.startValue);
14266     },
14267
14268     deferredFocus : function(){
14269         if(this.editing){
14270             this.field.focus();
14271         }
14272     },
14273
14274     /**
14275      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14276      * reverted to the original starting value.
14277      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14278      * cancel (defaults to false)
14279      */
14280     cancelEdit : function(remainVisible){
14281         if(this.editing){
14282             this.setValue(this.startValue);
14283             if(remainVisible !== true){
14284                 this.hide();
14285             }
14286         }
14287     },
14288
14289     // private
14290     onBlur : function(){
14291         if(this.allowBlur !== true && this.editing){
14292             this.completeEdit();
14293         }
14294     },
14295
14296     // private
14297     onHide : function(){
14298         if(this.editing){
14299             this.completeEdit();
14300             return;
14301         }
14302         this.field.blur();
14303         if(this.field.collapse){
14304             this.field.collapse();
14305         }
14306         this.el.hide();
14307         if(this.hideEl !== false){
14308             this.boundEl.show();
14309         }
14310         if(Roo.QuickTips){
14311             Roo.QuickTips.enable();
14312         }
14313     },
14314
14315     /**
14316      * Sets the data value of the editor
14317      * @param {Mixed} value Any valid value supported by the underlying field
14318      */
14319     setValue : function(v){
14320         this.field.setValue(v);
14321     },
14322
14323     /**
14324      * Gets the data value of the editor
14325      * @return {Mixed} The data value
14326      */
14327     getValue : function(){
14328         return this.field.getValue();
14329     }
14330 });/*
14331  * Based on:
14332  * Ext JS Library 1.1.1
14333  * Copyright(c) 2006-2007, Ext JS, LLC.
14334  *
14335  * Originally Released Under LGPL - original licence link has changed is not relivant.
14336  *
14337  * Fork - LGPL
14338  * <script type="text/javascript">
14339  */
14340  
14341 /**
14342  * @class Roo.BasicDialog
14343  * @extends Roo.util.Observable
14344  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14345  * <pre><code>
14346 var dlg = new Roo.BasicDialog("my-dlg", {
14347     height: 200,
14348     width: 300,
14349     minHeight: 100,
14350     minWidth: 150,
14351     modal: true,
14352     proxyDrag: true,
14353     shadow: true
14354 });
14355 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14356 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14357 dlg.addButton('Cancel', dlg.hide, dlg);
14358 dlg.show();
14359 </code></pre>
14360   <b>A Dialog should always be a direct child of the body element.</b>
14361  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14362  * @cfg {String} title Default text to display in the title bar (defaults to null)
14363  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14364  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14365  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14366  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14367  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14368  * (defaults to null with no animation)
14369  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14370  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14371  * property for valid values (defaults to 'all')
14372  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14373  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14374  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14375  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14376  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14377  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14378  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14379  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14380  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14381  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14382  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14383  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14384  * draggable = true (defaults to false)
14385  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14386  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14387  * shadow (defaults to false)
14388  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14389  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14390  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14391  * @cfg {Array} buttons Array of buttons
14392  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14393  * @constructor
14394  * Create a new BasicDialog.
14395  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14396  * @param {Object} config Configuration options
14397  */
14398 Roo.BasicDialog = function(el, config){
14399     this.el = Roo.get(el);
14400     var dh = Roo.DomHelper;
14401     if(!this.el && config && config.autoCreate){
14402         if(typeof config.autoCreate == "object"){
14403             if(!config.autoCreate.id){
14404                 config.autoCreate.id = el;
14405             }
14406             this.el = dh.append(document.body,
14407                         config.autoCreate, true);
14408         }else{
14409             this.el = dh.append(document.body,
14410                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14411         }
14412     }
14413     el = this.el;
14414     el.setDisplayed(true);
14415     el.hide = this.hideAction;
14416     this.id = el.id;
14417     el.addClass("x-dlg");
14418
14419     Roo.apply(this, config);
14420
14421     this.proxy = el.createProxy("x-dlg-proxy");
14422     this.proxy.hide = this.hideAction;
14423     this.proxy.setOpacity(.5);
14424     this.proxy.hide();
14425
14426     if(config.width){
14427         el.setWidth(config.width);
14428     }
14429     if(config.height){
14430         el.setHeight(config.height);
14431     }
14432     this.size = el.getSize();
14433     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14434         this.xy = [config.x,config.y];
14435     }else{
14436         this.xy = el.getCenterXY(true);
14437     }
14438     /** The header element @type Roo.Element */
14439     this.header = el.child("> .x-dlg-hd");
14440     /** The body element @type Roo.Element */
14441     this.body = el.child("> .x-dlg-bd");
14442     /** The footer element @type Roo.Element */
14443     this.footer = el.child("> .x-dlg-ft");
14444
14445     if(!this.header){
14446         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14447     }
14448     if(!this.body){
14449         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14450     }
14451
14452     this.header.unselectable();
14453     if(this.title){
14454         this.header.update(this.title);
14455     }
14456     // this element allows the dialog to be focused for keyboard event
14457     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14458     this.focusEl.swallowEvent("click", true);
14459
14460     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14461
14462     // wrap the body and footer for special rendering
14463     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14464     if(this.footer){
14465         this.bwrap.dom.appendChild(this.footer.dom);
14466     }
14467
14468     this.bg = this.el.createChild({
14469         tag: "div", cls:"x-dlg-bg",
14470         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14471     });
14472     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14473
14474
14475     if(this.autoScroll !== false && !this.autoTabs){
14476         this.body.setStyle("overflow", "auto");
14477     }
14478
14479     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14480
14481     if(this.closable !== false){
14482         this.el.addClass("x-dlg-closable");
14483         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14484         this.close.on("click", this.closeClick, this);
14485         this.close.addClassOnOver("x-dlg-close-over");
14486     }
14487     if(this.collapsible !== false){
14488         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14489         this.collapseBtn.on("click", this.collapseClick, this);
14490         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14491         this.header.on("dblclick", this.collapseClick, this);
14492     }
14493     if(this.resizable !== false){
14494         this.el.addClass("x-dlg-resizable");
14495         this.resizer = new Roo.Resizable(el, {
14496             minWidth: this.minWidth || 80,
14497             minHeight:this.minHeight || 80,
14498             handles: this.resizeHandles || "all",
14499             pinned: true
14500         });
14501         this.resizer.on("beforeresize", this.beforeResize, this);
14502         this.resizer.on("resize", this.onResize, this);
14503     }
14504     if(this.draggable !== false){
14505         el.addClass("x-dlg-draggable");
14506         if (!this.proxyDrag) {
14507             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14508         }
14509         else {
14510             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14511         }
14512         dd.setHandleElId(this.header.id);
14513         dd.endDrag = this.endMove.createDelegate(this);
14514         dd.startDrag = this.startMove.createDelegate(this);
14515         dd.onDrag = this.onDrag.createDelegate(this);
14516         dd.scroll = false;
14517         this.dd = dd;
14518     }
14519     if(this.modal){
14520         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14521         this.mask.enableDisplayMode("block");
14522         this.mask.hide();
14523         this.el.addClass("x-dlg-modal");
14524     }
14525     if(this.shadow){
14526         this.shadow = new Roo.Shadow({
14527             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14528             offset : this.shadowOffset
14529         });
14530     }else{
14531         this.shadowOffset = 0;
14532     }
14533     if(Roo.useShims && this.shim !== false){
14534         this.shim = this.el.createShim();
14535         this.shim.hide = this.hideAction;
14536         this.shim.hide();
14537     }else{
14538         this.shim = false;
14539     }
14540     if(this.autoTabs){
14541         this.initTabs();
14542     }
14543     if (this.buttons) { 
14544         var bts= this.buttons;
14545         this.buttons = [];
14546         Roo.each(bts, function(b) {
14547             this.addButton(b);
14548         }, this);
14549     }
14550     
14551     
14552     this.addEvents({
14553         /**
14554          * @event keydown
14555          * Fires when a key is pressed
14556          * @param {Roo.BasicDialog} this
14557          * @param {Roo.EventObject} e
14558          */
14559         "keydown" : true,
14560         /**
14561          * @event move
14562          * Fires when this dialog is moved by the user.
14563          * @param {Roo.BasicDialog} this
14564          * @param {Number} x The new page X
14565          * @param {Number} y The new page Y
14566          */
14567         "move" : true,
14568         /**
14569          * @event resize
14570          * Fires when this dialog is resized by the user.
14571          * @param {Roo.BasicDialog} this
14572          * @param {Number} width The new width
14573          * @param {Number} height The new height
14574          */
14575         "resize" : true,
14576         /**
14577          * @event beforehide
14578          * Fires before this dialog is hidden.
14579          * @param {Roo.BasicDialog} this
14580          */
14581         "beforehide" : true,
14582         /**
14583          * @event hide
14584          * Fires when this dialog is hidden.
14585          * @param {Roo.BasicDialog} this
14586          */
14587         "hide" : true,
14588         /**
14589          * @event beforeshow
14590          * Fires before this dialog is shown.
14591          * @param {Roo.BasicDialog} this
14592          */
14593         "beforeshow" : true,
14594         /**
14595          * @event show
14596          * Fires when this dialog is shown.
14597          * @param {Roo.BasicDialog} this
14598          */
14599         "show" : true
14600     });
14601     el.on("keydown", this.onKeyDown, this);
14602     el.on("mousedown", this.toFront, this);
14603     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14604     this.el.hide();
14605     Roo.DialogManager.register(this);
14606     Roo.BasicDialog.superclass.constructor.call(this);
14607 };
14608
14609 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14610     shadowOffset: Roo.isIE ? 6 : 5,
14611     minHeight: 80,
14612     minWidth: 200,
14613     minButtonWidth: 75,
14614     defaultButton: null,
14615     buttonAlign: "right",
14616     tabTag: 'div',
14617     firstShow: true,
14618
14619     /**
14620      * Sets the dialog title text
14621      * @param {String} text The title text to display
14622      * @return {Roo.BasicDialog} this
14623      */
14624     setTitle : function(text){
14625         this.header.update(text);
14626         return this;
14627     },
14628
14629     // private
14630     closeClick : function(){
14631         this.hide();
14632     },
14633
14634     // private
14635     collapseClick : function(){
14636         this[this.collapsed ? "expand" : "collapse"]();
14637     },
14638
14639     /**
14640      * Collapses the dialog to its minimized state (only the title bar is visible).
14641      * Equivalent to the user clicking the collapse dialog button.
14642      */
14643     collapse : function(){
14644         if(!this.collapsed){
14645             this.collapsed = true;
14646             this.el.addClass("x-dlg-collapsed");
14647             this.restoreHeight = this.el.getHeight();
14648             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14649         }
14650     },
14651
14652     /**
14653      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14654      * clicking the expand dialog button.
14655      */
14656     expand : function(){
14657         if(this.collapsed){
14658             this.collapsed = false;
14659             this.el.removeClass("x-dlg-collapsed");
14660             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14661         }
14662     },
14663
14664     /**
14665      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14666      * @return {Roo.TabPanel} The tabs component
14667      */
14668     initTabs : function(){
14669         var tabs = this.getTabs();
14670         while(tabs.getTab(0)){
14671             tabs.removeTab(0);
14672         }
14673         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14674             var dom = el.dom;
14675             tabs.addTab(Roo.id(dom), dom.title);
14676             dom.title = "";
14677         });
14678         tabs.activate(0);
14679         return tabs;
14680     },
14681
14682     // private
14683     beforeResize : function(){
14684         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14685     },
14686
14687     // private
14688     onResize : function(){
14689         this.refreshSize();
14690         this.syncBodyHeight();
14691         this.adjustAssets();
14692         this.focus();
14693         this.fireEvent("resize", this, this.size.width, this.size.height);
14694     },
14695
14696     // private
14697     onKeyDown : function(e){
14698         if(this.isVisible()){
14699             this.fireEvent("keydown", this, e);
14700         }
14701     },
14702
14703     /**
14704      * Resizes the dialog.
14705      * @param {Number} width
14706      * @param {Number} height
14707      * @return {Roo.BasicDialog} this
14708      */
14709     resizeTo : function(width, height){
14710         this.el.setSize(width, height);
14711         this.size = {width: width, height: height};
14712         this.syncBodyHeight();
14713         if(this.fixedcenter){
14714             this.center();
14715         }
14716         if(this.isVisible()){
14717             this.constrainXY();
14718             this.adjustAssets();
14719         }
14720         this.fireEvent("resize", this, width, height);
14721         return this;
14722     },
14723
14724
14725     /**
14726      * Resizes the dialog to fit the specified content size.
14727      * @param {Number} width
14728      * @param {Number} height
14729      * @return {Roo.BasicDialog} this
14730      */
14731     setContentSize : function(w, h){
14732         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14733         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14734         //if(!this.el.isBorderBox()){
14735             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14736             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14737         //}
14738         if(this.tabs){
14739             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14740             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14741         }
14742         this.resizeTo(w, h);
14743         return this;
14744     },
14745
14746     /**
14747      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14748      * executed in response to a particular key being pressed while the dialog is active.
14749      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14750      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14751      * @param {Function} fn The function to call
14752      * @param {Object} scope (optional) The scope of the function
14753      * @return {Roo.BasicDialog} this
14754      */
14755     addKeyListener : function(key, fn, scope){
14756         var keyCode, shift, ctrl, alt;
14757         if(typeof key == "object" && !(key instanceof Array)){
14758             keyCode = key["key"];
14759             shift = key["shift"];
14760             ctrl = key["ctrl"];
14761             alt = key["alt"];
14762         }else{
14763             keyCode = key;
14764         }
14765         var handler = function(dlg, e){
14766             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14767                 var k = e.getKey();
14768                 if(keyCode instanceof Array){
14769                     for(var i = 0, len = keyCode.length; i < len; i++){
14770                         if(keyCode[i] == k){
14771                           fn.call(scope || window, dlg, k, e);
14772                           return;
14773                         }
14774                     }
14775                 }else{
14776                     if(k == keyCode){
14777                         fn.call(scope || window, dlg, k, e);
14778                     }
14779                 }
14780             }
14781         };
14782         this.on("keydown", handler);
14783         return this;
14784     },
14785
14786     /**
14787      * Returns the TabPanel component (creates it if it doesn't exist).
14788      * Note: If you wish to simply check for the existence of tabs without creating them,
14789      * check for a null 'tabs' property.
14790      * @return {Roo.TabPanel} The tabs component
14791      */
14792     getTabs : function(){
14793         if(!this.tabs){
14794             this.el.addClass("x-dlg-auto-tabs");
14795             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14796             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14797         }
14798         return this.tabs;
14799     },
14800
14801     /**
14802      * Adds a button to the footer section of the dialog.
14803      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14804      * object or a valid Roo.DomHelper element config
14805      * @param {Function} handler The function called when the button is clicked
14806      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14807      * @return {Roo.Button} The new button
14808      */
14809     addButton : function(config, handler, scope){
14810         var dh = Roo.DomHelper;
14811         if(!this.footer){
14812             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14813         }
14814         if(!this.btnContainer){
14815             var tb = this.footer.createChild({
14816
14817                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14818                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14819             }, null, true);
14820             this.btnContainer = tb.firstChild.firstChild.firstChild;
14821         }
14822         var bconfig = {
14823             handler: handler,
14824             scope: scope,
14825             minWidth: this.minButtonWidth,
14826             hideParent:true
14827         };
14828         if(typeof config == "string"){
14829             bconfig.text = config;
14830         }else{
14831             if(config.tag){
14832                 bconfig.dhconfig = config;
14833             }else{
14834                 Roo.apply(bconfig, config);
14835             }
14836         }
14837         var fc = false;
14838         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14839             bconfig.position = Math.max(0, bconfig.position);
14840             fc = this.btnContainer.childNodes[bconfig.position];
14841         }
14842          
14843         var btn = new Roo.Button(
14844             fc ? 
14845                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14846                 : this.btnContainer.appendChild(document.createElement("td")),
14847             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14848             bconfig
14849         );
14850         this.syncBodyHeight();
14851         if(!this.buttons){
14852             /**
14853              * Array of all the buttons that have been added to this dialog via addButton
14854              * @type Array
14855              */
14856             this.buttons = [];
14857         }
14858         this.buttons.push(btn);
14859         return btn;
14860     },
14861
14862     /**
14863      * Sets the default button to be focused when the dialog is displayed.
14864      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14865      * @return {Roo.BasicDialog} this
14866      */
14867     setDefaultButton : function(btn){
14868         this.defaultButton = btn;
14869         return this;
14870     },
14871
14872     // private
14873     getHeaderFooterHeight : function(safe){
14874         var height = 0;
14875         if(this.header){
14876            height += this.header.getHeight();
14877         }
14878         if(this.footer){
14879            var fm = this.footer.getMargins();
14880             height += (this.footer.getHeight()+fm.top+fm.bottom);
14881         }
14882         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14883         height += this.centerBg.getPadding("tb");
14884         return height;
14885     },
14886
14887     // private
14888     syncBodyHeight : function(){
14889         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14890         var height = this.size.height - this.getHeaderFooterHeight(false);
14891         bd.setHeight(height-bd.getMargins("tb"));
14892         var hh = this.header.getHeight();
14893         var h = this.size.height-hh;
14894         cb.setHeight(h);
14895         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14896         bw.setHeight(h-cb.getPadding("tb"));
14897         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14898         bd.setWidth(bw.getWidth(true));
14899         if(this.tabs){
14900             this.tabs.syncHeight();
14901             if(Roo.isIE){
14902                 this.tabs.el.repaint();
14903             }
14904         }
14905     },
14906
14907     /**
14908      * Restores the previous state of the dialog if Roo.state is configured.
14909      * @return {Roo.BasicDialog} this
14910      */
14911     restoreState : function(){
14912         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14913         if(box && box.width){
14914             this.xy = [box.x, box.y];
14915             this.resizeTo(box.width, box.height);
14916         }
14917         return this;
14918     },
14919
14920     // private
14921     beforeShow : function(){
14922         this.expand();
14923         if(this.fixedcenter){
14924             this.xy = this.el.getCenterXY(true);
14925         }
14926         if(this.modal){
14927             Roo.get(document.body).addClass("x-body-masked");
14928             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14929             this.mask.show();
14930         }
14931         this.constrainXY();
14932     },
14933
14934     // private
14935     animShow : function(){
14936         var b = Roo.get(this.animateTarget).getBox();
14937         this.proxy.setSize(b.width, b.height);
14938         this.proxy.setLocation(b.x, b.y);
14939         this.proxy.show();
14940         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14941                     true, .35, this.showEl.createDelegate(this));
14942     },
14943
14944     /**
14945      * Shows the dialog.
14946      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14947      * @return {Roo.BasicDialog} this
14948      */
14949     show : function(animateTarget){
14950         if (this.fireEvent("beforeshow", this) === false){
14951             return;
14952         }
14953         if(this.syncHeightBeforeShow){
14954             this.syncBodyHeight();
14955         }else if(this.firstShow){
14956             this.firstShow = false;
14957             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14958         }
14959         this.animateTarget = animateTarget || this.animateTarget;
14960         if(!this.el.isVisible()){
14961             this.beforeShow();
14962             if(this.animateTarget && Roo.get(this.animateTarget)){
14963                 this.animShow();
14964             }else{
14965                 this.showEl();
14966             }
14967         }
14968         return this;
14969     },
14970
14971     // private
14972     showEl : function(){
14973         this.proxy.hide();
14974         this.el.setXY(this.xy);
14975         this.el.show();
14976         this.adjustAssets(true);
14977         this.toFront();
14978         this.focus();
14979         // IE peekaboo bug - fix found by Dave Fenwick
14980         if(Roo.isIE){
14981             this.el.repaint();
14982         }
14983         this.fireEvent("show", this);
14984     },
14985
14986     /**
14987      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14988      * dialog itself will receive focus.
14989      */
14990     focus : function(){
14991         if(this.defaultButton){
14992             this.defaultButton.focus();
14993         }else{
14994             this.focusEl.focus();
14995         }
14996     },
14997
14998     // private
14999     constrainXY : function(){
15000         if(this.constraintoviewport !== false){
15001             if(!this.viewSize){
15002                 if(this.container){
15003                     var s = this.container.getSize();
15004                     this.viewSize = [s.width, s.height];
15005                 }else{
15006                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15007                 }
15008             }
15009             var s = Roo.get(this.container||document).getScroll();
15010
15011             var x = this.xy[0], y = this.xy[1];
15012             var w = this.size.width, h = this.size.height;
15013             var vw = this.viewSize[0], vh = this.viewSize[1];
15014             // only move it if it needs it
15015             var moved = false;
15016             // first validate right/bottom
15017             if(x + w > vw+s.left){
15018                 x = vw - w;
15019                 moved = true;
15020             }
15021             if(y + h > vh+s.top){
15022                 y = vh - h;
15023                 moved = true;
15024             }
15025             // then make sure top/left isn't negative
15026             if(x < s.left){
15027                 x = s.left;
15028                 moved = true;
15029             }
15030             if(y < s.top){
15031                 y = s.top;
15032                 moved = true;
15033             }
15034             if(moved){
15035                 // cache xy
15036                 this.xy = [x, y];
15037                 if(this.isVisible()){
15038                     this.el.setLocation(x, y);
15039                     this.adjustAssets();
15040                 }
15041             }
15042         }
15043     },
15044
15045     // private
15046     onDrag : function(){
15047         if(!this.proxyDrag){
15048             this.xy = this.el.getXY();
15049             this.adjustAssets();
15050         }
15051     },
15052
15053     // private
15054     adjustAssets : function(doShow){
15055         var x = this.xy[0], y = this.xy[1];
15056         var w = this.size.width, h = this.size.height;
15057         if(doShow === true){
15058             if(this.shadow){
15059                 this.shadow.show(this.el);
15060             }
15061             if(this.shim){
15062                 this.shim.show();
15063             }
15064         }
15065         if(this.shadow && this.shadow.isVisible()){
15066             this.shadow.show(this.el);
15067         }
15068         if(this.shim && this.shim.isVisible()){
15069             this.shim.setBounds(x, y, w, h);
15070         }
15071     },
15072
15073     // private
15074     adjustViewport : function(w, h){
15075         if(!w || !h){
15076             w = Roo.lib.Dom.getViewWidth();
15077             h = Roo.lib.Dom.getViewHeight();
15078         }
15079         // cache the size
15080         this.viewSize = [w, h];
15081         if(this.modal && this.mask.isVisible()){
15082             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15083             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15084         }
15085         if(this.isVisible()){
15086             this.constrainXY();
15087         }
15088     },
15089
15090     /**
15091      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15092      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15093      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15094      */
15095     destroy : function(removeEl){
15096         if(this.isVisible()){
15097             this.animateTarget = null;
15098             this.hide();
15099         }
15100         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15101         if(this.tabs){
15102             this.tabs.destroy(removeEl);
15103         }
15104         Roo.destroy(
15105              this.shim,
15106              this.proxy,
15107              this.resizer,
15108              this.close,
15109              this.mask
15110         );
15111         if(this.dd){
15112             this.dd.unreg();
15113         }
15114         if(this.buttons){
15115            for(var i = 0, len = this.buttons.length; i < len; i++){
15116                this.buttons[i].destroy();
15117            }
15118         }
15119         this.el.removeAllListeners();
15120         if(removeEl === true){
15121             this.el.update("");
15122             this.el.remove();
15123         }
15124         Roo.DialogManager.unregister(this);
15125     },
15126
15127     // private
15128     startMove : function(){
15129         if(this.proxyDrag){
15130             this.proxy.show();
15131         }
15132         if(this.constraintoviewport !== false){
15133             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15134         }
15135     },
15136
15137     // private
15138     endMove : function(){
15139         if(!this.proxyDrag){
15140             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15141         }else{
15142             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15143             this.proxy.hide();
15144         }
15145         this.refreshSize();
15146         this.adjustAssets();
15147         this.focus();
15148         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15149     },
15150
15151     /**
15152      * Brings this dialog to the front of any other visible dialogs
15153      * @return {Roo.BasicDialog} this
15154      */
15155     toFront : function(){
15156         Roo.DialogManager.bringToFront(this);
15157         return this;
15158     },
15159
15160     /**
15161      * Sends this dialog to the back (under) of any other visible dialogs
15162      * @return {Roo.BasicDialog} this
15163      */
15164     toBack : function(){
15165         Roo.DialogManager.sendToBack(this);
15166         return this;
15167     },
15168
15169     /**
15170      * Centers this dialog in the viewport
15171      * @return {Roo.BasicDialog} this
15172      */
15173     center : function(){
15174         var xy = this.el.getCenterXY(true);
15175         this.moveTo(xy[0], xy[1]);
15176         return this;
15177     },
15178
15179     /**
15180      * Moves the dialog's top-left corner to the specified point
15181      * @param {Number} x
15182      * @param {Number} y
15183      * @return {Roo.BasicDialog} this
15184      */
15185     moveTo : function(x, y){
15186         this.xy = [x,y];
15187         if(this.isVisible()){
15188             this.el.setXY(this.xy);
15189             this.adjustAssets();
15190         }
15191         return this;
15192     },
15193
15194     /**
15195      * Aligns the dialog to the specified element
15196      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15197      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15198      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15199      * @return {Roo.BasicDialog} this
15200      */
15201     alignTo : function(element, position, offsets){
15202         this.xy = this.el.getAlignToXY(element, position, offsets);
15203         if(this.isVisible()){
15204             this.el.setXY(this.xy);
15205             this.adjustAssets();
15206         }
15207         return this;
15208     },
15209
15210     /**
15211      * Anchors an element to another element and realigns it when the window is resized.
15212      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15213      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15214      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15215      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15216      * is a number, it is used as the buffer delay (defaults to 50ms).
15217      * @return {Roo.BasicDialog} this
15218      */
15219     anchorTo : function(el, alignment, offsets, monitorScroll){
15220         var action = function(){
15221             this.alignTo(el, alignment, offsets);
15222         };
15223         Roo.EventManager.onWindowResize(action, this);
15224         var tm = typeof monitorScroll;
15225         if(tm != 'undefined'){
15226             Roo.EventManager.on(window, 'scroll', action, this,
15227                 {buffer: tm == 'number' ? monitorScroll : 50});
15228         }
15229         action.call(this);
15230         return this;
15231     },
15232
15233     /**
15234      * Returns true if the dialog is visible
15235      * @return {Boolean}
15236      */
15237     isVisible : function(){
15238         return this.el.isVisible();
15239     },
15240
15241     // private
15242     animHide : function(callback){
15243         var b = Roo.get(this.animateTarget).getBox();
15244         this.proxy.show();
15245         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15246         this.el.hide();
15247         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15248                     this.hideEl.createDelegate(this, [callback]));
15249     },
15250
15251     /**
15252      * Hides the dialog.
15253      * @param {Function} callback (optional) Function to call when the dialog is hidden
15254      * @return {Roo.BasicDialog} this
15255      */
15256     hide : function(callback){
15257         if (this.fireEvent("beforehide", this) === false){
15258             return;
15259         }
15260         if(this.shadow){
15261             this.shadow.hide();
15262         }
15263         if(this.shim) {
15264           this.shim.hide();
15265         }
15266         // sometimes animateTarget seems to get set.. causing problems...
15267         // this just double checks..
15268         if(this.animateTarget && Roo.get(this.animateTarget)) {
15269            this.animHide(callback);
15270         }else{
15271             this.el.hide();
15272             this.hideEl(callback);
15273         }
15274         return this;
15275     },
15276
15277     // private
15278     hideEl : function(callback){
15279         this.proxy.hide();
15280         if(this.modal){
15281             this.mask.hide();
15282             Roo.get(document.body).removeClass("x-body-masked");
15283         }
15284         this.fireEvent("hide", this);
15285         if(typeof callback == "function"){
15286             callback();
15287         }
15288     },
15289
15290     // private
15291     hideAction : function(){
15292         this.setLeft("-10000px");
15293         this.setTop("-10000px");
15294         this.setStyle("visibility", "hidden");
15295     },
15296
15297     // private
15298     refreshSize : function(){
15299         this.size = this.el.getSize();
15300         this.xy = this.el.getXY();
15301         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15302     },
15303
15304     // private
15305     // z-index is managed by the DialogManager and may be overwritten at any time
15306     setZIndex : function(index){
15307         if(this.modal){
15308             this.mask.setStyle("z-index", index);
15309         }
15310         if(this.shim){
15311             this.shim.setStyle("z-index", ++index);
15312         }
15313         if(this.shadow){
15314             this.shadow.setZIndex(++index);
15315         }
15316         this.el.setStyle("z-index", ++index);
15317         if(this.proxy){
15318             this.proxy.setStyle("z-index", ++index);
15319         }
15320         if(this.resizer){
15321             this.resizer.proxy.setStyle("z-index", ++index);
15322         }
15323
15324         this.lastZIndex = index;
15325     },
15326
15327     /**
15328      * Returns the element for this dialog
15329      * @return {Roo.Element} The underlying dialog Element
15330      */
15331     getEl : function(){
15332         return this.el;
15333     }
15334 });
15335
15336 /**
15337  * @class Roo.DialogManager
15338  * Provides global access to BasicDialogs that have been created and
15339  * support for z-indexing (layering) multiple open dialogs.
15340  */
15341 Roo.DialogManager = function(){
15342     var list = {};
15343     var accessList = [];
15344     var front = null;
15345
15346     // private
15347     var sortDialogs = function(d1, d2){
15348         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15349     };
15350
15351     // private
15352     var orderDialogs = function(){
15353         accessList.sort(sortDialogs);
15354         var seed = Roo.DialogManager.zseed;
15355         for(var i = 0, len = accessList.length; i < len; i++){
15356             var dlg = accessList[i];
15357             if(dlg){
15358                 dlg.setZIndex(seed + (i*10));
15359             }
15360         }
15361     };
15362
15363     return {
15364         /**
15365          * The starting z-index for BasicDialogs (defaults to 9000)
15366          * @type Number The z-index value
15367          */
15368         zseed : 9000,
15369
15370         // private
15371         register : function(dlg){
15372             list[dlg.id] = dlg;
15373             accessList.push(dlg);
15374         },
15375
15376         // private
15377         unregister : function(dlg){
15378             delete list[dlg.id];
15379             var i=0;
15380             var len=0;
15381             if(!accessList.indexOf){
15382                 for(  i = 0, len = accessList.length; i < len; i++){
15383                     if(accessList[i] == dlg){
15384                         accessList.splice(i, 1);
15385                         return;
15386                     }
15387                 }
15388             }else{
15389                  i = accessList.indexOf(dlg);
15390                 if(i != -1){
15391                     accessList.splice(i, 1);
15392                 }
15393             }
15394         },
15395
15396         /**
15397          * Gets a registered dialog by id
15398          * @param {String/Object} id The id of the dialog or a dialog
15399          * @return {Roo.BasicDialog} this
15400          */
15401         get : function(id){
15402             return typeof id == "object" ? id : list[id];
15403         },
15404
15405         /**
15406          * Brings the specified dialog to the front
15407          * @param {String/Object} dlg The id of the dialog or a dialog
15408          * @return {Roo.BasicDialog} this
15409          */
15410         bringToFront : function(dlg){
15411             dlg = this.get(dlg);
15412             if(dlg != front){
15413                 front = dlg;
15414                 dlg._lastAccess = new Date().getTime();
15415                 orderDialogs();
15416             }
15417             return dlg;
15418         },
15419
15420         /**
15421          * Sends the specified dialog to the back
15422          * @param {String/Object} dlg The id of the dialog or a dialog
15423          * @return {Roo.BasicDialog} this
15424          */
15425         sendToBack : function(dlg){
15426             dlg = this.get(dlg);
15427             dlg._lastAccess = -(new Date().getTime());
15428             orderDialogs();
15429             return dlg;
15430         },
15431
15432         /**
15433          * Hides all dialogs
15434          */
15435         hideAll : function(){
15436             for(var id in list){
15437                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15438                     list[id].hide();
15439                 }
15440             }
15441         }
15442     };
15443 }();
15444
15445 /**
15446  * @class Roo.LayoutDialog
15447  * @extends Roo.BasicDialog
15448  * Dialog which provides adjustments for working with a layout in a Dialog.
15449  * Add your necessary layout config options to the dialog's config.<br>
15450  * Example usage (including a nested layout):
15451  * <pre><code>
15452 if(!dialog){
15453     dialog = new Roo.LayoutDialog("download-dlg", {
15454         modal: true,
15455         width:600,
15456         height:450,
15457         shadow:true,
15458         minWidth:500,
15459         minHeight:350,
15460         autoTabs:true,
15461         proxyDrag:true,
15462         // layout config merges with the dialog config
15463         center:{
15464             tabPosition: "top",
15465             alwaysShowTabs: true
15466         }
15467     });
15468     dialog.addKeyListener(27, dialog.hide, dialog);
15469     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15470     dialog.addButton("Build It!", this.getDownload, this);
15471
15472     // we can even add nested layouts
15473     var innerLayout = new Roo.BorderLayout("dl-inner", {
15474         east: {
15475             initialSize: 200,
15476             autoScroll:true,
15477             split:true
15478         },
15479         center: {
15480             autoScroll:true
15481         }
15482     });
15483     innerLayout.beginUpdate();
15484     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15485     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15486     innerLayout.endUpdate(true);
15487
15488     var layout = dialog.getLayout();
15489     layout.beginUpdate();
15490     layout.add("center", new Roo.ContentPanel("standard-panel",
15491                         {title: "Download the Source", fitToFrame:true}));
15492     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15493                {title: "Build your own roo.js"}));
15494     layout.getRegion("center").showPanel(sp);
15495     layout.endUpdate();
15496 }
15497 </code></pre>
15498     * @constructor
15499     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15500     * @param {Object} config configuration options
15501   */
15502 Roo.LayoutDialog = function(el, cfg){
15503     
15504     var config=  cfg;
15505     if (typeof(cfg) == 'undefined') {
15506         config = Roo.apply({}, el);
15507         // not sure why we use documentElement here.. - it should always be body.
15508         // IE7 borks horribly if we use documentElement.
15509         // webkit also does not like documentElement - it creates a body element...
15510         el = Roo.get( document.body || document.documentElement ).createChild();
15511         //config.autoCreate = true;
15512     }
15513     
15514     
15515     config.autoTabs = false;
15516     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15517     this.body.setStyle({overflow:"hidden", position:"relative"});
15518     this.layout = new Roo.BorderLayout(this.body.dom, config);
15519     this.layout.monitorWindowResize = false;
15520     this.el.addClass("x-dlg-auto-layout");
15521     // fix case when center region overwrites center function
15522     this.center = Roo.BasicDialog.prototype.center;
15523     this.on("show", this.layout.layout, this.layout, true);
15524     if (config.items) {
15525         var xitems = config.items;
15526         delete config.items;
15527         Roo.each(xitems, this.addxtype, this);
15528     }
15529     
15530     
15531 };
15532 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15533     /**
15534      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15535      * @deprecated
15536      */
15537     endUpdate : function(){
15538         this.layout.endUpdate();
15539     },
15540
15541     /**
15542      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15543      *  @deprecated
15544      */
15545     beginUpdate : function(){
15546         this.layout.beginUpdate();
15547     },
15548
15549     /**
15550      * Get the BorderLayout for this dialog
15551      * @return {Roo.BorderLayout}
15552      */
15553     getLayout : function(){
15554         return this.layout;
15555     },
15556
15557     showEl : function(){
15558         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15559         if(Roo.isIE7){
15560             this.layout.layout();
15561         }
15562     },
15563
15564     // private
15565     // Use the syncHeightBeforeShow config option to control this automatically
15566     syncBodyHeight : function(){
15567         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15568         if(this.layout){this.layout.layout();}
15569     },
15570     
15571       /**
15572      * Add an xtype element (actually adds to the layout.)
15573      * @return {Object} xdata xtype object data.
15574      */
15575     
15576     addxtype : function(c) {
15577         return this.layout.addxtype(c);
15578     }
15579 });/*
15580  * Based on:
15581  * Ext JS Library 1.1.1
15582  * Copyright(c) 2006-2007, Ext JS, LLC.
15583  *
15584  * Originally Released Under LGPL - original licence link has changed is not relivant.
15585  *
15586  * Fork - LGPL
15587  * <script type="text/javascript">
15588  */
15589  
15590 /**
15591  * @class Roo.MessageBox
15592  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15593  * Example usage:
15594  *<pre><code>
15595 // Basic alert:
15596 Roo.Msg.alert('Status', 'Changes saved successfully.');
15597
15598 // Prompt for user data:
15599 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15600     if (btn == 'ok'){
15601         // process text value...
15602     }
15603 });
15604
15605 // Show a dialog using config options:
15606 Roo.Msg.show({
15607    title:'Save Changes?',
15608    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15609    buttons: Roo.Msg.YESNOCANCEL,
15610    fn: processResult,
15611    animEl: 'elId'
15612 });
15613 </code></pre>
15614  * @singleton
15615  */
15616 Roo.MessageBox = function(){
15617     var dlg, opt, mask, waitTimer;
15618     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15619     var buttons, activeTextEl, bwidth;
15620
15621     // private
15622     var handleButton = function(button){
15623         dlg.hide();
15624         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15625     };
15626
15627     // private
15628     var handleHide = function(){
15629         if(opt && opt.cls){
15630             dlg.el.removeClass(opt.cls);
15631         }
15632         if(waitTimer){
15633             Roo.TaskMgr.stop(waitTimer);
15634             waitTimer = null;
15635         }
15636     };
15637
15638     // private
15639     var updateButtons = function(b){
15640         var width = 0;
15641         if(!b){
15642             buttons["ok"].hide();
15643             buttons["cancel"].hide();
15644             buttons["yes"].hide();
15645             buttons["no"].hide();
15646             dlg.footer.dom.style.display = 'none';
15647             return width;
15648         }
15649         dlg.footer.dom.style.display = '';
15650         for(var k in buttons){
15651             if(typeof buttons[k] != "function"){
15652                 if(b[k]){
15653                     buttons[k].show();
15654                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15655                     width += buttons[k].el.getWidth()+15;
15656                 }else{
15657                     buttons[k].hide();
15658                 }
15659             }
15660         }
15661         return width;
15662     };
15663
15664     // private
15665     var handleEsc = function(d, k, e){
15666         if(opt && opt.closable !== false){
15667             dlg.hide();
15668         }
15669         if(e){
15670             e.stopEvent();
15671         }
15672     };
15673
15674     return {
15675         /**
15676          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15677          * @return {Roo.BasicDialog} The BasicDialog element
15678          */
15679         getDialog : function(){
15680            if(!dlg){
15681                 dlg = new Roo.BasicDialog("x-msg-box", {
15682                     autoCreate : true,
15683                     shadow: true,
15684                     draggable: true,
15685                     resizable:false,
15686                     constraintoviewport:false,
15687                     fixedcenter:true,
15688                     collapsible : false,
15689                     shim:true,
15690                     modal: true,
15691                     width:400, height:100,
15692                     buttonAlign:"center",
15693                     closeClick : function(){
15694                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15695                             handleButton("no");
15696                         }else{
15697                             handleButton("cancel");
15698                         }
15699                     }
15700                 });
15701                 dlg.on("hide", handleHide);
15702                 mask = dlg.mask;
15703                 dlg.addKeyListener(27, handleEsc);
15704                 buttons = {};
15705                 var bt = this.buttonText;
15706                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15707                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15708                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15709                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15710                 bodyEl = dlg.body.createChild({
15711
15712                     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>'
15713                 });
15714                 msgEl = bodyEl.dom.firstChild;
15715                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15716                 textboxEl.enableDisplayMode();
15717                 textboxEl.addKeyListener([10,13], function(){
15718                     if(dlg.isVisible() && opt && opt.buttons){
15719                         if(opt.buttons.ok){
15720                             handleButton("ok");
15721                         }else if(opt.buttons.yes){
15722                             handleButton("yes");
15723                         }
15724                     }
15725                 });
15726                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15727                 textareaEl.enableDisplayMode();
15728                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15729                 progressEl.enableDisplayMode();
15730                 var pf = progressEl.dom.firstChild;
15731                 if (pf) {
15732                     pp = Roo.get(pf.firstChild);
15733                     pp.setHeight(pf.offsetHeight);
15734                 }
15735                 
15736             }
15737             return dlg;
15738         },
15739
15740         /**
15741          * Updates the message box body text
15742          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15743          * the XHTML-compliant non-breaking space character '&amp;#160;')
15744          * @return {Roo.MessageBox} This message box
15745          */
15746         updateText : function(text){
15747             if(!dlg.isVisible() && !opt.width){
15748                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15749             }
15750             msgEl.innerHTML = text || '&#160;';
15751       
15752             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15753             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15754             var w = Math.max(
15755                     Math.min(opt.width || cw , this.maxWidth), 
15756                     Math.max(opt.minWidth || this.minWidth, bwidth)
15757             );
15758             if(opt.prompt){
15759                 activeTextEl.setWidth(w);
15760             }
15761             if(dlg.isVisible()){
15762                 dlg.fixedcenter = false;
15763             }
15764             // to big, make it scroll. = But as usual stupid IE does not support
15765             // !important..
15766             
15767             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15768                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15769                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15770             } else {
15771                 bodyEl.dom.style.height = '';
15772                 bodyEl.dom.style.overflowY = '';
15773             }
15774             if (cw > w) {
15775                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15776             } else {
15777                 bodyEl.dom.style.overflowX = '';
15778             }
15779             
15780             dlg.setContentSize(w, bodyEl.getHeight());
15781             if(dlg.isVisible()){
15782                 dlg.fixedcenter = true;
15783             }
15784             return this;
15785         },
15786
15787         /**
15788          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15789          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15790          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15791          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15792          * @return {Roo.MessageBox} This message box
15793          */
15794         updateProgress : function(value, text){
15795             if(text){
15796                 this.updateText(text);
15797             }
15798             if (pp) { // weird bug on my firefox - for some reason this is not defined
15799                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15800             }
15801             return this;
15802         },        
15803
15804         /**
15805          * Returns true if the message box is currently displayed
15806          * @return {Boolean} True if the message box is visible, else false
15807          */
15808         isVisible : function(){
15809             return dlg && dlg.isVisible();  
15810         },
15811
15812         /**
15813          * Hides the message box if it is displayed
15814          */
15815         hide : function(){
15816             if(this.isVisible()){
15817                 dlg.hide();
15818             }  
15819         },
15820
15821         /**
15822          * Displays a new message box, or reinitializes an existing message box, based on the config options
15823          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15824          * The following config object properties are supported:
15825          * <pre>
15826 Property    Type             Description
15827 ----------  ---------------  ------------------------------------------------------------------------------------
15828 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15829                                    closes (defaults to undefined)
15830 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15831                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15832 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15833                                    progress and wait dialogs will ignore this property and always hide the
15834                                    close button as they can only be closed programmatically.
15835 cls               String           A custom CSS class to apply to the message box element
15836 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15837                                    displayed (defaults to 75)
15838 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15839                                    function will be btn (the name of the button that was clicked, if applicable,
15840                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15841                                    Progress and wait dialogs will ignore this option since they do not respond to
15842                                    user actions and can only be closed programmatically, so any required function
15843                                    should be called by the same code after it closes the dialog.
15844 icon              String           A CSS class that provides a background image to be used as an icon for
15845                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15846 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15847 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15848 modal             Boolean          False to allow user interaction with the page while the message box is
15849                                    displayed (defaults to true)
15850 msg               String           A string that will replace the existing message box body text (defaults
15851                                    to the XHTML-compliant non-breaking space character '&#160;')
15852 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15853 progress          Boolean          True to display a progress bar (defaults to false)
15854 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15855 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15856 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15857 title             String           The title text
15858 value             String           The string value to set into the active textbox element if displayed
15859 wait              Boolean          True to display a progress bar (defaults to false)
15860 width             Number           The width of the dialog in pixels
15861 </pre>
15862          *
15863          * Example usage:
15864          * <pre><code>
15865 Roo.Msg.show({
15866    title: 'Address',
15867    msg: 'Please enter your address:',
15868    width: 300,
15869    buttons: Roo.MessageBox.OKCANCEL,
15870    multiline: true,
15871    fn: saveAddress,
15872    animEl: 'addAddressBtn'
15873 });
15874 </code></pre>
15875          * @param {Object} config Configuration options
15876          * @return {Roo.MessageBox} This message box
15877          */
15878         show : function(options)
15879         {
15880             
15881             // this causes nightmares if you show one dialog after another
15882             // especially on callbacks..
15883              
15884             if(this.isVisible()){
15885                 
15886                 this.hide();
15887                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15888                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15889                 Roo.log("New Dialog Message:" +  options.msg )
15890                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15891                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15892                 
15893             }
15894             var d = this.getDialog();
15895             opt = options;
15896             d.setTitle(opt.title || "&#160;");
15897             d.close.setDisplayed(opt.closable !== false);
15898             activeTextEl = textboxEl;
15899             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15900             if(opt.prompt){
15901                 if(opt.multiline){
15902                     textboxEl.hide();
15903                     textareaEl.show();
15904                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15905                         opt.multiline : this.defaultTextHeight);
15906                     activeTextEl = textareaEl;
15907                 }else{
15908                     textboxEl.show();
15909                     textareaEl.hide();
15910                 }
15911             }else{
15912                 textboxEl.hide();
15913                 textareaEl.hide();
15914             }
15915             progressEl.setDisplayed(opt.progress === true);
15916             this.updateProgress(0);
15917             activeTextEl.dom.value = opt.value || "";
15918             if(opt.prompt){
15919                 dlg.setDefaultButton(activeTextEl);
15920             }else{
15921                 var bs = opt.buttons;
15922                 var db = null;
15923                 if(bs && bs.ok){
15924                     db = buttons["ok"];
15925                 }else if(bs && bs.yes){
15926                     db = buttons["yes"];
15927                 }
15928                 dlg.setDefaultButton(db);
15929             }
15930             bwidth = updateButtons(opt.buttons);
15931             this.updateText(opt.msg);
15932             if(opt.cls){
15933                 d.el.addClass(opt.cls);
15934             }
15935             d.proxyDrag = opt.proxyDrag === true;
15936             d.modal = opt.modal !== false;
15937             d.mask = opt.modal !== false ? mask : false;
15938             if(!d.isVisible()){
15939                 // force it to the end of the z-index stack so it gets a cursor in FF
15940                 document.body.appendChild(dlg.el.dom);
15941                 d.animateTarget = null;
15942                 d.show(options.animEl);
15943             }
15944             return this;
15945         },
15946
15947         /**
15948          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15949          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15950          * and closing the message box when the process is complete.
15951          * @param {String} title The title bar text
15952          * @param {String} msg The message box body text
15953          * @return {Roo.MessageBox} This message box
15954          */
15955         progress : function(title, msg){
15956             this.show({
15957                 title : title,
15958                 msg : msg,
15959                 buttons: false,
15960                 progress:true,
15961                 closable:false,
15962                 minWidth: this.minProgressWidth,
15963                 modal : true
15964             });
15965             return this;
15966         },
15967
15968         /**
15969          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15970          * If a callback function is passed it will be called after the user clicks the button, and the
15971          * id of the button that was clicked will be passed as the only parameter to the callback
15972          * (could also be the top-right close button).
15973          * @param {String} title The title bar text
15974          * @param {String} msg The message box body text
15975          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15976          * @param {Object} scope (optional) The scope of the callback function
15977          * @return {Roo.MessageBox} This message box
15978          */
15979         alert : function(title, msg, fn, scope){
15980             this.show({
15981                 title : title,
15982                 msg : msg,
15983                 buttons: this.OK,
15984                 fn: fn,
15985                 scope : scope,
15986                 modal : true
15987             });
15988             return this;
15989         },
15990
15991         /**
15992          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15993          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15994          * You are responsible for closing the message box when the process is complete.
15995          * @param {String} msg The message box body text
15996          * @param {String} title (optional) The title bar text
15997          * @return {Roo.MessageBox} This message box
15998          */
15999         wait : function(msg, title){
16000             this.show({
16001                 title : title,
16002                 msg : msg,
16003                 buttons: false,
16004                 closable:false,
16005                 progress:true,
16006                 modal:true,
16007                 width:300,
16008                 wait:true
16009             });
16010             waitTimer = Roo.TaskMgr.start({
16011                 run: function(i){
16012                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16013                 },
16014                 interval: 1000
16015             });
16016             return this;
16017         },
16018
16019         /**
16020          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16021          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16022          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16023          * @param {String} title The title bar text
16024          * @param {String} msg The message box body text
16025          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16026          * @param {Object} scope (optional) The scope of the callback function
16027          * @return {Roo.MessageBox} This message box
16028          */
16029         confirm : function(title, msg, fn, scope){
16030             this.show({
16031                 title : title,
16032                 msg : msg,
16033                 buttons: this.YESNO,
16034                 fn: fn,
16035                 scope : scope,
16036                 modal : true
16037             });
16038             return this;
16039         },
16040
16041         /**
16042          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16043          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16044          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16045          * (could also be the top-right close button) and the text that was entered will be passed as the two
16046          * parameters to the callback.
16047          * @param {String} title The title bar text
16048          * @param {String} msg The message box body text
16049          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16050          * @param {Object} scope (optional) The scope of the callback function
16051          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16052          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16053          * @return {Roo.MessageBox} This message box
16054          */
16055         prompt : function(title, msg, fn, scope, multiline){
16056             this.show({
16057                 title : title,
16058                 msg : msg,
16059                 buttons: this.OKCANCEL,
16060                 fn: fn,
16061                 minWidth:250,
16062                 scope : scope,
16063                 prompt:true,
16064                 multiline: multiline,
16065                 modal : true
16066             });
16067             return this;
16068         },
16069
16070         /**
16071          * Button config that displays a single OK button
16072          * @type Object
16073          */
16074         OK : {ok:true},
16075         /**
16076          * Button config that displays Yes and No buttons
16077          * @type Object
16078          */
16079         YESNO : {yes:true, no:true},
16080         /**
16081          * Button config that displays OK and Cancel buttons
16082          * @type Object
16083          */
16084         OKCANCEL : {ok:true, cancel:true},
16085         /**
16086          * Button config that displays Yes, No and Cancel buttons
16087          * @type Object
16088          */
16089         YESNOCANCEL : {yes:true, no:true, cancel:true},
16090
16091         /**
16092          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16093          * @type Number
16094          */
16095         defaultTextHeight : 75,
16096         /**
16097          * The maximum width in pixels of the message box (defaults to 600)
16098          * @type Number
16099          */
16100         maxWidth : 600,
16101         /**
16102          * The minimum width in pixels of the message box (defaults to 100)
16103          * @type Number
16104          */
16105         minWidth : 100,
16106         /**
16107          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16108          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16109          * @type Number
16110          */
16111         minProgressWidth : 250,
16112         /**
16113          * An object containing the default button text strings that can be overriden for localized language support.
16114          * Supported properties are: ok, cancel, yes and no.
16115          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16116          * @type Object
16117          */
16118         buttonText : {
16119             ok : "OK",
16120             cancel : "Cancel",
16121             yes : "Yes",
16122             no : "No"
16123         }
16124     };
16125 }();
16126
16127 /**
16128  * Shorthand for {@link Roo.MessageBox}
16129  */
16130 Roo.Msg = Roo.MessageBox;/*
16131  * Based on:
16132  * Ext JS Library 1.1.1
16133  * Copyright(c) 2006-2007, Ext JS, LLC.
16134  *
16135  * Originally Released Under LGPL - original licence link has changed is not relivant.
16136  *
16137  * Fork - LGPL
16138  * <script type="text/javascript">
16139  */
16140 /**
16141  * @class Roo.QuickTips
16142  * Provides attractive and customizable tooltips for any element.
16143  * @singleton
16144  */
16145 Roo.QuickTips = function(){
16146     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16147     var ce, bd, xy, dd;
16148     var visible = false, disabled = true, inited = false;
16149     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16150     
16151     var onOver = function(e){
16152         if(disabled){
16153             return;
16154         }
16155         var t = e.getTarget();
16156         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16157             return;
16158         }
16159         if(ce && t == ce.el){
16160             clearTimeout(hideProc);
16161             return;
16162         }
16163         if(t && tagEls[t.id]){
16164             tagEls[t.id].el = t;
16165             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16166             return;
16167         }
16168         var ttp, et = Roo.fly(t);
16169         var ns = cfg.namespace;
16170         if(tm.interceptTitles && t.title){
16171             ttp = t.title;
16172             t.qtip = ttp;
16173             t.removeAttribute("title");
16174             e.preventDefault();
16175         }else{
16176             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16177         }
16178         if(ttp){
16179             showProc = show.defer(tm.showDelay, tm, [{
16180                 el: t, 
16181                 text: ttp, 
16182                 width: et.getAttributeNS(ns, cfg.width),
16183                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16184                 title: et.getAttributeNS(ns, cfg.title),
16185                     cls: et.getAttributeNS(ns, cfg.cls)
16186             }]);
16187         }
16188     };
16189     
16190     var onOut = function(e){
16191         clearTimeout(showProc);
16192         var t = e.getTarget();
16193         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16194             hideProc = setTimeout(hide, tm.hideDelay);
16195         }
16196     };
16197     
16198     var onMove = function(e){
16199         if(disabled){
16200             return;
16201         }
16202         xy = e.getXY();
16203         xy[1] += 18;
16204         if(tm.trackMouse && ce){
16205             el.setXY(xy);
16206         }
16207     };
16208     
16209     var onDown = function(e){
16210         clearTimeout(showProc);
16211         clearTimeout(hideProc);
16212         if(!e.within(el)){
16213             if(tm.hideOnClick){
16214                 hide();
16215                 tm.disable();
16216                 tm.enable.defer(100, tm);
16217             }
16218         }
16219     };
16220     
16221     var getPad = function(){
16222         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16223     };
16224
16225     var show = function(o){
16226         if(disabled){
16227             return;
16228         }
16229         clearTimeout(dismissProc);
16230         ce = o;
16231         if(removeCls){ // in case manually hidden
16232             el.removeClass(removeCls);
16233             removeCls = null;
16234         }
16235         if(ce.cls){
16236             el.addClass(ce.cls);
16237             removeCls = ce.cls;
16238         }
16239         if(ce.title){
16240             tipTitle.update(ce.title);
16241             tipTitle.show();
16242         }else{
16243             tipTitle.update('');
16244             tipTitle.hide();
16245         }
16246         el.dom.style.width  = tm.maxWidth+'px';
16247         //tipBody.dom.style.width = '';
16248         tipBodyText.update(o.text);
16249         var p = getPad(), w = ce.width;
16250         if(!w){
16251             var td = tipBodyText.dom;
16252             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16253             if(aw > tm.maxWidth){
16254                 w = tm.maxWidth;
16255             }else if(aw < tm.minWidth){
16256                 w = tm.minWidth;
16257             }else{
16258                 w = aw;
16259             }
16260         }
16261         //tipBody.setWidth(w);
16262         el.setWidth(parseInt(w, 10) + p);
16263         if(ce.autoHide === false){
16264             close.setDisplayed(true);
16265             if(dd){
16266                 dd.unlock();
16267             }
16268         }else{
16269             close.setDisplayed(false);
16270             if(dd){
16271                 dd.lock();
16272             }
16273         }
16274         if(xy){
16275             el.avoidY = xy[1]-18;
16276             el.setXY(xy);
16277         }
16278         if(tm.animate){
16279             el.setOpacity(.1);
16280             el.setStyle("visibility", "visible");
16281             el.fadeIn({callback: afterShow});
16282         }else{
16283             afterShow();
16284         }
16285     };
16286     
16287     var afterShow = function(){
16288         if(ce){
16289             el.show();
16290             esc.enable();
16291             if(tm.autoDismiss && ce.autoHide !== false){
16292                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16293             }
16294         }
16295     };
16296     
16297     var hide = function(noanim){
16298         clearTimeout(dismissProc);
16299         clearTimeout(hideProc);
16300         ce = null;
16301         if(el.isVisible()){
16302             esc.disable();
16303             if(noanim !== true && tm.animate){
16304                 el.fadeOut({callback: afterHide});
16305             }else{
16306                 afterHide();
16307             } 
16308         }
16309     };
16310     
16311     var afterHide = function(){
16312         el.hide();
16313         if(removeCls){
16314             el.removeClass(removeCls);
16315             removeCls = null;
16316         }
16317     };
16318     
16319     return {
16320         /**
16321         * @cfg {Number} minWidth
16322         * The minimum width of the quick tip (defaults to 40)
16323         */
16324        minWidth : 40,
16325         /**
16326         * @cfg {Number} maxWidth
16327         * The maximum width of the quick tip (defaults to 300)
16328         */
16329        maxWidth : 300,
16330         /**
16331         * @cfg {Boolean} interceptTitles
16332         * True to automatically use the element's DOM title value if available (defaults to false)
16333         */
16334        interceptTitles : false,
16335         /**
16336         * @cfg {Boolean} trackMouse
16337         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16338         */
16339        trackMouse : false,
16340         /**
16341         * @cfg {Boolean} hideOnClick
16342         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16343         */
16344        hideOnClick : true,
16345         /**
16346         * @cfg {Number} showDelay
16347         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16348         */
16349        showDelay : 500,
16350         /**
16351         * @cfg {Number} hideDelay
16352         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16353         */
16354        hideDelay : 200,
16355         /**
16356         * @cfg {Boolean} autoHide
16357         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16358         * Used in conjunction with hideDelay.
16359         */
16360        autoHide : true,
16361         /**
16362         * @cfg {Boolean}
16363         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16364         * (defaults to true).  Used in conjunction with autoDismissDelay.
16365         */
16366        autoDismiss : true,
16367         /**
16368         * @cfg {Number}
16369         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16370         */
16371        autoDismissDelay : 5000,
16372        /**
16373         * @cfg {Boolean} animate
16374         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16375         */
16376        animate : false,
16377
16378        /**
16379         * @cfg {String} title
16380         * Title text to display (defaults to '').  This can be any valid HTML markup.
16381         */
16382         title: '',
16383        /**
16384         * @cfg {String} text
16385         * Body text to display (defaults to '').  This can be any valid HTML markup.
16386         */
16387         text : '',
16388        /**
16389         * @cfg {String} cls
16390         * A CSS class to apply to the base quick tip element (defaults to '').
16391         */
16392         cls : '',
16393        /**
16394         * @cfg {Number} width
16395         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16396         * minWidth or maxWidth.
16397         */
16398         width : null,
16399
16400     /**
16401      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16402      * or display QuickTips in a page.
16403      */
16404        init : function(){
16405           tm = Roo.QuickTips;
16406           cfg = tm.tagConfig;
16407           if(!inited){
16408               if(!Roo.isReady){ // allow calling of init() before onReady
16409                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16410                   return;
16411               }
16412               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16413               el.fxDefaults = {stopFx: true};
16414               // maximum custom styling
16415               //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>');
16416               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>');              
16417               tipTitle = el.child('h3');
16418               tipTitle.enableDisplayMode("block");
16419               tipBody = el.child('div.x-tip-bd');
16420               tipBodyText = el.child('div.x-tip-bd-inner');
16421               //bdLeft = el.child('div.x-tip-bd-left');
16422               //bdRight = el.child('div.x-tip-bd-right');
16423               close = el.child('div.x-tip-close');
16424               close.enableDisplayMode("block");
16425               close.on("click", hide);
16426               var d = Roo.get(document);
16427               d.on("mousedown", onDown);
16428               d.on("mouseover", onOver);
16429               d.on("mouseout", onOut);
16430               d.on("mousemove", onMove);
16431               esc = d.addKeyListener(27, hide);
16432               esc.disable();
16433               if(Roo.dd.DD){
16434                   dd = el.initDD("default", null, {
16435                       onDrag : function(){
16436                           el.sync();  
16437                       }
16438                   });
16439                   dd.setHandleElId(tipTitle.id);
16440                   dd.lock();
16441               }
16442               inited = true;
16443           }
16444           this.enable(); 
16445        },
16446
16447     /**
16448      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16449      * are supported:
16450      * <pre>
16451 Property    Type                   Description
16452 ----------  ---------------------  ------------------------------------------------------------------------
16453 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16454      * </ul>
16455      * @param {Object} config The config object
16456      */
16457        register : function(config){
16458            var cs = config instanceof Array ? config : arguments;
16459            for(var i = 0, len = cs.length; i < len; i++) {
16460                var c = cs[i];
16461                var target = c.target;
16462                if(target){
16463                    if(target instanceof Array){
16464                        for(var j = 0, jlen = target.length; j < jlen; j++){
16465                            tagEls[target[j]] = c;
16466                        }
16467                    }else{
16468                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16469                    }
16470                }
16471            }
16472        },
16473
16474     /**
16475      * Removes this quick tip from its element and destroys it.
16476      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16477      */
16478        unregister : function(el){
16479            delete tagEls[Roo.id(el)];
16480        },
16481
16482     /**
16483      * Enable this quick tip.
16484      */
16485        enable : function(){
16486            if(inited && disabled){
16487                locks.pop();
16488                if(locks.length < 1){
16489                    disabled = false;
16490                }
16491            }
16492        },
16493
16494     /**
16495      * Disable this quick tip.
16496      */
16497        disable : function(){
16498           disabled = true;
16499           clearTimeout(showProc);
16500           clearTimeout(hideProc);
16501           clearTimeout(dismissProc);
16502           if(ce){
16503               hide(true);
16504           }
16505           locks.push(1);
16506        },
16507
16508     /**
16509      * Returns true if the quick tip is enabled, else false.
16510      */
16511        isEnabled : function(){
16512             return !disabled;
16513        },
16514
16515         // private
16516        tagConfig : {
16517            namespace : "ext",
16518            attribute : "qtip",
16519            width : "width",
16520            target : "target",
16521            title : "qtitle",
16522            hide : "hide",
16523            cls : "qclass"
16524        }
16525    };
16526 }();
16527
16528 // backwards compat
16529 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16530  * Based on:
16531  * Ext JS Library 1.1.1
16532  * Copyright(c) 2006-2007, Ext JS, LLC.
16533  *
16534  * Originally Released Under LGPL - original licence link has changed is not relivant.
16535  *
16536  * Fork - LGPL
16537  * <script type="text/javascript">
16538  */
16539  
16540
16541 /**
16542  * @class Roo.tree.TreePanel
16543  * @extends Roo.data.Tree
16544
16545  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16546  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16547  * @cfg {Boolean} enableDD true to enable drag and drop
16548  * @cfg {Boolean} enableDrag true to enable just drag
16549  * @cfg {Boolean} enableDrop true to enable just drop
16550  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16551  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16552  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16553  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16554  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16555  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16556  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16557  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16558  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16559  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16560  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16561  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16562  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16563  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16564  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / 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>
16565  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / 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>
16566  * 
16567  * @constructor
16568  * @param {String/HTMLElement/Element} el The container element
16569  * @param {Object} config
16570  */
16571 Roo.tree.TreePanel = function(el, config){
16572     var root = false;
16573     var loader = false;
16574     if (config.root) {
16575         root = config.root;
16576         delete config.root;
16577     }
16578     if (config.loader) {
16579         loader = config.loader;
16580         delete config.loader;
16581     }
16582     
16583     Roo.apply(this, config);
16584     Roo.tree.TreePanel.superclass.constructor.call(this);
16585     this.el = Roo.get(el);
16586     this.el.addClass('x-tree');
16587     //console.log(root);
16588     if (root) {
16589         this.setRootNode( Roo.factory(root, Roo.tree));
16590     }
16591     if (loader) {
16592         this.loader = Roo.factory(loader, Roo.tree);
16593     }
16594    /**
16595     * Read-only. The id of the container element becomes this TreePanel's id.
16596     */
16597     this.id = this.el.id;
16598     this.addEvents({
16599         /**
16600         * @event beforeload
16601         * Fires before a node is loaded, return false to cancel
16602         * @param {Node} node The node being loaded
16603         */
16604         "beforeload" : true,
16605         /**
16606         * @event load
16607         * Fires when a node is loaded
16608         * @param {Node} node The node that was loaded
16609         */
16610         "load" : true,
16611         /**
16612         * @event textchange
16613         * Fires when the text for a node is changed
16614         * @param {Node} node The node
16615         * @param {String} text The new text
16616         * @param {String} oldText The old text
16617         */
16618         "textchange" : true,
16619         /**
16620         * @event beforeexpand
16621         * Fires before a node is expanded, return false to cancel.
16622         * @param {Node} node The node
16623         * @param {Boolean} deep
16624         * @param {Boolean} anim
16625         */
16626         "beforeexpand" : true,
16627         /**
16628         * @event beforecollapse
16629         * Fires before a node is collapsed, return false to cancel.
16630         * @param {Node} node The node
16631         * @param {Boolean} deep
16632         * @param {Boolean} anim
16633         */
16634         "beforecollapse" : true,
16635         /**
16636         * @event expand
16637         * Fires when a node is expanded
16638         * @param {Node} node The node
16639         */
16640         "expand" : true,
16641         /**
16642         * @event disabledchange
16643         * Fires when the disabled status of a node changes
16644         * @param {Node} node The node
16645         * @param {Boolean} disabled
16646         */
16647         "disabledchange" : true,
16648         /**
16649         * @event collapse
16650         * Fires when a node is collapsed
16651         * @param {Node} node The node
16652         */
16653         "collapse" : true,
16654         /**
16655         * @event beforeclick
16656         * Fires before click processing on a node. Return false to cancel the default action.
16657         * @param {Node} node The node
16658         * @param {Roo.EventObject} e The event object
16659         */
16660         "beforeclick":true,
16661         /**
16662         * @event checkchange
16663         * Fires when a node with a checkbox's checked property changes
16664         * @param {Node} this This node
16665         * @param {Boolean} checked
16666         */
16667         "checkchange":true,
16668         /**
16669         * @event click
16670         * Fires when a node is clicked
16671         * @param {Node} node The node
16672         * @param {Roo.EventObject} e The event object
16673         */
16674         "click":true,
16675         /**
16676         * @event dblclick
16677         * Fires when a node is double clicked
16678         * @param {Node} node The node
16679         * @param {Roo.EventObject} e The event object
16680         */
16681         "dblclick":true,
16682         /**
16683         * @event contextmenu
16684         * Fires when a node is right clicked
16685         * @param {Node} node The node
16686         * @param {Roo.EventObject} e The event object
16687         */
16688         "contextmenu":true,
16689         /**
16690         * @event beforechildrenrendered
16691         * Fires right before the child nodes for a node are rendered
16692         * @param {Node} node The node
16693         */
16694         "beforechildrenrendered":true,
16695         /**
16696         * @event startdrag
16697         * Fires when a node starts being dragged
16698         * @param {Roo.tree.TreePanel} this
16699         * @param {Roo.tree.TreeNode} node
16700         * @param {event} e The raw browser event
16701         */ 
16702        "startdrag" : true,
16703        /**
16704         * @event enddrag
16705         * Fires when a drag operation is complete
16706         * @param {Roo.tree.TreePanel} this
16707         * @param {Roo.tree.TreeNode} node
16708         * @param {event} e The raw browser event
16709         */
16710        "enddrag" : true,
16711        /**
16712         * @event dragdrop
16713         * Fires when a dragged node is dropped on a valid DD target
16714         * @param {Roo.tree.TreePanel} this
16715         * @param {Roo.tree.TreeNode} node
16716         * @param {DD} dd The dd it was dropped on
16717         * @param {event} e The raw browser event
16718         */
16719        "dragdrop" : true,
16720        /**
16721         * @event beforenodedrop
16722         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16723         * passed to handlers has the following properties:<br />
16724         * <ul style="padding:5px;padding-left:16px;">
16725         * <li>tree - The TreePanel</li>
16726         * <li>target - The node being targeted for the drop</li>
16727         * <li>data - The drag data from the drag source</li>
16728         * <li>point - The point of the drop - append, above or below</li>
16729         * <li>source - The drag source</li>
16730         * <li>rawEvent - Raw mouse event</li>
16731         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16732         * to be inserted by setting them on this object.</li>
16733         * <li>cancel - Set this to true to cancel the drop.</li>
16734         * </ul>
16735         * @param {Object} dropEvent
16736         */
16737        "beforenodedrop" : true,
16738        /**
16739         * @event nodedrop
16740         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16741         * passed to handlers has the following properties:<br />
16742         * <ul style="padding:5px;padding-left:16px;">
16743         * <li>tree - The TreePanel</li>
16744         * <li>target - The node being targeted for the drop</li>
16745         * <li>data - The drag data from the drag source</li>
16746         * <li>point - The point of the drop - append, above or below</li>
16747         * <li>source - The drag source</li>
16748         * <li>rawEvent - Raw mouse event</li>
16749         * <li>dropNode - Dropped node(s).</li>
16750         * </ul>
16751         * @param {Object} dropEvent
16752         */
16753        "nodedrop" : true,
16754         /**
16755         * @event nodedragover
16756         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16757         * passed to handlers has the following properties:<br />
16758         * <ul style="padding:5px;padding-left:16px;">
16759         * <li>tree - The TreePanel</li>
16760         * <li>target - The node being targeted for the drop</li>
16761         * <li>data - The drag data from the drag source</li>
16762         * <li>point - The point of the drop - append, above or below</li>
16763         * <li>source - The drag source</li>
16764         * <li>rawEvent - Raw mouse event</li>
16765         * <li>dropNode - Drop node(s) provided by the source.</li>
16766         * <li>cancel - Set this to true to signal drop not allowed.</li>
16767         * </ul>
16768         * @param {Object} dragOverEvent
16769         */
16770        "nodedragover" : true
16771         
16772     });
16773     if(this.singleExpand){
16774        this.on("beforeexpand", this.restrictExpand, this);
16775     }
16776     if (this.editor) {
16777         this.editor.tree = this;
16778         this.editor = Roo.factory(this.editor, Roo.tree);
16779     }
16780     
16781     if (this.selModel) {
16782         this.selModel = Roo.factory(this.selModel, Roo.tree);
16783     }
16784    
16785 };
16786 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16787     rootVisible : true,
16788     animate: Roo.enableFx,
16789     lines : true,
16790     enableDD : false,
16791     hlDrop : Roo.enableFx,
16792   
16793     renderer: false,
16794     
16795     rendererTip: false,
16796     // private
16797     restrictExpand : function(node){
16798         var p = node.parentNode;
16799         if(p){
16800             if(p.expandedChild && p.expandedChild.parentNode == p){
16801                 p.expandedChild.collapse();
16802             }
16803             p.expandedChild = node;
16804         }
16805     },
16806
16807     // private override
16808     setRootNode : function(node){
16809         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16810         if(!this.rootVisible){
16811             node.ui = new Roo.tree.RootTreeNodeUI(node);
16812         }
16813         return node;
16814     },
16815
16816     /**
16817      * Returns the container element for this TreePanel
16818      */
16819     getEl : function(){
16820         return this.el;
16821     },
16822
16823     /**
16824      * Returns the default TreeLoader for this TreePanel
16825      */
16826     getLoader : function(){
16827         return this.loader;
16828     },
16829
16830     /**
16831      * Expand all nodes
16832      */
16833     expandAll : function(){
16834         this.root.expand(true);
16835     },
16836
16837     /**
16838      * Collapse all nodes
16839      */
16840     collapseAll : function(){
16841         this.root.collapse(true);
16842     },
16843
16844     /**
16845      * Returns the selection model used by this TreePanel
16846      */
16847     getSelectionModel : function(){
16848         if(!this.selModel){
16849             this.selModel = new Roo.tree.DefaultSelectionModel();
16850         }
16851         return this.selModel;
16852     },
16853
16854     /**
16855      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16856      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16857      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16858      * @return {Array}
16859      */
16860     getChecked : function(a, startNode){
16861         startNode = startNode || this.root;
16862         var r = [];
16863         var f = function(){
16864             if(this.attributes.checked){
16865                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16866             }
16867         }
16868         startNode.cascade(f);
16869         return r;
16870     },
16871
16872     /**
16873      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16874      * @param {String} path
16875      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16876      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16877      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16878      */
16879     expandPath : function(path, attr, callback){
16880         attr = attr || "id";
16881         var keys = path.split(this.pathSeparator);
16882         var curNode = this.root;
16883         if(curNode.attributes[attr] != keys[1]){ // invalid root
16884             if(callback){
16885                 callback(false, null);
16886             }
16887             return;
16888         }
16889         var index = 1;
16890         var f = function(){
16891             if(++index == keys.length){
16892                 if(callback){
16893                     callback(true, curNode);
16894                 }
16895                 return;
16896             }
16897             var c = curNode.findChild(attr, keys[index]);
16898             if(!c){
16899                 if(callback){
16900                     callback(false, curNode);
16901                 }
16902                 return;
16903             }
16904             curNode = c;
16905             c.expand(false, false, f);
16906         };
16907         curNode.expand(false, false, f);
16908     },
16909
16910     /**
16911      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16912      * @param {String} path
16913      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16914      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16915      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16916      */
16917     selectPath : function(path, attr, callback){
16918         attr = attr || "id";
16919         var keys = path.split(this.pathSeparator);
16920         var v = keys.pop();
16921         if(keys.length > 0){
16922             var f = function(success, node){
16923                 if(success && node){
16924                     var n = node.findChild(attr, v);
16925                     if(n){
16926                         n.select();
16927                         if(callback){
16928                             callback(true, n);
16929                         }
16930                     }else if(callback){
16931                         callback(false, n);
16932                     }
16933                 }else{
16934                     if(callback){
16935                         callback(false, n);
16936                     }
16937                 }
16938             };
16939             this.expandPath(keys.join(this.pathSeparator), attr, f);
16940         }else{
16941             this.root.select();
16942             if(callback){
16943                 callback(true, this.root);
16944             }
16945         }
16946     },
16947
16948     getTreeEl : function(){
16949         return this.el;
16950     },
16951
16952     /**
16953      * Trigger rendering of this TreePanel
16954      */
16955     render : function(){
16956         if (this.innerCt) {
16957             return this; // stop it rendering more than once!!
16958         }
16959         
16960         this.innerCt = this.el.createChild({tag:"ul",
16961                cls:"x-tree-root-ct " +
16962                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16963
16964         if(this.containerScroll){
16965             Roo.dd.ScrollManager.register(this.el);
16966         }
16967         if((this.enableDD || this.enableDrop) && !this.dropZone){
16968            /**
16969             * The dropZone used by this tree if drop is enabled
16970             * @type Roo.tree.TreeDropZone
16971             */
16972              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16973                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16974            });
16975         }
16976         if((this.enableDD || this.enableDrag) && !this.dragZone){
16977            /**
16978             * The dragZone used by this tree if drag is enabled
16979             * @type Roo.tree.TreeDragZone
16980             */
16981             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16982                ddGroup: this.ddGroup || "TreeDD",
16983                scroll: this.ddScroll
16984            });
16985         }
16986         this.getSelectionModel().init(this);
16987         if (!this.root) {
16988             Roo.log("ROOT not set in tree");
16989             return this;
16990         }
16991         this.root.render();
16992         if(!this.rootVisible){
16993             this.root.renderChildren();
16994         }
16995         return this;
16996     }
16997 });/*
16998  * Based on:
16999  * Ext JS Library 1.1.1
17000  * Copyright(c) 2006-2007, Ext JS, LLC.
17001  *
17002  * Originally Released Under LGPL - original licence link has changed is not relivant.
17003  *
17004  * Fork - LGPL
17005  * <script type="text/javascript">
17006  */
17007  
17008
17009 /**
17010  * @class Roo.tree.DefaultSelectionModel
17011  * @extends Roo.util.Observable
17012  * The default single selection for a TreePanel.
17013  * @param {Object} cfg Configuration
17014  */
17015 Roo.tree.DefaultSelectionModel = function(cfg){
17016    this.selNode = null;
17017    
17018    
17019    
17020    this.addEvents({
17021        /**
17022         * @event selectionchange
17023         * Fires when the selected node changes
17024         * @param {DefaultSelectionModel} this
17025         * @param {TreeNode} node the new selection
17026         */
17027        "selectionchange" : true,
17028
17029        /**
17030         * @event beforeselect
17031         * Fires before the selected node changes, return false to cancel the change
17032         * @param {DefaultSelectionModel} this
17033         * @param {TreeNode} node the new selection
17034         * @param {TreeNode} node the old selection
17035         */
17036        "beforeselect" : true
17037    });
17038    
17039     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17040 };
17041
17042 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17043     init : function(tree){
17044         this.tree = tree;
17045         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17046         tree.on("click", this.onNodeClick, this);
17047     },
17048     
17049     onNodeClick : function(node, e){
17050         if (e.ctrlKey && this.selNode == node)  {
17051             this.unselect(node);
17052             return;
17053         }
17054         this.select(node);
17055     },
17056     
17057     /**
17058      * Select a node.
17059      * @param {TreeNode} node The node to select
17060      * @return {TreeNode} The selected node
17061      */
17062     select : function(node){
17063         var last = this.selNode;
17064         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17065             if(last){
17066                 last.ui.onSelectedChange(false);
17067             }
17068             this.selNode = node;
17069             node.ui.onSelectedChange(true);
17070             this.fireEvent("selectionchange", this, node, last);
17071         }
17072         return node;
17073     },
17074     
17075     /**
17076      * Deselect a node.
17077      * @param {TreeNode} node The node to unselect
17078      */
17079     unselect : function(node){
17080         if(this.selNode == node){
17081             this.clearSelections();
17082         }    
17083     },
17084     
17085     /**
17086      * Clear all selections
17087      */
17088     clearSelections : function(){
17089         var n = this.selNode;
17090         if(n){
17091             n.ui.onSelectedChange(false);
17092             this.selNode = null;
17093             this.fireEvent("selectionchange", this, null);
17094         }
17095         return n;
17096     },
17097     
17098     /**
17099      * Get the selected node
17100      * @return {TreeNode} The selected node
17101      */
17102     getSelectedNode : function(){
17103         return this.selNode;    
17104     },
17105     
17106     /**
17107      * Returns true if the node is selected
17108      * @param {TreeNode} node The node to check
17109      * @return {Boolean}
17110      */
17111     isSelected : function(node){
17112         return this.selNode == node;  
17113     },
17114
17115     /**
17116      * Selects the node above the selected node in the tree, intelligently walking the nodes
17117      * @return TreeNode The new selection
17118      */
17119     selectPrevious : function(){
17120         var s = this.selNode || this.lastSelNode;
17121         if(!s){
17122             return null;
17123         }
17124         var ps = s.previousSibling;
17125         if(ps){
17126             if(!ps.isExpanded() || ps.childNodes.length < 1){
17127                 return this.select(ps);
17128             } else{
17129                 var lc = ps.lastChild;
17130                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17131                     lc = lc.lastChild;
17132                 }
17133                 return this.select(lc);
17134             }
17135         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17136             return this.select(s.parentNode);
17137         }
17138         return null;
17139     },
17140
17141     /**
17142      * Selects the node above the selected node in the tree, intelligently walking the nodes
17143      * @return TreeNode The new selection
17144      */
17145     selectNext : function(){
17146         var s = this.selNode || this.lastSelNode;
17147         if(!s){
17148             return null;
17149         }
17150         if(s.firstChild && s.isExpanded()){
17151              return this.select(s.firstChild);
17152          }else if(s.nextSibling){
17153              return this.select(s.nextSibling);
17154          }else if(s.parentNode){
17155             var newS = null;
17156             s.parentNode.bubble(function(){
17157                 if(this.nextSibling){
17158                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17159                     return false;
17160                 }
17161             });
17162             return newS;
17163          }
17164         return null;
17165     },
17166
17167     onKeyDown : function(e){
17168         var s = this.selNode || this.lastSelNode;
17169         // undesirable, but required
17170         var sm = this;
17171         if(!s){
17172             return;
17173         }
17174         var k = e.getKey();
17175         switch(k){
17176              case e.DOWN:
17177                  e.stopEvent();
17178                  this.selectNext();
17179              break;
17180              case e.UP:
17181                  e.stopEvent();
17182                  this.selectPrevious();
17183              break;
17184              case e.RIGHT:
17185                  e.preventDefault();
17186                  if(s.hasChildNodes()){
17187                      if(!s.isExpanded()){
17188                          s.expand();
17189                      }else if(s.firstChild){
17190                          this.select(s.firstChild, e);
17191                      }
17192                  }
17193              break;
17194              case e.LEFT:
17195                  e.preventDefault();
17196                  if(s.hasChildNodes() && s.isExpanded()){
17197                      s.collapse();
17198                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17199                      this.select(s.parentNode, e);
17200                  }
17201              break;
17202         };
17203     }
17204 });
17205
17206 /**
17207  * @class Roo.tree.MultiSelectionModel
17208  * @extends Roo.util.Observable
17209  * Multi selection for a TreePanel.
17210  * @param {Object} cfg Configuration
17211  */
17212 Roo.tree.MultiSelectionModel = function(){
17213    this.selNodes = [];
17214    this.selMap = {};
17215    this.addEvents({
17216        /**
17217         * @event selectionchange
17218         * Fires when the selected nodes change
17219         * @param {MultiSelectionModel} this
17220         * @param {Array} nodes Array of the selected nodes
17221         */
17222        "selectionchange" : true
17223    });
17224    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17225    
17226 };
17227
17228 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17229     init : function(tree){
17230         this.tree = tree;
17231         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17232         tree.on("click", this.onNodeClick, this);
17233     },
17234     
17235     onNodeClick : function(node, e){
17236         this.select(node, e, e.ctrlKey);
17237     },
17238     
17239     /**
17240      * Select a node.
17241      * @param {TreeNode} node The node to select
17242      * @param {EventObject} e (optional) An event associated with the selection
17243      * @param {Boolean} keepExisting True to retain existing selections
17244      * @return {TreeNode} The selected node
17245      */
17246     select : function(node, e, keepExisting){
17247         if(keepExisting !== true){
17248             this.clearSelections(true);
17249         }
17250         if(this.isSelected(node)){
17251             this.lastSelNode = node;
17252             return node;
17253         }
17254         this.selNodes.push(node);
17255         this.selMap[node.id] = node;
17256         this.lastSelNode = node;
17257         node.ui.onSelectedChange(true);
17258         this.fireEvent("selectionchange", this, this.selNodes);
17259         return node;
17260     },
17261     
17262     /**
17263      * Deselect a node.
17264      * @param {TreeNode} node The node to unselect
17265      */
17266     unselect : function(node){
17267         if(this.selMap[node.id]){
17268             node.ui.onSelectedChange(false);
17269             var sn = this.selNodes;
17270             var index = -1;
17271             if(sn.indexOf){
17272                 index = sn.indexOf(node);
17273             }else{
17274                 for(var i = 0, len = sn.length; i < len; i++){
17275                     if(sn[i] == node){
17276                         index = i;
17277                         break;
17278                     }
17279                 }
17280             }
17281             if(index != -1){
17282                 this.selNodes.splice(index, 1);
17283             }
17284             delete this.selMap[node.id];
17285             this.fireEvent("selectionchange", this, this.selNodes);
17286         }
17287     },
17288     
17289     /**
17290      * Clear all selections
17291      */
17292     clearSelections : function(suppressEvent){
17293         var sn = this.selNodes;
17294         if(sn.length > 0){
17295             for(var i = 0, len = sn.length; i < len; i++){
17296                 sn[i].ui.onSelectedChange(false);
17297             }
17298             this.selNodes = [];
17299             this.selMap = {};
17300             if(suppressEvent !== true){
17301                 this.fireEvent("selectionchange", this, this.selNodes);
17302             }
17303         }
17304     },
17305     
17306     /**
17307      * Returns true if the node is selected
17308      * @param {TreeNode} node The node to check
17309      * @return {Boolean}
17310      */
17311     isSelected : function(node){
17312         return this.selMap[node.id] ? true : false;  
17313     },
17314     
17315     /**
17316      * Returns an array of the selected nodes
17317      * @return {Array}
17318      */
17319     getSelectedNodes : function(){
17320         return this.selNodes;    
17321     },
17322
17323     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17324
17325     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17326
17327     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17328 });/*
17329  * Based on:
17330  * Ext JS Library 1.1.1
17331  * Copyright(c) 2006-2007, Ext JS, LLC.
17332  *
17333  * Originally Released Under LGPL - original licence link has changed is not relivant.
17334  *
17335  * Fork - LGPL
17336  * <script type="text/javascript">
17337  */
17338  
17339 /**
17340  * @class Roo.tree.TreeNode
17341  * @extends Roo.data.Node
17342  * @cfg {String} text The text for this node
17343  * @cfg {Boolean} expanded true to start the node expanded
17344  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17345  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17346  * @cfg {Boolean} disabled true to start the node disabled
17347  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17348  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17349  * @cfg {String} cls A css class to be added to the node
17350  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17351  * @cfg {String} href URL of the link used for the node (defaults to #)
17352  * @cfg {String} hrefTarget target frame for the link
17353  * @cfg {String} qtip An Ext QuickTip for the node
17354  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17355  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17356  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17357  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17358  * (defaults to undefined with no checkbox rendered)
17359  * @constructor
17360  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17361  */
17362 Roo.tree.TreeNode = function(attributes){
17363     attributes = attributes || {};
17364     if(typeof attributes == "string"){
17365         attributes = {text: attributes};
17366     }
17367     this.childrenRendered = false;
17368     this.rendered = false;
17369     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17370     this.expanded = attributes.expanded === true;
17371     this.isTarget = attributes.isTarget !== false;
17372     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17373     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17374
17375     /**
17376      * Read-only. The text for this node. To change it use setText().
17377      * @type String
17378      */
17379     this.text = attributes.text;
17380     /**
17381      * True if this node is disabled.
17382      * @type Boolean
17383      */
17384     this.disabled = attributes.disabled === true;
17385
17386     this.addEvents({
17387         /**
17388         * @event textchange
17389         * Fires when the text for this node is changed
17390         * @param {Node} this This node
17391         * @param {String} text The new text
17392         * @param {String} oldText The old text
17393         */
17394         "textchange" : true,
17395         /**
17396         * @event beforeexpand
17397         * Fires before this node is expanded, return false to cancel.
17398         * @param {Node} this This node
17399         * @param {Boolean} deep
17400         * @param {Boolean} anim
17401         */
17402         "beforeexpand" : true,
17403         /**
17404         * @event beforecollapse
17405         * Fires before this node is collapsed, return false to cancel.
17406         * @param {Node} this This node
17407         * @param {Boolean} deep
17408         * @param {Boolean} anim
17409         */
17410         "beforecollapse" : true,
17411         /**
17412         * @event expand
17413         * Fires when this node is expanded
17414         * @param {Node} this This node
17415         */
17416         "expand" : true,
17417         /**
17418         * @event disabledchange
17419         * Fires when the disabled status of this node changes
17420         * @param {Node} this This node
17421         * @param {Boolean} disabled
17422         */
17423         "disabledchange" : true,
17424         /**
17425         * @event collapse
17426         * Fires when this node is collapsed
17427         * @param {Node} this This node
17428         */
17429         "collapse" : true,
17430         /**
17431         * @event beforeclick
17432         * Fires before click processing. Return false to cancel the default action.
17433         * @param {Node} this This node
17434         * @param {Roo.EventObject} e The event object
17435         */
17436         "beforeclick":true,
17437         /**
17438         * @event checkchange
17439         * Fires when a node with a checkbox's checked property changes
17440         * @param {Node} this This node
17441         * @param {Boolean} checked
17442         */
17443         "checkchange":true,
17444         /**
17445         * @event click
17446         * Fires when this node is clicked
17447         * @param {Node} this This node
17448         * @param {Roo.EventObject} e The event object
17449         */
17450         "click":true,
17451         /**
17452         * @event dblclick
17453         * Fires when this node is double clicked
17454         * @param {Node} this This node
17455         * @param {Roo.EventObject} e The event object
17456         */
17457         "dblclick":true,
17458         /**
17459         * @event contextmenu
17460         * Fires when this node is right clicked
17461         * @param {Node} this This node
17462         * @param {Roo.EventObject} e The event object
17463         */
17464         "contextmenu":true,
17465         /**
17466         * @event beforechildrenrendered
17467         * Fires right before the child nodes for this node are rendered
17468         * @param {Node} this This node
17469         */
17470         "beforechildrenrendered":true
17471     });
17472
17473     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17474
17475     /**
17476      * Read-only. The UI for this node
17477      * @type TreeNodeUI
17478      */
17479     this.ui = new uiClass(this);
17480     
17481     // finally support items[]
17482     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17483         return;
17484     }
17485     
17486     
17487     Roo.each(this.attributes.items, function(c) {
17488         this.appendChild(Roo.factory(c,Roo.Tree));
17489     }, this);
17490     delete this.attributes.items;
17491     
17492     
17493     
17494 };
17495 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17496     preventHScroll: true,
17497     /**
17498      * Returns true if this node is expanded
17499      * @return {Boolean}
17500      */
17501     isExpanded : function(){
17502         return this.expanded;
17503     },
17504
17505     /**
17506      * Returns the UI object for this node
17507      * @return {TreeNodeUI}
17508      */
17509     getUI : function(){
17510         return this.ui;
17511     },
17512
17513     // private override
17514     setFirstChild : function(node){
17515         var of = this.firstChild;
17516         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17517         if(this.childrenRendered && of && node != of){
17518             of.renderIndent(true, true);
17519         }
17520         if(this.rendered){
17521             this.renderIndent(true, true);
17522         }
17523     },
17524
17525     // private override
17526     setLastChild : function(node){
17527         var ol = this.lastChild;
17528         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17529         if(this.childrenRendered && ol && node != ol){
17530             ol.renderIndent(true, true);
17531         }
17532         if(this.rendered){
17533             this.renderIndent(true, true);
17534         }
17535     },
17536
17537     // these methods are overridden to provide lazy rendering support
17538     // private override
17539     appendChild : function()
17540     {
17541         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17542         if(node && this.childrenRendered){
17543             node.render();
17544         }
17545         this.ui.updateExpandIcon();
17546         return node;
17547     },
17548
17549     // private override
17550     removeChild : function(node){
17551         this.ownerTree.getSelectionModel().unselect(node);
17552         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17553         // if it's been rendered remove dom node
17554         if(this.childrenRendered){
17555             node.ui.remove();
17556         }
17557         if(this.childNodes.length < 1){
17558             this.collapse(false, false);
17559         }else{
17560             this.ui.updateExpandIcon();
17561         }
17562         if(!this.firstChild) {
17563             this.childrenRendered = false;
17564         }
17565         return node;
17566     },
17567
17568     // private override
17569     insertBefore : function(node, refNode){
17570         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17571         if(newNode && refNode && this.childrenRendered){
17572             node.render();
17573         }
17574         this.ui.updateExpandIcon();
17575         return newNode;
17576     },
17577
17578     /**
17579      * Sets the text for this node
17580      * @param {String} text
17581      */
17582     setText : function(text){
17583         var oldText = this.text;
17584         this.text = text;
17585         this.attributes.text = text;
17586         if(this.rendered){ // event without subscribing
17587             this.ui.onTextChange(this, text, oldText);
17588         }
17589         this.fireEvent("textchange", this, text, oldText);
17590     },
17591
17592     /**
17593      * Triggers selection of this node
17594      */
17595     select : function(){
17596         this.getOwnerTree().getSelectionModel().select(this);
17597     },
17598
17599     /**
17600      * Triggers deselection of this node
17601      */
17602     unselect : function(){
17603         this.getOwnerTree().getSelectionModel().unselect(this);
17604     },
17605
17606     /**
17607      * Returns true if this node is selected
17608      * @return {Boolean}
17609      */
17610     isSelected : function(){
17611         return this.getOwnerTree().getSelectionModel().isSelected(this);
17612     },
17613
17614     /**
17615      * Expand this node.
17616      * @param {Boolean} deep (optional) True to expand all children as well
17617      * @param {Boolean} anim (optional) false to cancel the default animation
17618      * @param {Function} callback (optional) A callback to be called when
17619      * expanding this node completes (does not wait for deep expand to complete).
17620      * Called with 1 parameter, this node.
17621      */
17622     expand : function(deep, anim, callback){
17623         if(!this.expanded){
17624             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17625                 return;
17626             }
17627             if(!this.childrenRendered){
17628                 this.renderChildren();
17629             }
17630             this.expanded = true;
17631             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17632                 this.ui.animExpand(function(){
17633                     this.fireEvent("expand", this);
17634                     if(typeof callback == "function"){
17635                         callback(this);
17636                     }
17637                     if(deep === true){
17638                         this.expandChildNodes(true);
17639                     }
17640                 }.createDelegate(this));
17641                 return;
17642             }else{
17643                 this.ui.expand();
17644                 this.fireEvent("expand", this);
17645                 if(typeof callback == "function"){
17646                     callback(this);
17647                 }
17648             }
17649         }else{
17650            if(typeof callback == "function"){
17651                callback(this);
17652            }
17653         }
17654         if(deep === true){
17655             this.expandChildNodes(true);
17656         }
17657     },
17658
17659     isHiddenRoot : function(){
17660         return this.isRoot && !this.getOwnerTree().rootVisible;
17661     },
17662
17663     /**
17664      * Collapse this node.
17665      * @param {Boolean} deep (optional) True to collapse all children as well
17666      * @param {Boolean} anim (optional) false to cancel the default animation
17667      */
17668     collapse : function(deep, anim){
17669         if(this.expanded && !this.isHiddenRoot()){
17670             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17671                 return;
17672             }
17673             this.expanded = false;
17674             if((this.getOwnerTree().animate && anim !== false) || anim){
17675                 this.ui.animCollapse(function(){
17676                     this.fireEvent("collapse", this);
17677                     if(deep === true){
17678                         this.collapseChildNodes(true);
17679                     }
17680                 }.createDelegate(this));
17681                 return;
17682             }else{
17683                 this.ui.collapse();
17684                 this.fireEvent("collapse", this);
17685             }
17686         }
17687         if(deep === true){
17688             var cs = this.childNodes;
17689             for(var i = 0, len = cs.length; i < len; i++) {
17690                 cs[i].collapse(true, false);
17691             }
17692         }
17693     },
17694
17695     // private
17696     delayedExpand : function(delay){
17697         if(!this.expandProcId){
17698             this.expandProcId = this.expand.defer(delay, this);
17699         }
17700     },
17701
17702     // private
17703     cancelExpand : function(){
17704         if(this.expandProcId){
17705             clearTimeout(this.expandProcId);
17706         }
17707         this.expandProcId = false;
17708     },
17709
17710     /**
17711      * Toggles expanded/collapsed state of the node
17712      */
17713     toggle : function(){
17714         if(this.expanded){
17715             this.collapse();
17716         }else{
17717             this.expand();
17718         }
17719     },
17720
17721     /**
17722      * Ensures all parent nodes are expanded
17723      */
17724     ensureVisible : function(callback){
17725         var tree = this.getOwnerTree();
17726         tree.expandPath(this.parentNode.getPath(), false, function(){
17727             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17728             Roo.callback(callback);
17729         }.createDelegate(this));
17730     },
17731
17732     /**
17733      * Expand all child nodes
17734      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17735      */
17736     expandChildNodes : function(deep){
17737         var cs = this.childNodes;
17738         for(var i = 0, len = cs.length; i < len; i++) {
17739                 cs[i].expand(deep);
17740         }
17741     },
17742
17743     /**
17744      * Collapse all child nodes
17745      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17746      */
17747     collapseChildNodes : function(deep){
17748         var cs = this.childNodes;
17749         for(var i = 0, len = cs.length; i < len; i++) {
17750                 cs[i].collapse(deep);
17751         }
17752     },
17753
17754     /**
17755      * Disables this node
17756      */
17757     disable : function(){
17758         this.disabled = true;
17759         this.unselect();
17760         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17761             this.ui.onDisableChange(this, true);
17762         }
17763         this.fireEvent("disabledchange", this, true);
17764     },
17765
17766     /**
17767      * Enables this node
17768      */
17769     enable : function(){
17770         this.disabled = false;
17771         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17772             this.ui.onDisableChange(this, false);
17773         }
17774         this.fireEvent("disabledchange", this, false);
17775     },
17776
17777     // private
17778     renderChildren : function(suppressEvent){
17779         if(suppressEvent !== false){
17780             this.fireEvent("beforechildrenrendered", this);
17781         }
17782         var cs = this.childNodes;
17783         for(var i = 0, len = cs.length; i < len; i++){
17784             cs[i].render(true);
17785         }
17786         this.childrenRendered = true;
17787     },
17788
17789     // private
17790     sort : function(fn, scope){
17791         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17792         if(this.childrenRendered){
17793             var cs = this.childNodes;
17794             for(var i = 0, len = cs.length; i < len; i++){
17795                 cs[i].render(true);
17796             }
17797         }
17798     },
17799
17800     // private
17801     render : function(bulkRender){
17802         this.ui.render(bulkRender);
17803         if(!this.rendered){
17804             this.rendered = true;
17805             if(this.expanded){
17806                 this.expanded = false;
17807                 this.expand(false, false);
17808             }
17809         }
17810     },
17811
17812     // private
17813     renderIndent : function(deep, refresh){
17814         if(refresh){
17815             this.ui.childIndent = null;
17816         }
17817         this.ui.renderIndent();
17818         if(deep === true && this.childrenRendered){
17819             var cs = this.childNodes;
17820             for(var i = 0, len = cs.length; i < len; i++){
17821                 cs[i].renderIndent(true, refresh);
17822             }
17823         }
17824     }
17825 });/*
17826  * Based on:
17827  * Ext JS Library 1.1.1
17828  * Copyright(c) 2006-2007, Ext JS, LLC.
17829  *
17830  * Originally Released Under LGPL - original licence link has changed is not relivant.
17831  *
17832  * Fork - LGPL
17833  * <script type="text/javascript">
17834  */
17835  
17836 /**
17837  * @class Roo.tree.AsyncTreeNode
17838  * @extends Roo.tree.TreeNode
17839  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17840  * @constructor
17841  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17842  */
17843  Roo.tree.AsyncTreeNode = function(config){
17844     this.loaded = false;
17845     this.loading = false;
17846     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17847     /**
17848     * @event beforeload
17849     * Fires before this node is loaded, return false to cancel
17850     * @param {Node} this This node
17851     */
17852     this.addEvents({'beforeload':true, 'load': true});
17853     /**
17854     * @event load
17855     * Fires when this node is loaded
17856     * @param {Node} this This node
17857     */
17858     /**
17859      * The loader used by this node (defaults to using the tree's defined loader)
17860      * @type TreeLoader
17861      * @property loader
17862      */
17863 };
17864 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17865     expand : function(deep, anim, callback){
17866         if(this.loading){ // if an async load is already running, waiting til it's done
17867             var timer;
17868             var f = function(){
17869                 if(!this.loading){ // done loading
17870                     clearInterval(timer);
17871                     this.expand(deep, anim, callback);
17872                 }
17873             }.createDelegate(this);
17874             timer = setInterval(f, 200);
17875             return;
17876         }
17877         if(!this.loaded){
17878             if(this.fireEvent("beforeload", this) === false){
17879                 return;
17880             }
17881             this.loading = true;
17882             this.ui.beforeLoad(this);
17883             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17884             if(loader){
17885                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17886                 return;
17887             }
17888         }
17889         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17890     },
17891     
17892     /**
17893      * Returns true if this node is currently loading
17894      * @return {Boolean}
17895      */
17896     isLoading : function(){
17897         return this.loading;  
17898     },
17899     
17900     loadComplete : function(deep, anim, callback){
17901         this.loading = false;
17902         this.loaded = true;
17903         this.ui.afterLoad(this);
17904         this.fireEvent("load", this);
17905         this.expand(deep, anim, callback);
17906     },
17907     
17908     /**
17909      * Returns true if this node has been loaded
17910      * @return {Boolean}
17911      */
17912     isLoaded : function(){
17913         return this.loaded;
17914     },
17915     
17916     hasChildNodes : function(){
17917         if(!this.isLeaf() && !this.loaded){
17918             return true;
17919         }else{
17920             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17921         }
17922     },
17923
17924     /**
17925      * Trigger a reload for this node
17926      * @param {Function} callback
17927      */
17928     reload : function(callback){
17929         this.collapse(false, false);
17930         while(this.firstChild){
17931             this.removeChild(this.firstChild);
17932         }
17933         this.childrenRendered = false;
17934         this.loaded = false;
17935         if(this.isHiddenRoot()){
17936             this.expanded = false;
17937         }
17938         this.expand(false, false, callback);
17939     }
17940 });/*
17941  * Based on:
17942  * Ext JS Library 1.1.1
17943  * Copyright(c) 2006-2007, Ext JS, LLC.
17944  *
17945  * Originally Released Under LGPL - original licence link has changed is not relivant.
17946  *
17947  * Fork - LGPL
17948  * <script type="text/javascript">
17949  */
17950  
17951 /**
17952  * @class Roo.tree.TreeNodeUI
17953  * @constructor
17954  * @param {Object} node The node to render
17955  * The TreeNode UI implementation is separate from the
17956  * tree implementation. Unless you are customizing the tree UI,
17957  * you should never have to use this directly.
17958  */
17959 Roo.tree.TreeNodeUI = function(node){
17960     this.node = node;
17961     this.rendered = false;
17962     this.animating = false;
17963     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17964 };
17965
17966 Roo.tree.TreeNodeUI.prototype = {
17967     removeChild : function(node){
17968         if(this.rendered){
17969             this.ctNode.removeChild(node.ui.getEl());
17970         }
17971     },
17972
17973     beforeLoad : function(){
17974          this.addClass("x-tree-node-loading");
17975     },
17976
17977     afterLoad : function(){
17978          this.removeClass("x-tree-node-loading");
17979     },
17980
17981     onTextChange : function(node, text, oldText){
17982         if(this.rendered){
17983             this.textNode.innerHTML = text;
17984         }
17985     },
17986
17987     onDisableChange : function(node, state){
17988         this.disabled = state;
17989         if(state){
17990             this.addClass("x-tree-node-disabled");
17991         }else{
17992             this.removeClass("x-tree-node-disabled");
17993         }
17994     },
17995
17996     onSelectedChange : function(state){
17997         if(state){
17998             this.focus();
17999             this.addClass("x-tree-selected");
18000         }else{
18001             //this.blur();
18002             this.removeClass("x-tree-selected");
18003         }
18004     },
18005
18006     onMove : function(tree, node, oldParent, newParent, index, refNode){
18007         this.childIndent = null;
18008         if(this.rendered){
18009             var targetNode = newParent.ui.getContainer();
18010             if(!targetNode){//target not rendered
18011                 this.holder = document.createElement("div");
18012                 this.holder.appendChild(this.wrap);
18013                 return;
18014             }
18015             var insertBefore = refNode ? refNode.ui.getEl() : null;
18016             if(insertBefore){
18017                 targetNode.insertBefore(this.wrap, insertBefore);
18018             }else{
18019                 targetNode.appendChild(this.wrap);
18020             }
18021             this.node.renderIndent(true);
18022         }
18023     },
18024
18025     addClass : function(cls){
18026         if(this.elNode){
18027             Roo.fly(this.elNode).addClass(cls);
18028         }
18029     },
18030
18031     removeClass : function(cls){
18032         if(this.elNode){
18033             Roo.fly(this.elNode).removeClass(cls);
18034         }
18035     },
18036
18037     remove : function(){
18038         if(this.rendered){
18039             this.holder = document.createElement("div");
18040             this.holder.appendChild(this.wrap);
18041         }
18042     },
18043
18044     fireEvent : function(){
18045         return this.node.fireEvent.apply(this.node, arguments);
18046     },
18047
18048     initEvents : function(){
18049         this.node.on("move", this.onMove, this);
18050         var E = Roo.EventManager;
18051         var a = this.anchor;
18052
18053         var el = Roo.fly(a, '_treeui');
18054
18055         if(Roo.isOpera){ // opera render bug ignores the CSS
18056             el.setStyle("text-decoration", "none");
18057         }
18058
18059         el.on("click", this.onClick, this);
18060         el.on("dblclick", this.onDblClick, this);
18061
18062         if(this.checkbox){
18063             Roo.EventManager.on(this.checkbox,
18064                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18065         }
18066
18067         el.on("contextmenu", this.onContextMenu, this);
18068
18069         var icon = Roo.fly(this.iconNode);
18070         icon.on("click", this.onClick, this);
18071         icon.on("dblclick", this.onDblClick, this);
18072         icon.on("contextmenu", this.onContextMenu, this);
18073         E.on(this.ecNode, "click", this.ecClick, this, true);
18074
18075         if(this.node.disabled){
18076             this.addClass("x-tree-node-disabled");
18077         }
18078         if(this.node.hidden){
18079             this.addClass("x-tree-node-disabled");
18080         }
18081         var ot = this.node.getOwnerTree();
18082         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18083         if(dd && (!this.node.isRoot || ot.rootVisible)){
18084             Roo.dd.Registry.register(this.elNode, {
18085                 node: this.node,
18086                 handles: this.getDDHandles(),
18087                 isHandle: false
18088             });
18089         }
18090     },
18091
18092     getDDHandles : function(){
18093         return [this.iconNode, this.textNode];
18094     },
18095
18096     hide : function(){
18097         if(this.rendered){
18098             this.wrap.style.display = "none";
18099         }
18100     },
18101
18102     show : function(){
18103         if(this.rendered){
18104             this.wrap.style.display = "";
18105         }
18106     },
18107
18108     onContextMenu : function(e){
18109         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18110             e.preventDefault();
18111             this.focus();
18112             this.fireEvent("contextmenu", this.node, e);
18113         }
18114     },
18115
18116     onClick : function(e){
18117         if(this.dropping){
18118             e.stopEvent();
18119             return;
18120         }
18121         if(this.fireEvent("beforeclick", this.node, e) !== false){
18122             if(!this.disabled && this.node.attributes.href){
18123                 this.fireEvent("click", this.node, e);
18124                 return;
18125             }
18126             e.preventDefault();
18127             if(this.disabled){
18128                 return;
18129             }
18130
18131             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18132                 this.node.toggle();
18133             }
18134
18135             this.fireEvent("click", this.node, e);
18136         }else{
18137             e.stopEvent();
18138         }
18139     },
18140
18141     onDblClick : function(e){
18142         e.preventDefault();
18143         if(this.disabled){
18144             return;
18145         }
18146         if(this.checkbox){
18147             this.toggleCheck();
18148         }
18149         if(!this.animating && this.node.hasChildNodes()){
18150             this.node.toggle();
18151         }
18152         this.fireEvent("dblclick", this.node, e);
18153     },
18154
18155     onCheckChange : function(){
18156         var checked = this.checkbox.checked;
18157         this.node.attributes.checked = checked;
18158         this.fireEvent('checkchange', this.node, checked);
18159     },
18160
18161     ecClick : function(e){
18162         if(!this.animating && this.node.hasChildNodes()){
18163             this.node.toggle();
18164         }
18165     },
18166
18167     startDrop : function(){
18168         this.dropping = true;
18169     },
18170
18171     // delayed drop so the click event doesn't get fired on a drop
18172     endDrop : function(){
18173        setTimeout(function(){
18174            this.dropping = false;
18175        }.createDelegate(this), 50);
18176     },
18177
18178     expand : function(){
18179         this.updateExpandIcon();
18180         this.ctNode.style.display = "";
18181     },
18182
18183     focus : function(){
18184         if(!this.node.preventHScroll){
18185             try{this.anchor.focus();
18186             }catch(e){}
18187         }else if(!Roo.isIE){
18188             try{
18189                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18190                 var l = noscroll.scrollLeft;
18191                 this.anchor.focus();
18192                 noscroll.scrollLeft = l;
18193             }catch(e){}
18194         }
18195     },
18196
18197     toggleCheck : function(value){
18198         var cb = this.checkbox;
18199         if(cb){
18200             cb.checked = (value === undefined ? !cb.checked : value);
18201         }
18202     },
18203
18204     blur : function(){
18205         try{
18206             this.anchor.blur();
18207         }catch(e){}
18208     },
18209
18210     animExpand : function(callback){
18211         var ct = Roo.get(this.ctNode);
18212         ct.stopFx();
18213         if(!this.node.hasChildNodes()){
18214             this.updateExpandIcon();
18215             this.ctNode.style.display = "";
18216             Roo.callback(callback);
18217             return;
18218         }
18219         this.animating = true;
18220         this.updateExpandIcon();
18221
18222         ct.slideIn('t', {
18223            callback : function(){
18224                this.animating = false;
18225                Roo.callback(callback);
18226             },
18227             scope: this,
18228             duration: this.node.ownerTree.duration || .25
18229         });
18230     },
18231
18232     highlight : function(){
18233         var tree = this.node.getOwnerTree();
18234         Roo.fly(this.wrap).highlight(
18235             tree.hlColor || "C3DAF9",
18236             {endColor: tree.hlBaseColor}
18237         );
18238     },
18239
18240     collapse : function(){
18241         this.updateExpandIcon();
18242         this.ctNode.style.display = "none";
18243     },
18244
18245     animCollapse : function(callback){
18246         var ct = Roo.get(this.ctNode);
18247         ct.enableDisplayMode('block');
18248         ct.stopFx();
18249
18250         this.animating = true;
18251         this.updateExpandIcon();
18252
18253         ct.slideOut('t', {
18254             callback : function(){
18255                this.animating = false;
18256                Roo.callback(callback);
18257             },
18258             scope: this,
18259             duration: this.node.ownerTree.duration || .25
18260         });
18261     },
18262
18263     getContainer : function(){
18264         return this.ctNode;
18265     },
18266
18267     getEl : function(){
18268         return this.wrap;
18269     },
18270
18271     appendDDGhost : function(ghostNode){
18272         ghostNode.appendChild(this.elNode.cloneNode(true));
18273     },
18274
18275     getDDRepairXY : function(){
18276         return Roo.lib.Dom.getXY(this.iconNode);
18277     },
18278
18279     onRender : function(){
18280         this.render();
18281     },
18282
18283     render : function(bulkRender){
18284         var n = this.node, a = n.attributes;
18285         var targetNode = n.parentNode ?
18286               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18287
18288         if(!this.rendered){
18289             this.rendered = true;
18290
18291             this.renderElements(n, a, targetNode, bulkRender);
18292
18293             if(a.qtip){
18294                if(this.textNode.setAttributeNS){
18295                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18296                    if(a.qtipTitle){
18297                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18298                    }
18299                }else{
18300                    this.textNode.setAttribute("ext:qtip", a.qtip);
18301                    if(a.qtipTitle){
18302                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18303                    }
18304                }
18305             }else if(a.qtipCfg){
18306                 a.qtipCfg.target = Roo.id(this.textNode);
18307                 Roo.QuickTips.register(a.qtipCfg);
18308             }
18309             this.initEvents();
18310             if(!this.node.expanded){
18311                 this.updateExpandIcon();
18312             }
18313         }else{
18314             if(bulkRender === true) {
18315                 targetNode.appendChild(this.wrap);
18316             }
18317         }
18318     },
18319
18320     renderElements : function(n, a, targetNode, bulkRender)
18321     {
18322         // add some indent caching, this helps performance when rendering a large tree
18323         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18324         var t = n.getOwnerTree();
18325         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18326         if (typeof(n.attributes.html) != 'undefined') {
18327             txt = n.attributes.html;
18328         }
18329         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18330         var cb = typeof a.checked == 'boolean';
18331         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18332         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18333             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18334             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18335             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18336             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18337             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18338              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18339                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18340             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18341             "</li>"];
18342
18343         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18344             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18345                                 n.nextSibling.ui.getEl(), buf.join(""));
18346         }else{
18347             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18348         }
18349
18350         this.elNode = this.wrap.childNodes[0];
18351         this.ctNode = this.wrap.childNodes[1];
18352         var cs = this.elNode.childNodes;
18353         this.indentNode = cs[0];
18354         this.ecNode = cs[1];
18355         this.iconNode = cs[2];
18356         var index = 3;
18357         if(cb){
18358             this.checkbox = cs[3];
18359             index++;
18360         }
18361         this.anchor = cs[index];
18362         this.textNode = cs[index].firstChild;
18363     },
18364
18365     getAnchor : function(){
18366         return this.anchor;
18367     },
18368
18369     getTextEl : function(){
18370         return this.textNode;
18371     },
18372
18373     getIconEl : function(){
18374         return this.iconNode;
18375     },
18376
18377     isChecked : function(){
18378         return this.checkbox ? this.checkbox.checked : false;
18379     },
18380
18381     updateExpandIcon : function(){
18382         if(this.rendered){
18383             var n = this.node, c1, c2;
18384             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18385             var hasChild = n.hasChildNodes();
18386             if(hasChild){
18387                 if(n.expanded){
18388                     cls += "-minus";
18389                     c1 = "x-tree-node-collapsed";
18390                     c2 = "x-tree-node-expanded";
18391                 }else{
18392                     cls += "-plus";
18393                     c1 = "x-tree-node-expanded";
18394                     c2 = "x-tree-node-collapsed";
18395                 }
18396                 if(this.wasLeaf){
18397                     this.removeClass("x-tree-node-leaf");
18398                     this.wasLeaf = false;
18399                 }
18400                 if(this.c1 != c1 || this.c2 != c2){
18401                     Roo.fly(this.elNode).replaceClass(c1, c2);
18402                     this.c1 = c1; this.c2 = c2;
18403                 }
18404             }else{
18405                 // this changes non-leafs into leafs if they have no children.
18406                 // it's not very rational behaviour..
18407                 
18408                 if(!this.wasLeaf && this.node.leaf){
18409                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18410                     delete this.c1;
18411                     delete this.c2;
18412                     this.wasLeaf = true;
18413                 }
18414             }
18415             var ecc = "x-tree-ec-icon "+cls;
18416             if(this.ecc != ecc){
18417                 this.ecNode.className = ecc;
18418                 this.ecc = ecc;
18419             }
18420         }
18421     },
18422
18423     getChildIndent : function(){
18424         if(!this.childIndent){
18425             var buf = [];
18426             var p = this.node;
18427             while(p){
18428                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18429                     if(!p.isLast()) {
18430                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18431                     } else {
18432                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18433                     }
18434                 }
18435                 p = p.parentNode;
18436             }
18437             this.childIndent = buf.join("");
18438         }
18439         return this.childIndent;
18440     },
18441
18442     renderIndent : function(){
18443         if(this.rendered){
18444             var indent = "";
18445             var p = this.node.parentNode;
18446             if(p){
18447                 indent = p.ui.getChildIndent();
18448             }
18449             if(this.indentMarkup != indent){ // don't rerender if not required
18450                 this.indentNode.innerHTML = indent;
18451                 this.indentMarkup = indent;
18452             }
18453             this.updateExpandIcon();
18454         }
18455     }
18456 };
18457
18458 Roo.tree.RootTreeNodeUI = function(){
18459     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18460 };
18461 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18462     render : function(){
18463         if(!this.rendered){
18464             var targetNode = this.node.ownerTree.innerCt.dom;
18465             this.node.expanded = true;
18466             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18467             this.wrap = this.ctNode = targetNode.firstChild;
18468         }
18469     },
18470     collapse : function(){
18471     },
18472     expand : function(){
18473     }
18474 });/*
18475  * Based on:
18476  * Ext JS Library 1.1.1
18477  * Copyright(c) 2006-2007, Ext JS, LLC.
18478  *
18479  * Originally Released Under LGPL - original licence link has changed is not relivant.
18480  *
18481  * Fork - LGPL
18482  * <script type="text/javascript">
18483  */
18484 /**
18485  * @class Roo.tree.TreeLoader
18486  * @extends Roo.util.Observable
18487  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18488  * nodes from a specified URL. The response must be a javascript Array definition
18489  * who's elements are node definition objects. eg:
18490  * <pre><code>
18491 {  success : true,
18492    data :      [
18493    
18494     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18495     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18496     ]
18497 }
18498
18499
18500 </code></pre>
18501  * <br><br>
18502  * The old style respose with just an array is still supported, but not recommended.
18503  * <br><br>
18504  *
18505  * A server request is sent, and child nodes are loaded only when a node is expanded.
18506  * The loading node's id is passed to the server under the parameter name "node" to
18507  * enable the server to produce the correct child nodes.
18508  * <br><br>
18509  * To pass extra parameters, an event handler may be attached to the "beforeload"
18510  * event, and the parameters specified in the TreeLoader's baseParams property:
18511  * <pre><code>
18512     myTreeLoader.on("beforeload", function(treeLoader, node) {
18513         this.baseParams.category = node.attributes.category;
18514     }, this);
18515 </code></pre><
18516  * This would pass an HTTP parameter called "category" to the server containing
18517  * the value of the Node's "category" attribute.
18518  * @constructor
18519  * Creates a new Treeloader.
18520  * @param {Object} config A config object containing config properties.
18521  */
18522 Roo.tree.TreeLoader = function(config){
18523     this.baseParams = {};
18524     this.requestMethod = "POST";
18525     Roo.apply(this, config);
18526
18527     this.addEvents({
18528     
18529         /**
18530          * @event beforeload
18531          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18532          * @param {Object} This TreeLoader object.
18533          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18534          * @param {Object} callback The callback function specified in the {@link #load} call.
18535          */
18536         beforeload : true,
18537         /**
18538          * @event load
18539          * Fires when the node has been successfuly loaded.
18540          * @param {Object} This TreeLoader object.
18541          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18542          * @param {Object} response The response object containing the data from the server.
18543          */
18544         load : true,
18545         /**
18546          * @event loadexception
18547          * Fires if the network request failed.
18548          * @param {Object} This TreeLoader object.
18549          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18550          * @param {Object} response The response object containing the data from the server.
18551          */
18552         loadexception : true,
18553         /**
18554          * @event create
18555          * Fires before a node is created, enabling you to return custom Node types 
18556          * @param {Object} This TreeLoader object.
18557          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18558          */
18559         create : true
18560     });
18561
18562     Roo.tree.TreeLoader.superclass.constructor.call(this);
18563 };
18564
18565 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18566     /**
18567     * @cfg {String} dataUrl The URL from which to request a Json string which
18568     * specifies an array of node definition object representing the child nodes
18569     * to be loaded.
18570     */
18571     /**
18572     * @cfg {String} requestMethod either GET or POST
18573     * defaults to POST (due to BC)
18574     * to be loaded.
18575     */
18576     /**
18577     * @cfg {Object} baseParams (optional) An object containing properties which
18578     * specify HTTP parameters to be passed to each request for child nodes.
18579     */
18580     /**
18581     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18582     * created by this loader. If the attributes sent by the server have an attribute in this object,
18583     * they take priority.
18584     */
18585     /**
18586     * @cfg {Object} uiProviders (optional) An object containing properties which
18587     * 
18588     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18589     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18590     * <i>uiProvider</i> attribute of a returned child node is a string rather
18591     * than a reference to a TreeNodeUI implementation, this that string value
18592     * is used as a property name in the uiProviders object. You can define the provider named
18593     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18594     */
18595     uiProviders : {},
18596
18597     /**
18598     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18599     * child nodes before loading.
18600     */
18601     clearOnLoad : true,
18602
18603     /**
18604     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18605     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18606     * Grid query { data : [ .....] }
18607     */
18608     
18609     root : false,
18610      /**
18611     * @cfg {String} queryParam (optional) 
18612     * Name of the query as it will be passed on the querystring (defaults to 'node')
18613     * eg. the request will be ?node=[id]
18614     */
18615     
18616     
18617     queryParam: false,
18618     
18619     /**
18620      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18621      * This is called automatically when a node is expanded, but may be used to reload
18622      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18623      * @param {Roo.tree.TreeNode} node
18624      * @param {Function} callback
18625      */
18626     load : function(node, callback){
18627         if(this.clearOnLoad){
18628             while(node.firstChild){
18629                 node.removeChild(node.firstChild);
18630             }
18631         }
18632         if(node.attributes.children){ // preloaded json children
18633             var cs = node.attributes.children;
18634             for(var i = 0, len = cs.length; i < len; i++){
18635                 node.appendChild(this.createNode(cs[i]));
18636             }
18637             if(typeof callback == "function"){
18638                 callback();
18639             }
18640         }else if(this.dataUrl){
18641             this.requestData(node, callback);
18642         }
18643     },
18644
18645     getParams: function(node){
18646         var buf = [], bp = this.baseParams;
18647         for(var key in bp){
18648             if(typeof bp[key] != "function"){
18649                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18650             }
18651         }
18652         var n = this.queryParam === false ? 'node' : this.queryParam;
18653         buf.push(n + "=", encodeURIComponent(node.id));
18654         return buf.join("");
18655     },
18656
18657     requestData : function(node, callback){
18658         if(this.fireEvent("beforeload", this, node, callback) !== false){
18659             this.transId = Roo.Ajax.request({
18660                 method:this.requestMethod,
18661                 url: this.dataUrl||this.url,
18662                 success: this.handleResponse,
18663                 failure: this.handleFailure,
18664                 scope: this,
18665                 argument: {callback: callback, node: node},
18666                 params: this.getParams(node)
18667             });
18668         }else{
18669             // if the load is cancelled, make sure we notify
18670             // the node that we are done
18671             if(typeof callback == "function"){
18672                 callback();
18673             }
18674         }
18675     },
18676
18677     isLoading : function(){
18678         return this.transId ? true : false;
18679     },
18680
18681     abort : function(){
18682         if(this.isLoading()){
18683             Roo.Ajax.abort(this.transId);
18684         }
18685     },
18686
18687     // private
18688     createNode : function(attr)
18689     {
18690         // apply baseAttrs, nice idea Corey!
18691         if(this.baseAttrs){
18692             Roo.applyIf(attr, this.baseAttrs);
18693         }
18694         if(this.applyLoader !== false){
18695             attr.loader = this;
18696         }
18697         // uiProvider = depreciated..
18698         
18699         if(typeof(attr.uiProvider) == 'string'){
18700            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18701                 /**  eval:var:attr */ eval(attr.uiProvider);
18702         }
18703         if(typeof(this.uiProviders['default']) != 'undefined') {
18704             attr.uiProvider = this.uiProviders['default'];
18705         }
18706         
18707         this.fireEvent('create', this, attr);
18708         
18709         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18710         return(attr.leaf ?
18711                         new Roo.tree.TreeNode(attr) :
18712                         new Roo.tree.AsyncTreeNode(attr));
18713     },
18714
18715     processResponse : function(response, node, callback)
18716     {
18717         var json = response.responseText;
18718         try {
18719             
18720             var o = Roo.decode(json);
18721             
18722             if (this.root === false && typeof(o.success) != undefined) {
18723                 this.root = 'data'; // the default behaviour for list like data..
18724                 }
18725                 
18726             if (this.root !== false &&  !o.success) {
18727                 // it's a failure condition.
18728                 var a = response.argument;
18729                 this.fireEvent("loadexception", this, a.node, response);
18730                 Roo.log("Load failed - should have a handler really");
18731                 return;
18732             }
18733             
18734             
18735             
18736             if (this.root !== false) {
18737                  o = o[this.root];
18738             }
18739             
18740             for(var i = 0, len = o.length; i < len; i++){
18741                 var n = this.createNode(o[i]);
18742                 if(n){
18743                     node.appendChild(n);
18744                 }
18745             }
18746             if(typeof callback == "function"){
18747                 callback(this, node);
18748             }
18749         }catch(e){
18750             this.handleFailure(response);
18751         }
18752     },
18753
18754     handleResponse : function(response){
18755         this.transId = false;
18756         var a = response.argument;
18757         this.processResponse(response, a.node, a.callback);
18758         this.fireEvent("load", this, a.node, response);
18759     },
18760
18761     handleFailure : function(response)
18762     {
18763         // should handle failure better..
18764         this.transId = false;
18765         var a = response.argument;
18766         this.fireEvent("loadexception", this, a.node, response);
18767         if(typeof a.callback == "function"){
18768             a.callback(this, a.node);
18769         }
18770     }
18771 });/*
18772  * Based on:
18773  * Ext JS Library 1.1.1
18774  * Copyright(c) 2006-2007, Ext JS, LLC.
18775  *
18776  * Originally Released Under LGPL - original licence link has changed is not relivant.
18777  *
18778  * Fork - LGPL
18779  * <script type="text/javascript">
18780  */
18781
18782 /**
18783 * @class Roo.tree.TreeFilter
18784 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18785 * @param {TreePanel} tree
18786 * @param {Object} config (optional)
18787  */
18788 Roo.tree.TreeFilter = function(tree, config){
18789     this.tree = tree;
18790     this.filtered = {};
18791     Roo.apply(this, config);
18792 };
18793
18794 Roo.tree.TreeFilter.prototype = {
18795     clearBlank:false,
18796     reverse:false,
18797     autoClear:false,
18798     remove:false,
18799
18800      /**
18801      * Filter the data by a specific attribute.
18802      * @param {String/RegExp} value Either string that the attribute value
18803      * should start with or a RegExp to test against the attribute
18804      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18805      * @param {TreeNode} startNode (optional) The node to start the filter at.
18806      */
18807     filter : function(value, attr, startNode){
18808         attr = attr || "text";
18809         var f;
18810         if(typeof value == "string"){
18811             var vlen = value.length;
18812             // auto clear empty filter
18813             if(vlen == 0 && this.clearBlank){
18814                 this.clear();
18815                 return;
18816             }
18817             value = value.toLowerCase();
18818             f = function(n){
18819                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18820             };
18821         }else if(value.exec){ // regex?
18822             f = function(n){
18823                 return value.test(n.attributes[attr]);
18824             };
18825         }else{
18826             throw 'Illegal filter type, must be string or regex';
18827         }
18828         this.filterBy(f, null, startNode);
18829         },
18830
18831     /**
18832      * Filter by a function. The passed function will be called with each
18833      * node in the tree (or from the startNode). If the function returns true, the node is kept
18834      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18835      * @param {Function} fn The filter function
18836      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18837      */
18838     filterBy : function(fn, scope, startNode){
18839         startNode = startNode || this.tree.root;
18840         if(this.autoClear){
18841             this.clear();
18842         }
18843         var af = this.filtered, rv = this.reverse;
18844         var f = function(n){
18845             if(n == startNode){
18846                 return true;
18847             }
18848             if(af[n.id]){
18849                 return false;
18850             }
18851             var m = fn.call(scope || n, n);
18852             if(!m || rv){
18853                 af[n.id] = n;
18854                 n.ui.hide();
18855                 return false;
18856             }
18857             return true;
18858         };
18859         startNode.cascade(f);
18860         if(this.remove){
18861            for(var id in af){
18862                if(typeof id != "function"){
18863                    var n = af[id];
18864                    if(n && n.parentNode){
18865                        n.parentNode.removeChild(n);
18866                    }
18867                }
18868            }
18869         }
18870     },
18871
18872     /**
18873      * Clears the current filter. Note: with the "remove" option
18874      * set a filter cannot be cleared.
18875      */
18876     clear : function(){
18877         var t = this.tree;
18878         var af = this.filtered;
18879         for(var id in af){
18880             if(typeof id != "function"){
18881                 var n = af[id];
18882                 if(n){
18883                     n.ui.show();
18884                 }
18885             }
18886         }
18887         this.filtered = {};
18888     }
18889 };
18890 /*
18891  * Based on:
18892  * Ext JS Library 1.1.1
18893  * Copyright(c) 2006-2007, Ext JS, LLC.
18894  *
18895  * Originally Released Under LGPL - original licence link has changed is not relivant.
18896  *
18897  * Fork - LGPL
18898  * <script type="text/javascript">
18899  */
18900  
18901
18902 /**
18903  * @class Roo.tree.TreeSorter
18904  * Provides sorting of nodes in a TreePanel
18905  * 
18906  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18907  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18908  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18909  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18910  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18911  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18912  * @constructor
18913  * @param {TreePanel} tree
18914  * @param {Object} config
18915  */
18916 Roo.tree.TreeSorter = function(tree, config){
18917     Roo.apply(this, config);
18918     tree.on("beforechildrenrendered", this.doSort, this);
18919     tree.on("append", this.updateSort, this);
18920     tree.on("insert", this.updateSort, this);
18921     
18922     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18923     var p = this.property || "text";
18924     var sortType = this.sortType;
18925     var fs = this.folderSort;
18926     var cs = this.caseSensitive === true;
18927     var leafAttr = this.leafAttr || 'leaf';
18928
18929     this.sortFn = function(n1, n2){
18930         if(fs){
18931             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18932                 return 1;
18933             }
18934             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18935                 return -1;
18936             }
18937         }
18938         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18939         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18940         if(v1 < v2){
18941                         return dsc ? +1 : -1;
18942                 }else if(v1 > v2){
18943                         return dsc ? -1 : +1;
18944         }else{
18945                 return 0;
18946         }
18947     };
18948 };
18949
18950 Roo.tree.TreeSorter.prototype = {
18951     doSort : function(node){
18952         node.sort(this.sortFn);
18953     },
18954     
18955     compareNodes : function(n1, n2){
18956         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18957     },
18958     
18959     updateSort : function(tree, node){
18960         if(node.childrenRendered){
18961             this.doSort.defer(1, this, [node]);
18962         }
18963     }
18964 };/*
18965  * Based on:
18966  * Ext JS Library 1.1.1
18967  * Copyright(c) 2006-2007, Ext JS, LLC.
18968  *
18969  * Originally Released Under LGPL - original licence link has changed is not relivant.
18970  *
18971  * Fork - LGPL
18972  * <script type="text/javascript">
18973  */
18974
18975 if(Roo.dd.DropZone){
18976     
18977 Roo.tree.TreeDropZone = function(tree, config){
18978     this.allowParentInsert = false;
18979     this.allowContainerDrop = false;
18980     this.appendOnly = false;
18981     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18982     this.tree = tree;
18983     this.lastInsertClass = "x-tree-no-status";
18984     this.dragOverData = {};
18985 };
18986
18987 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18988     ddGroup : "TreeDD",
18989     scroll:  true,
18990     
18991     expandDelay : 1000,
18992     
18993     expandNode : function(node){
18994         if(node.hasChildNodes() && !node.isExpanded()){
18995             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18996         }
18997     },
18998     
18999     queueExpand : function(node){
19000         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19001     },
19002     
19003     cancelExpand : function(){
19004         if(this.expandProcId){
19005             clearTimeout(this.expandProcId);
19006             this.expandProcId = false;
19007         }
19008     },
19009     
19010     isValidDropPoint : function(n, pt, dd, e, data){
19011         if(!n || !data){ return false; }
19012         var targetNode = n.node;
19013         var dropNode = data.node;
19014         // default drop rules
19015         if(!(targetNode && targetNode.isTarget && pt)){
19016             return false;
19017         }
19018         if(pt == "append" && targetNode.allowChildren === false){
19019             return false;
19020         }
19021         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19022             return false;
19023         }
19024         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19025             return false;
19026         }
19027         // reuse the object
19028         var overEvent = this.dragOverData;
19029         overEvent.tree = this.tree;
19030         overEvent.target = targetNode;
19031         overEvent.data = data;
19032         overEvent.point = pt;
19033         overEvent.source = dd;
19034         overEvent.rawEvent = e;
19035         overEvent.dropNode = dropNode;
19036         overEvent.cancel = false;  
19037         var result = this.tree.fireEvent("nodedragover", overEvent);
19038         return overEvent.cancel === false && result !== false;
19039     },
19040     
19041     getDropPoint : function(e, n, dd)
19042     {
19043         var tn = n.node;
19044         if(tn.isRoot){
19045             return tn.allowChildren !== false ? "append" : false; // always append for root
19046         }
19047         var dragEl = n.ddel;
19048         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19049         var y = Roo.lib.Event.getPageY(e);
19050         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19051         
19052         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19053         var noAppend = tn.allowChildren === false;
19054         if(this.appendOnly || tn.parentNode.allowChildren === false){
19055             return noAppend ? false : "append";
19056         }
19057         var noBelow = false;
19058         if(!this.allowParentInsert){
19059             noBelow = tn.hasChildNodes() && tn.isExpanded();
19060         }
19061         var q = (b - t) / (noAppend ? 2 : 3);
19062         if(y >= t && y < (t + q)){
19063             return "above";
19064         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19065             return "below";
19066         }else{
19067             return "append";
19068         }
19069     },
19070     
19071     onNodeEnter : function(n, dd, e, data)
19072     {
19073         this.cancelExpand();
19074     },
19075     
19076     onNodeOver : function(n, dd, e, data)
19077     {
19078        
19079         var pt = this.getDropPoint(e, n, dd);
19080         var node = n.node;
19081         
19082         // auto node expand check
19083         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19084             this.queueExpand(node);
19085         }else if(pt != "append"){
19086             this.cancelExpand();
19087         }
19088         
19089         // set the insert point style on the target node
19090         var returnCls = this.dropNotAllowed;
19091         if(this.isValidDropPoint(n, pt, dd, e, data)){
19092            if(pt){
19093                var el = n.ddel;
19094                var cls;
19095                if(pt == "above"){
19096                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19097                    cls = "x-tree-drag-insert-above";
19098                }else if(pt == "below"){
19099                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19100                    cls = "x-tree-drag-insert-below";
19101                }else{
19102                    returnCls = "x-tree-drop-ok-append";
19103                    cls = "x-tree-drag-append";
19104                }
19105                if(this.lastInsertClass != cls){
19106                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19107                    this.lastInsertClass = cls;
19108                }
19109            }
19110        }
19111        return returnCls;
19112     },
19113     
19114     onNodeOut : function(n, dd, e, data){
19115         
19116         this.cancelExpand();
19117         this.removeDropIndicators(n);
19118     },
19119     
19120     onNodeDrop : function(n, dd, e, data){
19121         var point = this.getDropPoint(e, n, dd);
19122         var targetNode = n.node;
19123         targetNode.ui.startDrop();
19124         if(!this.isValidDropPoint(n, point, dd, e, data)){
19125             targetNode.ui.endDrop();
19126             return false;
19127         }
19128         // first try to find the drop node
19129         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19130         var dropEvent = {
19131             tree : this.tree,
19132             target: targetNode,
19133             data: data,
19134             point: point,
19135             source: dd,
19136             rawEvent: e,
19137             dropNode: dropNode,
19138             cancel: !dropNode   
19139         };
19140         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19141         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19142             targetNode.ui.endDrop();
19143             return false;
19144         }
19145         // allow target changing
19146         targetNode = dropEvent.target;
19147         if(point == "append" && !targetNode.isExpanded()){
19148             targetNode.expand(false, null, function(){
19149                 this.completeDrop(dropEvent);
19150             }.createDelegate(this));
19151         }else{
19152             this.completeDrop(dropEvent);
19153         }
19154         return true;
19155     },
19156     
19157     completeDrop : function(de){
19158         var ns = de.dropNode, p = de.point, t = de.target;
19159         if(!(ns instanceof Array)){
19160             ns = [ns];
19161         }
19162         var n;
19163         for(var i = 0, len = ns.length; i < len; i++){
19164             n = ns[i];
19165             if(p == "above"){
19166                 t.parentNode.insertBefore(n, t);
19167             }else if(p == "below"){
19168                 t.parentNode.insertBefore(n, t.nextSibling);
19169             }else{
19170                 t.appendChild(n);
19171             }
19172         }
19173         n.ui.focus();
19174         if(this.tree.hlDrop){
19175             n.ui.highlight();
19176         }
19177         t.ui.endDrop();
19178         this.tree.fireEvent("nodedrop", de);
19179     },
19180     
19181     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19182         if(this.tree.hlDrop){
19183             dropNode.ui.focus();
19184             dropNode.ui.highlight();
19185         }
19186         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19187     },
19188     
19189     getTree : function(){
19190         return this.tree;
19191     },
19192     
19193     removeDropIndicators : function(n){
19194         if(n && n.ddel){
19195             var el = n.ddel;
19196             Roo.fly(el).removeClass([
19197                     "x-tree-drag-insert-above",
19198                     "x-tree-drag-insert-below",
19199                     "x-tree-drag-append"]);
19200             this.lastInsertClass = "_noclass";
19201         }
19202     },
19203     
19204     beforeDragDrop : function(target, e, id){
19205         this.cancelExpand();
19206         return true;
19207     },
19208     
19209     afterRepair : function(data){
19210         if(data && Roo.enableFx){
19211             data.node.ui.highlight();
19212         }
19213         this.hideProxy();
19214     } 
19215     
19216 });
19217
19218 }
19219 /*
19220  * Based on:
19221  * Ext JS Library 1.1.1
19222  * Copyright(c) 2006-2007, Ext JS, LLC.
19223  *
19224  * Originally Released Under LGPL - original licence link has changed is not relivant.
19225  *
19226  * Fork - LGPL
19227  * <script type="text/javascript">
19228  */
19229  
19230
19231 if(Roo.dd.DragZone){
19232 Roo.tree.TreeDragZone = function(tree, config){
19233     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19234     this.tree = tree;
19235 };
19236
19237 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19238     ddGroup : "TreeDD",
19239    
19240     onBeforeDrag : function(data, e){
19241         var n = data.node;
19242         return n && n.draggable && !n.disabled;
19243     },
19244      
19245     
19246     onInitDrag : function(e){
19247         var data = this.dragData;
19248         this.tree.getSelectionModel().select(data.node);
19249         this.proxy.update("");
19250         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19251         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19252     },
19253     
19254     getRepairXY : function(e, data){
19255         return data.node.ui.getDDRepairXY();
19256     },
19257     
19258     onEndDrag : function(data, e){
19259         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19260         
19261         
19262     },
19263     
19264     onValidDrop : function(dd, e, id){
19265         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19266         this.hideProxy();
19267     },
19268     
19269     beforeInvalidDrop : function(e, id){
19270         // this scrolls the original position back into view
19271         var sm = this.tree.getSelectionModel();
19272         sm.clearSelections();
19273         sm.select(this.dragData.node);
19274     }
19275 });
19276 }/*
19277  * Based on:
19278  * Ext JS Library 1.1.1
19279  * Copyright(c) 2006-2007, Ext JS, LLC.
19280  *
19281  * Originally Released Under LGPL - original licence link has changed is not relivant.
19282  *
19283  * Fork - LGPL
19284  * <script type="text/javascript">
19285  */
19286 /**
19287  * @class Roo.tree.TreeEditor
19288  * @extends Roo.Editor
19289  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19290  * as the editor field.
19291  * @constructor
19292  * @param {Object} config (used to be the tree panel.)
19293  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19294  * 
19295  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19296  * @cfg {Roo.form.TextField|Object} field The field configuration
19297  *
19298  * 
19299  */
19300 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19301     var tree = config;
19302     var field;
19303     if (oldconfig) { // old style..
19304         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19305     } else {
19306         // new style..
19307         tree = config.tree;
19308         config.field = config.field  || {};
19309         config.field.xtype = 'TextField';
19310         field = Roo.factory(config.field, Roo.form);
19311     }
19312     config = config || {};
19313     
19314     
19315     this.addEvents({
19316         /**
19317          * @event beforenodeedit
19318          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19319          * false from the handler of this event.
19320          * @param {Editor} this
19321          * @param {Roo.tree.Node} node 
19322          */
19323         "beforenodeedit" : true
19324     });
19325     
19326     //Roo.log(config);
19327     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19328
19329     this.tree = tree;
19330
19331     tree.on('beforeclick', this.beforeNodeClick, this);
19332     tree.getTreeEl().on('mousedown', this.hide, this);
19333     this.on('complete', this.updateNode, this);
19334     this.on('beforestartedit', this.fitToTree, this);
19335     this.on('startedit', this.bindScroll, this, {delay:10});
19336     this.on('specialkey', this.onSpecialKey, this);
19337 };
19338
19339 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19340     /**
19341      * @cfg {String} alignment
19342      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19343      */
19344     alignment: "l-l",
19345     // inherit
19346     autoSize: false,
19347     /**
19348      * @cfg {Boolean} hideEl
19349      * True to hide the bound element while the editor is displayed (defaults to false)
19350      */
19351     hideEl : false,
19352     /**
19353      * @cfg {String} cls
19354      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19355      */
19356     cls: "x-small-editor x-tree-editor",
19357     /**
19358      * @cfg {Boolean} shim
19359      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19360      */
19361     shim:false,
19362     // inherit
19363     shadow:"frame",
19364     /**
19365      * @cfg {Number} maxWidth
19366      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19367      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19368      * scroll and client offsets into account prior to each edit.
19369      */
19370     maxWidth: 250,
19371
19372     editDelay : 350,
19373
19374     // private
19375     fitToTree : function(ed, el){
19376         var td = this.tree.getTreeEl().dom, nd = el.dom;
19377         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19378             td.scrollLeft = nd.offsetLeft;
19379         }
19380         var w = Math.min(
19381                 this.maxWidth,
19382                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19383         this.setSize(w, '');
19384         
19385         return this.fireEvent('beforenodeedit', this, this.editNode);
19386         
19387     },
19388
19389     // private
19390     triggerEdit : function(node){
19391         this.completeEdit();
19392         this.editNode = node;
19393         this.startEdit(node.ui.textNode, node.text);
19394     },
19395
19396     // private
19397     bindScroll : function(){
19398         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19399     },
19400
19401     // private
19402     beforeNodeClick : function(node, e){
19403         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19404         this.lastClick = new Date();
19405         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19406             e.stopEvent();
19407             this.triggerEdit(node);
19408             return false;
19409         }
19410         return true;
19411     },
19412
19413     // private
19414     updateNode : function(ed, value){
19415         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19416         this.editNode.setText(value);
19417     },
19418
19419     // private
19420     onHide : function(){
19421         Roo.tree.TreeEditor.superclass.onHide.call(this);
19422         if(this.editNode){
19423             this.editNode.ui.focus();
19424         }
19425     },
19426
19427     // private
19428     onSpecialKey : function(field, e){
19429         var k = e.getKey();
19430         if(k == e.ESC){
19431             e.stopEvent();
19432             this.cancelEdit();
19433         }else if(k == e.ENTER && !e.hasModifier()){
19434             e.stopEvent();
19435             this.completeEdit();
19436         }
19437     }
19438 });//<Script type="text/javascript">
19439 /*
19440  * Based on:
19441  * Ext JS Library 1.1.1
19442  * Copyright(c) 2006-2007, Ext JS, LLC.
19443  *
19444  * Originally Released Under LGPL - original licence link has changed is not relivant.
19445  *
19446  * Fork - LGPL
19447  * <script type="text/javascript">
19448  */
19449  
19450 /**
19451  * Not documented??? - probably should be...
19452  */
19453
19454 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19455     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19456     
19457     renderElements : function(n, a, targetNode, bulkRender){
19458         //consel.log("renderElements?");
19459         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19460
19461         var t = n.getOwnerTree();
19462         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19463         
19464         var cols = t.columns;
19465         var bw = t.borderWidth;
19466         var c = cols[0];
19467         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19468          var cb = typeof a.checked == "boolean";
19469         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19470         var colcls = 'x-t-' + tid + '-c0';
19471         var buf = [
19472             '<li class="x-tree-node">',
19473             
19474                 
19475                 '<div class="x-tree-node-el ', a.cls,'">',
19476                     // extran...
19477                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19478                 
19479                 
19480                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19481                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19482                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19483                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19484                            (a.iconCls ? ' '+a.iconCls : ''),
19485                            '" unselectable="on" />',
19486                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19487                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19488                              
19489                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19490                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19491                             '<span unselectable="on" qtip="' + tx + '">',
19492                              tx,
19493                              '</span></a>' ,
19494                     '</div>',
19495                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19496                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19497                  ];
19498         for(var i = 1, len = cols.length; i < len; i++){
19499             c = cols[i];
19500             colcls = 'x-t-' + tid + '-c' +i;
19501             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19502             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19503                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19504                       "</div>");
19505          }
19506          
19507          buf.push(
19508             '</a>',
19509             '<div class="x-clear"></div></div>',
19510             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19511             "</li>");
19512         
19513         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19514             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19515                                 n.nextSibling.ui.getEl(), buf.join(""));
19516         }else{
19517             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19518         }
19519         var el = this.wrap.firstChild;
19520         this.elRow = el;
19521         this.elNode = el.firstChild;
19522         this.ranchor = el.childNodes[1];
19523         this.ctNode = this.wrap.childNodes[1];
19524         var cs = el.firstChild.childNodes;
19525         this.indentNode = cs[0];
19526         this.ecNode = cs[1];
19527         this.iconNode = cs[2];
19528         var index = 3;
19529         if(cb){
19530             this.checkbox = cs[3];
19531             index++;
19532         }
19533         this.anchor = cs[index];
19534         
19535         this.textNode = cs[index].firstChild;
19536         
19537         //el.on("click", this.onClick, this);
19538         //el.on("dblclick", this.onDblClick, this);
19539         
19540         
19541        // console.log(this);
19542     },
19543     initEvents : function(){
19544         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19545         
19546             
19547         var a = this.ranchor;
19548
19549         var el = Roo.get(a);
19550
19551         if(Roo.isOpera){ // opera render bug ignores the CSS
19552             el.setStyle("text-decoration", "none");
19553         }
19554
19555         el.on("click", this.onClick, this);
19556         el.on("dblclick", this.onDblClick, this);
19557         el.on("contextmenu", this.onContextMenu, this);
19558         
19559     },
19560     
19561     /*onSelectedChange : function(state){
19562         if(state){
19563             this.focus();
19564             this.addClass("x-tree-selected");
19565         }else{
19566             //this.blur();
19567             this.removeClass("x-tree-selected");
19568         }
19569     },*/
19570     addClass : function(cls){
19571         if(this.elRow){
19572             Roo.fly(this.elRow).addClass(cls);
19573         }
19574         
19575     },
19576     
19577     
19578     removeClass : function(cls){
19579         if(this.elRow){
19580             Roo.fly(this.elRow).removeClass(cls);
19581         }
19582     }
19583
19584     
19585     
19586 });//<Script type="text/javascript">
19587
19588 /*
19589  * Based on:
19590  * Ext JS Library 1.1.1
19591  * Copyright(c) 2006-2007, Ext JS, LLC.
19592  *
19593  * Originally Released Under LGPL - original licence link has changed is not relivant.
19594  *
19595  * Fork - LGPL
19596  * <script type="text/javascript">
19597  */
19598  
19599
19600 /**
19601  * @class Roo.tree.ColumnTree
19602  * @extends Roo.data.TreePanel
19603  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19604  * @cfg {int} borderWidth  compined right/left border allowance
19605  * @constructor
19606  * @param {String/HTMLElement/Element} el The container element
19607  * @param {Object} config
19608  */
19609 Roo.tree.ColumnTree =  function(el, config)
19610 {
19611    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19612    this.addEvents({
19613         /**
19614         * @event resize
19615         * Fire this event on a container when it resizes
19616         * @param {int} w Width
19617         * @param {int} h Height
19618         */
19619        "resize" : true
19620     });
19621     this.on('resize', this.onResize, this);
19622 };
19623
19624 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19625     //lines:false,
19626     
19627     
19628     borderWidth: Roo.isBorderBox ? 0 : 2, 
19629     headEls : false,
19630     
19631     render : function(){
19632         // add the header.....
19633        
19634         Roo.tree.ColumnTree.superclass.render.apply(this);
19635         
19636         this.el.addClass('x-column-tree');
19637         
19638         this.headers = this.el.createChild(
19639             {cls:'x-tree-headers'},this.innerCt.dom);
19640    
19641         var cols = this.columns, c;
19642         var totalWidth = 0;
19643         this.headEls = [];
19644         var  len = cols.length;
19645         for(var i = 0; i < len; i++){
19646              c = cols[i];
19647              totalWidth += c.width;
19648             this.headEls.push(this.headers.createChild({
19649                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19650                  cn: {
19651                      cls:'x-tree-hd-text',
19652                      html: c.header
19653                  },
19654                  style:'width:'+(c.width-this.borderWidth)+'px;'
19655              }));
19656         }
19657         this.headers.createChild({cls:'x-clear'});
19658         // prevent floats from wrapping when clipped
19659         this.headers.setWidth(totalWidth);
19660         //this.innerCt.setWidth(totalWidth);
19661         this.innerCt.setStyle({ overflow: 'auto' });
19662         this.onResize(this.width, this.height);
19663              
19664         
19665     },
19666     onResize : function(w,h)
19667     {
19668         this.height = h;
19669         this.width = w;
19670         // resize cols..
19671         this.innerCt.setWidth(this.width);
19672         this.innerCt.setHeight(this.height-20);
19673         
19674         // headers...
19675         var cols = this.columns, c;
19676         var totalWidth = 0;
19677         var expEl = false;
19678         var len = cols.length;
19679         for(var i = 0; i < len; i++){
19680             c = cols[i];
19681             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19682                 // it's the expander..
19683                 expEl  = this.headEls[i];
19684                 continue;
19685             }
19686             totalWidth += c.width;
19687             
19688         }
19689         if (expEl) {
19690             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19691         }
19692         this.headers.setWidth(w-20);
19693
19694         
19695         
19696         
19697     }
19698 });
19699 /*
19700  * Based on:
19701  * Ext JS Library 1.1.1
19702  * Copyright(c) 2006-2007, Ext JS, LLC.
19703  *
19704  * Originally Released Under LGPL - original licence link has changed is not relivant.
19705  *
19706  * Fork - LGPL
19707  * <script type="text/javascript">
19708  */
19709  
19710 /**
19711  * @class Roo.menu.Menu
19712  * @extends Roo.util.Observable
19713  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19714  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19715  * @constructor
19716  * Creates a new Menu
19717  * @param {Object} config Configuration options
19718  */
19719 Roo.menu.Menu = function(config){
19720     Roo.apply(this, config);
19721     this.id = this.id || Roo.id();
19722     this.addEvents({
19723         /**
19724          * @event beforeshow
19725          * Fires before this menu is displayed
19726          * @param {Roo.menu.Menu} this
19727          */
19728         beforeshow : true,
19729         /**
19730          * @event beforehide
19731          * Fires before this menu is hidden
19732          * @param {Roo.menu.Menu} this
19733          */
19734         beforehide : true,
19735         /**
19736          * @event show
19737          * Fires after this menu is displayed
19738          * @param {Roo.menu.Menu} this
19739          */
19740         show : true,
19741         /**
19742          * @event hide
19743          * Fires after this menu is hidden
19744          * @param {Roo.menu.Menu} this
19745          */
19746         hide : true,
19747         /**
19748          * @event click
19749          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19750          * @param {Roo.menu.Menu} this
19751          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19752          * @param {Roo.EventObject} e
19753          */
19754         click : true,
19755         /**
19756          * @event mouseover
19757          * Fires when the mouse is hovering over this menu
19758          * @param {Roo.menu.Menu} this
19759          * @param {Roo.EventObject} e
19760          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19761          */
19762         mouseover : true,
19763         /**
19764          * @event mouseout
19765          * Fires when the mouse exits this menu
19766          * @param {Roo.menu.Menu} this
19767          * @param {Roo.EventObject} e
19768          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19769          */
19770         mouseout : true,
19771         /**
19772          * @event itemclick
19773          * Fires when a menu item contained in this menu is clicked
19774          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19775          * @param {Roo.EventObject} e
19776          */
19777         itemclick: true
19778     });
19779     if (this.registerMenu) {
19780         Roo.menu.MenuMgr.register(this);
19781     }
19782     
19783     var mis = this.items;
19784     this.items = new Roo.util.MixedCollection();
19785     if(mis){
19786         this.add.apply(this, mis);
19787     }
19788 };
19789
19790 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19791     /**
19792      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19793      */
19794     minWidth : 120,
19795     /**
19796      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19797      * for bottom-right shadow (defaults to "sides")
19798      */
19799     shadow : "sides",
19800     /**
19801      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19802      * this menu (defaults to "tl-tr?")
19803      */
19804     subMenuAlign : "tl-tr?",
19805     /**
19806      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19807      * relative to its element of origin (defaults to "tl-bl?")
19808      */
19809     defaultAlign : "tl-bl?",
19810     /**
19811      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19812      */
19813     allowOtherMenus : false,
19814     /**
19815      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19816      */
19817     registerMenu : true,
19818
19819     hidden:true,
19820
19821     // private
19822     render : function(){
19823         if(this.el){
19824             return;
19825         }
19826         var el = this.el = new Roo.Layer({
19827             cls: "x-menu",
19828             shadow:this.shadow,
19829             constrain: false,
19830             parentEl: this.parentEl || document.body,
19831             zindex:15000
19832         });
19833
19834         this.keyNav = new Roo.menu.MenuNav(this);
19835
19836         if(this.plain){
19837             el.addClass("x-menu-plain");
19838         }
19839         if(this.cls){
19840             el.addClass(this.cls);
19841         }
19842         // generic focus element
19843         this.focusEl = el.createChild({
19844             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19845         });
19846         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19847         ul.on("click", this.onClick, this);
19848         ul.on("mouseover", this.onMouseOver, this);
19849         ul.on("mouseout", this.onMouseOut, this);
19850         this.items.each(function(item){
19851             var li = document.createElement("li");
19852             li.className = "x-menu-list-item";
19853             ul.dom.appendChild(li);
19854             item.render(li, this);
19855         }, this);
19856         this.ul = ul;
19857         this.autoWidth();
19858     },
19859
19860     // private
19861     autoWidth : function(){
19862         var el = this.el, ul = this.ul;
19863         if(!el){
19864             return;
19865         }
19866         var w = this.width;
19867         if(w){
19868             el.setWidth(w);
19869         }else if(Roo.isIE){
19870             el.setWidth(this.minWidth);
19871             var t = el.dom.offsetWidth; // force recalc
19872             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19873         }
19874     },
19875
19876     // private
19877     delayAutoWidth : function(){
19878         if(this.rendered){
19879             if(!this.awTask){
19880                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19881             }
19882             this.awTask.delay(20);
19883         }
19884     },
19885
19886     // private
19887     findTargetItem : function(e){
19888         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19889         if(t && t.menuItemId){
19890             return this.items.get(t.menuItemId);
19891         }
19892     },
19893
19894     // private
19895     onClick : function(e){
19896         var t;
19897         if(t = this.findTargetItem(e)){
19898             t.onClick(e);
19899             this.fireEvent("click", this, t, e);
19900         }
19901     },
19902
19903     // private
19904     setActiveItem : function(item, autoExpand){
19905         if(item != this.activeItem){
19906             if(this.activeItem){
19907                 this.activeItem.deactivate();
19908             }
19909             this.activeItem = item;
19910             item.activate(autoExpand);
19911         }else if(autoExpand){
19912             item.expandMenu();
19913         }
19914     },
19915
19916     // private
19917     tryActivate : function(start, step){
19918         var items = this.items;
19919         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19920             var item = items.get(i);
19921             if(!item.disabled && item.canActivate){
19922                 this.setActiveItem(item, false);
19923                 return item;
19924             }
19925         }
19926         return false;
19927     },
19928
19929     // private
19930     onMouseOver : function(e){
19931         var t;
19932         if(t = this.findTargetItem(e)){
19933             if(t.canActivate && !t.disabled){
19934                 this.setActiveItem(t, true);
19935             }
19936         }
19937         this.fireEvent("mouseover", this, e, t);
19938     },
19939
19940     // private
19941     onMouseOut : function(e){
19942         var t;
19943         if(t = this.findTargetItem(e)){
19944             if(t == this.activeItem && t.shouldDeactivate(e)){
19945                 this.activeItem.deactivate();
19946                 delete this.activeItem;
19947             }
19948         }
19949         this.fireEvent("mouseout", this, e, t);
19950     },
19951
19952     /**
19953      * Read-only.  Returns true if the menu is currently displayed, else false.
19954      * @type Boolean
19955      */
19956     isVisible : function(){
19957         return this.el && !this.hidden;
19958     },
19959
19960     /**
19961      * Displays this menu relative to another element
19962      * @param {String/HTMLElement/Roo.Element} element The element to align to
19963      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19964      * the element (defaults to this.defaultAlign)
19965      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19966      */
19967     show : function(el, pos, parentMenu){
19968         this.parentMenu = parentMenu;
19969         if(!this.el){
19970             this.render();
19971         }
19972         this.fireEvent("beforeshow", this);
19973         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19974     },
19975
19976     /**
19977      * Displays this menu at a specific xy position
19978      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19979      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19980      */
19981     showAt : function(xy, parentMenu, /* private: */_e){
19982         this.parentMenu = parentMenu;
19983         if(!this.el){
19984             this.render();
19985         }
19986         if(_e !== false){
19987             this.fireEvent("beforeshow", this);
19988             xy = this.el.adjustForConstraints(xy);
19989         }
19990         this.el.setXY(xy);
19991         this.el.show();
19992         this.hidden = false;
19993         this.focus();
19994         this.fireEvent("show", this);
19995     },
19996
19997     focus : function(){
19998         if(!this.hidden){
19999             this.doFocus.defer(50, this);
20000         }
20001     },
20002
20003     doFocus : function(){
20004         if(!this.hidden){
20005             this.focusEl.focus();
20006         }
20007     },
20008
20009     /**
20010      * Hides this menu and optionally all parent menus
20011      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20012      */
20013     hide : function(deep){
20014         if(this.el && this.isVisible()){
20015             this.fireEvent("beforehide", this);
20016             if(this.activeItem){
20017                 this.activeItem.deactivate();
20018                 this.activeItem = null;
20019             }
20020             this.el.hide();
20021             this.hidden = true;
20022             this.fireEvent("hide", this);
20023         }
20024         if(deep === true && this.parentMenu){
20025             this.parentMenu.hide(true);
20026         }
20027     },
20028
20029     /**
20030      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20031      * Any of the following are valid:
20032      * <ul>
20033      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20034      * <li>An HTMLElement object which will be converted to a menu item</li>
20035      * <li>A menu item config object that will be created as a new menu item</li>
20036      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20037      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20038      * </ul>
20039      * Usage:
20040      * <pre><code>
20041 // Create the menu
20042 var menu = new Roo.menu.Menu();
20043
20044 // Create a menu item to add by reference
20045 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20046
20047 // Add a bunch of items at once using different methods.
20048 // Only the last item added will be returned.
20049 var item = menu.add(
20050     menuItem,                // add existing item by ref
20051     'Dynamic Item',          // new TextItem
20052     '-',                     // new separator
20053     { text: 'Config Item' }  // new item by config
20054 );
20055 </code></pre>
20056      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20057      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20058      */
20059     add : function(){
20060         var a = arguments, l = a.length, item;
20061         for(var i = 0; i < l; i++){
20062             var el = a[i];
20063             if ((typeof(el) == "object") && el.xtype && el.xns) {
20064                 el = Roo.factory(el, Roo.menu);
20065             }
20066             
20067             if(el.render){ // some kind of Item
20068                 item = this.addItem(el);
20069             }else if(typeof el == "string"){ // string
20070                 if(el == "separator" || el == "-"){
20071                     item = this.addSeparator();
20072                 }else{
20073                     item = this.addText(el);
20074                 }
20075             }else if(el.tagName || el.el){ // element
20076                 item = this.addElement(el);
20077             }else if(typeof el == "object"){ // must be menu item config?
20078                 item = this.addMenuItem(el);
20079             }
20080         }
20081         return item;
20082     },
20083
20084     /**
20085      * Returns this menu's underlying {@link Roo.Element} object
20086      * @return {Roo.Element} The element
20087      */
20088     getEl : function(){
20089         if(!this.el){
20090             this.render();
20091         }
20092         return this.el;
20093     },
20094
20095     /**
20096      * Adds a separator bar to the menu
20097      * @return {Roo.menu.Item} The menu item that was added
20098      */
20099     addSeparator : function(){
20100         return this.addItem(new Roo.menu.Separator());
20101     },
20102
20103     /**
20104      * Adds an {@link Roo.Element} object to the menu
20105      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20106      * @return {Roo.menu.Item} The menu item that was added
20107      */
20108     addElement : function(el){
20109         return this.addItem(new Roo.menu.BaseItem(el));
20110     },
20111
20112     /**
20113      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20114      * @param {Roo.menu.Item} item The menu item to add
20115      * @return {Roo.menu.Item} The menu item that was added
20116      */
20117     addItem : function(item){
20118         this.items.add(item);
20119         if(this.ul){
20120             var li = document.createElement("li");
20121             li.className = "x-menu-list-item";
20122             this.ul.dom.appendChild(li);
20123             item.render(li, this);
20124             this.delayAutoWidth();
20125         }
20126         return item;
20127     },
20128
20129     /**
20130      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20131      * @param {Object} config A MenuItem config object
20132      * @return {Roo.menu.Item} The menu item that was added
20133      */
20134     addMenuItem : function(config){
20135         if(!(config instanceof Roo.menu.Item)){
20136             if(typeof config.checked == "boolean"){ // must be check menu item config?
20137                 config = new Roo.menu.CheckItem(config);
20138             }else{
20139                 config = new Roo.menu.Item(config);
20140             }
20141         }
20142         return this.addItem(config);
20143     },
20144
20145     /**
20146      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20147      * @param {String} text The text to display in the menu item
20148      * @return {Roo.menu.Item} The menu item that was added
20149      */
20150     addText : function(text){
20151         return this.addItem(new Roo.menu.TextItem({ text : text }));
20152     },
20153
20154     /**
20155      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20156      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20157      * @param {Roo.menu.Item} item The menu item to add
20158      * @return {Roo.menu.Item} The menu item that was added
20159      */
20160     insert : function(index, item){
20161         this.items.insert(index, item);
20162         if(this.ul){
20163             var li = document.createElement("li");
20164             li.className = "x-menu-list-item";
20165             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20166             item.render(li, this);
20167             this.delayAutoWidth();
20168         }
20169         return item;
20170     },
20171
20172     /**
20173      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20174      * @param {Roo.menu.Item} item The menu item to remove
20175      */
20176     remove : function(item){
20177         this.items.removeKey(item.id);
20178         item.destroy();
20179     },
20180
20181     /**
20182      * Removes and destroys all items in the menu
20183      */
20184     removeAll : function(){
20185         var f;
20186         while(f = this.items.first()){
20187             this.remove(f);
20188         }
20189     }
20190 });
20191
20192 // MenuNav is a private utility class used internally by the Menu
20193 Roo.menu.MenuNav = function(menu){
20194     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20195     this.scope = this.menu = menu;
20196 };
20197
20198 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20199     doRelay : function(e, h){
20200         var k = e.getKey();
20201         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20202             this.menu.tryActivate(0, 1);
20203             return false;
20204         }
20205         return h.call(this.scope || this, e, this.menu);
20206     },
20207
20208     up : function(e, m){
20209         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20210             m.tryActivate(m.items.length-1, -1);
20211         }
20212     },
20213
20214     down : function(e, m){
20215         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20216             m.tryActivate(0, 1);
20217         }
20218     },
20219
20220     right : function(e, m){
20221         if(m.activeItem){
20222             m.activeItem.expandMenu(true);
20223         }
20224     },
20225
20226     left : function(e, m){
20227         m.hide();
20228         if(m.parentMenu && m.parentMenu.activeItem){
20229             m.parentMenu.activeItem.activate();
20230         }
20231     },
20232
20233     enter : function(e, m){
20234         if(m.activeItem){
20235             e.stopPropagation();
20236             m.activeItem.onClick(e);
20237             m.fireEvent("click", this, m.activeItem);
20238             return true;
20239         }
20240     }
20241 });/*
20242  * Based on:
20243  * Ext JS Library 1.1.1
20244  * Copyright(c) 2006-2007, Ext JS, LLC.
20245  *
20246  * Originally Released Under LGPL - original licence link has changed is not relivant.
20247  *
20248  * Fork - LGPL
20249  * <script type="text/javascript">
20250  */
20251  
20252 /**
20253  * @class Roo.menu.MenuMgr
20254  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20255  * @singleton
20256  */
20257 Roo.menu.MenuMgr = function(){
20258    var menus, active, groups = {}, attached = false, lastShow = new Date();
20259
20260    // private - called when first menu is created
20261    function init(){
20262        menus = {};
20263        active = new Roo.util.MixedCollection();
20264        Roo.get(document).addKeyListener(27, function(){
20265            if(active.length > 0){
20266                hideAll();
20267            }
20268        });
20269    }
20270
20271    // private
20272    function hideAll(){
20273        if(active && active.length > 0){
20274            var c = active.clone();
20275            c.each(function(m){
20276                m.hide();
20277            });
20278        }
20279    }
20280
20281    // private
20282    function onHide(m){
20283        active.remove(m);
20284        if(active.length < 1){
20285            Roo.get(document).un("mousedown", onMouseDown);
20286            attached = false;
20287        }
20288    }
20289
20290    // private
20291    function onShow(m){
20292        var last = active.last();
20293        lastShow = new Date();
20294        active.add(m);
20295        if(!attached){
20296            Roo.get(document).on("mousedown", onMouseDown);
20297            attached = true;
20298        }
20299        if(m.parentMenu){
20300           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20301           m.parentMenu.activeChild = m;
20302        }else if(last && last.isVisible()){
20303           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20304        }
20305    }
20306
20307    // private
20308    function onBeforeHide(m){
20309        if(m.activeChild){
20310            m.activeChild.hide();
20311        }
20312        if(m.autoHideTimer){
20313            clearTimeout(m.autoHideTimer);
20314            delete m.autoHideTimer;
20315        }
20316    }
20317
20318    // private
20319    function onBeforeShow(m){
20320        var pm = m.parentMenu;
20321        if(!pm && !m.allowOtherMenus){
20322            hideAll();
20323        }else if(pm && pm.activeChild && active != m){
20324            pm.activeChild.hide();
20325        }
20326    }
20327
20328    // private
20329    function onMouseDown(e){
20330        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20331            hideAll();
20332        }
20333    }
20334
20335    // private
20336    function onBeforeCheck(mi, state){
20337        if(state){
20338            var g = groups[mi.group];
20339            for(var i = 0, l = g.length; i < l; i++){
20340                if(g[i] != mi){
20341                    g[i].setChecked(false);
20342                }
20343            }
20344        }
20345    }
20346
20347    return {
20348
20349        /**
20350         * Hides all menus that are currently visible
20351         */
20352        hideAll : function(){
20353             hideAll();  
20354        },
20355
20356        // private
20357        register : function(menu){
20358            if(!menus){
20359                init();
20360            }
20361            menus[menu.id] = menu;
20362            menu.on("beforehide", onBeforeHide);
20363            menu.on("hide", onHide);
20364            menu.on("beforeshow", onBeforeShow);
20365            menu.on("show", onShow);
20366            var g = menu.group;
20367            if(g && menu.events["checkchange"]){
20368                if(!groups[g]){
20369                    groups[g] = [];
20370                }
20371                groups[g].push(menu);
20372                menu.on("checkchange", onCheck);
20373            }
20374        },
20375
20376         /**
20377          * Returns a {@link Roo.menu.Menu} object
20378          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20379          * be used to generate and return a new Menu instance.
20380          */
20381        get : function(menu){
20382            if(typeof menu == "string"){ // menu id
20383                return menus[menu];
20384            }else if(menu.events){  // menu instance
20385                return menu;
20386            }else if(typeof menu.length == 'number'){ // array of menu items?
20387                return new Roo.menu.Menu({items:menu});
20388            }else{ // otherwise, must be a config
20389                return new Roo.menu.Menu(menu);
20390            }
20391        },
20392
20393        // private
20394        unregister : function(menu){
20395            delete menus[menu.id];
20396            menu.un("beforehide", onBeforeHide);
20397            menu.un("hide", onHide);
20398            menu.un("beforeshow", onBeforeShow);
20399            menu.un("show", onShow);
20400            var g = menu.group;
20401            if(g && menu.events["checkchange"]){
20402                groups[g].remove(menu);
20403                menu.un("checkchange", onCheck);
20404            }
20405        },
20406
20407        // private
20408        registerCheckable : function(menuItem){
20409            var g = menuItem.group;
20410            if(g){
20411                if(!groups[g]){
20412                    groups[g] = [];
20413                }
20414                groups[g].push(menuItem);
20415                menuItem.on("beforecheckchange", onBeforeCheck);
20416            }
20417        },
20418
20419        // private
20420        unregisterCheckable : function(menuItem){
20421            var g = menuItem.group;
20422            if(g){
20423                groups[g].remove(menuItem);
20424                menuItem.un("beforecheckchange", onBeforeCheck);
20425            }
20426        }
20427    };
20428 }();/*
20429  * Based on:
20430  * Ext JS Library 1.1.1
20431  * Copyright(c) 2006-2007, Ext JS, LLC.
20432  *
20433  * Originally Released Under LGPL - original licence link has changed is not relivant.
20434  *
20435  * Fork - LGPL
20436  * <script type="text/javascript">
20437  */
20438  
20439
20440 /**
20441  * @class Roo.menu.BaseItem
20442  * @extends Roo.Component
20443  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20444  * management and base configuration options shared by all menu components.
20445  * @constructor
20446  * Creates a new BaseItem
20447  * @param {Object} config Configuration options
20448  */
20449 Roo.menu.BaseItem = function(config){
20450     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20451
20452     this.addEvents({
20453         /**
20454          * @event click
20455          * Fires when this item is clicked
20456          * @param {Roo.menu.BaseItem} this
20457          * @param {Roo.EventObject} e
20458          */
20459         click: true,
20460         /**
20461          * @event activate
20462          * Fires when this item is activated
20463          * @param {Roo.menu.BaseItem} this
20464          */
20465         activate : true,
20466         /**
20467          * @event deactivate
20468          * Fires when this item is deactivated
20469          * @param {Roo.menu.BaseItem} this
20470          */
20471         deactivate : true
20472     });
20473
20474     if(this.handler){
20475         this.on("click", this.handler, this.scope, true);
20476     }
20477 };
20478
20479 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20480     /**
20481      * @cfg {Function} handler
20482      * A function that will handle the click event of this menu item (defaults to undefined)
20483      */
20484     /**
20485      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20486      */
20487     canActivate : false,
20488     /**
20489      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20490      */
20491     activeClass : "x-menu-item-active",
20492     /**
20493      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20494      */
20495     hideOnClick : true,
20496     /**
20497      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20498      */
20499     hideDelay : 100,
20500
20501     // private
20502     ctype: "Roo.menu.BaseItem",
20503
20504     // private
20505     actionMode : "container",
20506
20507     // private
20508     render : function(container, parentMenu){
20509         this.parentMenu = parentMenu;
20510         Roo.menu.BaseItem.superclass.render.call(this, container);
20511         this.container.menuItemId = this.id;
20512     },
20513
20514     // private
20515     onRender : function(container, position){
20516         this.el = Roo.get(this.el);
20517         container.dom.appendChild(this.el.dom);
20518     },
20519
20520     // private
20521     onClick : function(e){
20522         if(!this.disabled && this.fireEvent("click", this, e) !== false
20523                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20524             this.handleClick(e);
20525         }else{
20526             e.stopEvent();
20527         }
20528     },
20529
20530     // private
20531     activate : function(){
20532         if(this.disabled){
20533             return false;
20534         }
20535         var li = this.container;
20536         li.addClass(this.activeClass);
20537         this.region = li.getRegion().adjust(2, 2, -2, -2);
20538         this.fireEvent("activate", this);
20539         return true;
20540     },
20541
20542     // private
20543     deactivate : function(){
20544         this.container.removeClass(this.activeClass);
20545         this.fireEvent("deactivate", this);
20546     },
20547
20548     // private
20549     shouldDeactivate : function(e){
20550         return !this.region || !this.region.contains(e.getPoint());
20551     },
20552
20553     // private
20554     handleClick : function(e){
20555         if(this.hideOnClick){
20556             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20557         }
20558     },
20559
20560     // private
20561     expandMenu : function(autoActivate){
20562         // do nothing
20563     },
20564
20565     // private
20566     hideMenu : function(){
20567         // do nothing
20568     }
20569 });/*
20570  * Based on:
20571  * Ext JS Library 1.1.1
20572  * Copyright(c) 2006-2007, Ext JS, LLC.
20573  *
20574  * Originally Released Under LGPL - original licence link has changed is not relivant.
20575  *
20576  * Fork - LGPL
20577  * <script type="text/javascript">
20578  */
20579  
20580 /**
20581  * @class Roo.menu.Adapter
20582  * @extends Roo.menu.BaseItem
20583  * 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.
20584  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20585  * @constructor
20586  * Creates a new Adapter
20587  * @param {Object} config Configuration options
20588  */
20589 Roo.menu.Adapter = function(component, config){
20590     Roo.menu.Adapter.superclass.constructor.call(this, config);
20591     this.component = component;
20592 };
20593 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20594     // private
20595     canActivate : true,
20596
20597     // private
20598     onRender : function(container, position){
20599         this.component.render(container);
20600         this.el = this.component.getEl();
20601     },
20602
20603     // private
20604     activate : function(){
20605         if(this.disabled){
20606             return false;
20607         }
20608         this.component.focus();
20609         this.fireEvent("activate", this);
20610         return true;
20611     },
20612
20613     // private
20614     deactivate : function(){
20615         this.fireEvent("deactivate", this);
20616     },
20617
20618     // private
20619     disable : function(){
20620         this.component.disable();
20621         Roo.menu.Adapter.superclass.disable.call(this);
20622     },
20623
20624     // private
20625     enable : function(){
20626         this.component.enable();
20627         Roo.menu.Adapter.superclass.enable.call(this);
20628     }
20629 });/*
20630  * Based on:
20631  * Ext JS Library 1.1.1
20632  * Copyright(c) 2006-2007, Ext JS, LLC.
20633  *
20634  * Originally Released Under LGPL - original licence link has changed is not relivant.
20635  *
20636  * Fork - LGPL
20637  * <script type="text/javascript">
20638  */
20639
20640 /**
20641  * @class Roo.menu.TextItem
20642  * @extends Roo.menu.BaseItem
20643  * Adds a static text string to a menu, usually used as either a heading or group separator.
20644  * Note: old style constructor with text is still supported.
20645  * 
20646  * @constructor
20647  * Creates a new TextItem
20648  * @param {Object} cfg Configuration
20649  */
20650 Roo.menu.TextItem = function(cfg){
20651     if (typeof(cfg) == 'string') {
20652         this.text = cfg;
20653     } else {
20654         Roo.apply(this,cfg);
20655     }
20656     
20657     Roo.menu.TextItem.superclass.constructor.call(this);
20658 };
20659
20660 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20661     /**
20662      * @cfg {Boolean} text Text to show on item.
20663      */
20664     text : '',
20665     
20666     /**
20667      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20668      */
20669     hideOnClick : false,
20670     /**
20671      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20672      */
20673     itemCls : "x-menu-text",
20674
20675     // private
20676     onRender : function(){
20677         var s = document.createElement("span");
20678         s.className = this.itemCls;
20679         s.innerHTML = this.text;
20680         this.el = s;
20681         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20682     }
20683 });/*
20684  * Based on:
20685  * Ext JS Library 1.1.1
20686  * Copyright(c) 2006-2007, Ext JS, LLC.
20687  *
20688  * Originally Released Under LGPL - original licence link has changed is not relivant.
20689  *
20690  * Fork - LGPL
20691  * <script type="text/javascript">
20692  */
20693
20694 /**
20695  * @class Roo.menu.Separator
20696  * @extends Roo.menu.BaseItem
20697  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20698  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20699  * @constructor
20700  * @param {Object} config Configuration options
20701  */
20702 Roo.menu.Separator = function(config){
20703     Roo.menu.Separator.superclass.constructor.call(this, config);
20704 };
20705
20706 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20707     /**
20708      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20709      */
20710     itemCls : "x-menu-sep",
20711     /**
20712      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20713      */
20714     hideOnClick : false,
20715
20716     // private
20717     onRender : function(li){
20718         var s = document.createElement("span");
20719         s.className = this.itemCls;
20720         s.innerHTML = "&#160;";
20721         this.el = s;
20722         li.addClass("x-menu-sep-li");
20723         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20724     }
20725 });/*
20726  * Based on:
20727  * Ext JS Library 1.1.1
20728  * Copyright(c) 2006-2007, Ext JS, LLC.
20729  *
20730  * Originally Released Under LGPL - original licence link has changed is not relivant.
20731  *
20732  * Fork - LGPL
20733  * <script type="text/javascript">
20734  */
20735 /**
20736  * @class Roo.menu.Item
20737  * @extends Roo.menu.BaseItem
20738  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20739  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20740  * activation and click handling.
20741  * @constructor
20742  * Creates a new Item
20743  * @param {Object} config Configuration options
20744  */
20745 Roo.menu.Item = function(config){
20746     Roo.menu.Item.superclass.constructor.call(this, config);
20747     if(this.menu){
20748         this.menu = Roo.menu.MenuMgr.get(this.menu);
20749     }
20750 };
20751 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20752     
20753     /**
20754      * @cfg {String} text
20755      * The text to show on the menu item.
20756      */
20757     text: '',
20758      /**
20759      * @cfg {String} HTML to render in menu
20760      * The text to show on the menu item (HTML version).
20761      */
20762     html: '',
20763     /**
20764      * @cfg {String} icon
20765      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20766      */
20767     icon: undefined,
20768     /**
20769      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20770      */
20771     itemCls : "x-menu-item",
20772     /**
20773      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20774      */
20775     canActivate : true,
20776     /**
20777      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20778      */
20779     showDelay: 200,
20780     // doc'd in BaseItem
20781     hideDelay: 200,
20782
20783     // private
20784     ctype: "Roo.menu.Item",
20785     
20786     // private
20787     onRender : function(container, position){
20788         var el = document.createElement("a");
20789         el.hideFocus = true;
20790         el.unselectable = "on";
20791         el.href = this.href || "#";
20792         if(this.hrefTarget){
20793             el.target = this.hrefTarget;
20794         }
20795         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20796         
20797         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20798         
20799         el.innerHTML = String.format(
20800                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20801                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20802         this.el = el;
20803         Roo.menu.Item.superclass.onRender.call(this, container, position);
20804     },
20805
20806     /**
20807      * Sets the text to display in this menu item
20808      * @param {String} text The text to display
20809      * @param {Boolean} isHTML true to indicate text is pure html.
20810      */
20811     setText : function(text, isHTML){
20812         if (isHTML) {
20813             this.html = text;
20814         } else {
20815             this.text = text;
20816             this.html = '';
20817         }
20818         if(this.rendered){
20819             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20820      
20821             this.el.update(String.format(
20822                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20823                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20824             this.parentMenu.autoWidth();
20825         }
20826     },
20827
20828     // private
20829     handleClick : function(e){
20830         if(!this.href){ // if no link defined, stop the event automatically
20831             e.stopEvent();
20832         }
20833         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20834     },
20835
20836     // private
20837     activate : function(autoExpand){
20838         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20839             this.focus();
20840             if(autoExpand){
20841                 this.expandMenu();
20842             }
20843         }
20844         return true;
20845     },
20846
20847     // private
20848     shouldDeactivate : function(e){
20849         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20850             if(this.menu && this.menu.isVisible()){
20851                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20852             }
20853             return true;
20854         }
20855         return false;
20856     },
20857
20858     // private
20859     deactivate : function(){
20860         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20861         this.hideMenu();
20862     },
20863
20864     // private
20865     expandMenu : function(autoActivate){
20866         if(!this.disabled && this.menu){
20867             clearTimeout(this.hideTimer);
20868             delete this.hideTimer;
20869             if(!this.menu.isVisible() && !this.showTimer){
20870                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20871             }else if (this.menu.isVisible() && autoActivate){
20872                 this.menu.tryActivate(0, 1);
20873             }
20874         }
20875     },
20876
20877     // private
20878     deferExpand : function(autoActivate){
20879         delete this.showTimer;
20880         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20881         if(autoActivate){
20882             this.menu.tryActivate(0, 1);
20883         }
20884     },
20885
20886     // private
20887     hideMenu : function(){
20888         clearTimeout(this.showTimer);
20889         delete this.showTimer;
20890         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20891             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20892         }
20893     },
20894
20895     // private
20896     deferHide : function(){
20897         delete this.hideTimer;
20898         this.menu.hide();
20899     }
20900 });/*
20901  * Based on:
20902  * Ext JS Library 1.1.1
20903  * Copyright(c) 2006-2007, Ext JS, LLC.
20904  *
20905  * Originally Released Under LGPL - original licence link has changed is not relivant.
20906  *
20907  * Fork - LGPL
20908  * <script type="text/javascript">
20909  */
20910  
20911 /**
20912  * @class Roo.menu.CheckItem
20913  * @extends Roo.menu.Item
20914  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20915  * @constructor
20916  * Creates a new CheckItem
20917  * @param {Object} config Configuration options
20918  */
20919 Roo.menu.CheckItem = function(config){
20920     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20921     this.addEvents({
20922         /**
20923          * @event beforecheckchange
20924          * Fires before the checked value is set, providing an opportunity to cancel if needed
20925          * @param {Roo.menu.CheckItem} this
20926          * @param {Boolean} checked The new checked value that will be set
20927          */
20928         "beforecheckchange" : true,
20929         /**
20930          * @event checkchange
20931          * Fires after the checked value has been set
20932          * @param {Roo.menu.CheckItem} this
20933          * @param {Boolean} checked The checked value that was set
20934          */
20935         "checkchange" : true
20936     });
20937     if(this.checkHandler){
20938         this.on('checkchange', this.checkHandler, this.scope);
20939     }
20940 };
20941 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20942     /**
20943      * @cfg {String} group
20944      * All check items with the same group name will automatically be grouped into a single-select
20945      * radio button group (defaults to '')
20946      */
20947     /**
20948      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20949      */
20950     itemCls : "x-menu-item x-menu-check-item",
20951     /**
20952      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20953      */
20954     groupClass : "x-menu-group-item",
20955
20956     /**
20957      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20958      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20959      * initialized with checked = true will be rendered as checked.
20960      */
20961     checked: false,
20962
20963     // private
20964     ctype: "Roo.menu.CheckItem",
20965
20966     // private
20967     onRender : function(c){
20968         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20969         if(this.group){
20970             this.el.addClass(this.groupClass);
20971         }
20972         Roo.menu.MenuMgr.registerCheckable(this);
20973         if(this.checked){
20974             this.checked = false;
20975             this.setChecked(true, true);
20976         }
20977     },
20978
20979     // private
20980     destroy : function(){
20981         if(this.rendered){
20982             Roo.menu.MenuMgr.unregisterCheckable(this);
20983         }
20984         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20985     },
20986
20987     /**
20988      * Set the checked state of this item
20989      * @param {Boolean} checked The new checked value
20990      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20991      */
20992     setChecked : function(state, suppressEvent){
20993         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20994             if(this.container){
20995                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20996             }
20997             this.checked = state;
20998             if(suppressEvent !== true){
20999                 this.fireEvent("checkchange", this, state);
21000             }
21001         }
21002     },
21003
21004     // private
21005     handleClick : function(e){
21006        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21007            this.setChecked(!this.checked);
21008        }
21009        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21010     }
21011 });/*
21012  * Based on:
21013  * Ext JS Library 1.1.1
21014  * Copyright(c) 2006-2007, Ext JS, LLC.
21015  *
21016  * Originally Released Under LGPL - original licence link has changed is not relivant.
21017  *
21018  * Fork - LGPL
21019  * <script type="text/javascript">
21020  */
21021  
21022 /**
21023  * @class Roo.menu.DateItem
21024  * @extends Roo.menu.Adapter
21025  * A menu item that wraps the {@link Roo.DatPicker} component.
21026  * @constructor
21027  * Creates a new DateItem
21028  * @param {Object} config Configuration options
21029  */
21030 Roo.menu.DateItem = function(config){
21031     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21032     /** The Roo.DatePicker object @type Roo.DatePicker */
21033     this.picker = this.component;
21034     this.addEvents({select: true});
21035     
21036     this.picker.on("render", function(picker){
21037         picker.getEl().swallowEvent("click");
21038         picker.container.addClass("x-menu-date-item");
21039     });
21040
21041     this.picker.on("select", this.onSelect, this);
21042 };
21043
21044 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21045     // private
21046     onSelect : function(picker, date){
21047         this.fireEvent("select", this, date, picker);
21048         Roo.menu.DateItem.superclass.handleClick.call(this);
21049     }
21050 });/*
21051  * Based on:
21052  * Ext JS Library 1.1.1
21053  * Copyright(c) 2006-2007, Ext JS, LLC.
21054  *
21055  * Originally Released Under LGPL - original licence link has changed is not relivant.
21056  *
21057  * Fork - LGPL
21058  * <script type="text/javascript">
21059  */
21060  
21061 /**
21062  * @class Roo.menu.ColorItem
21063  * @extends Roo.menu.Adapter
21064  * A menu item that wraps the {@link Roo.ColorPalette} component.
21065  * @constructor
21066  * Creates a new ColorItem
21067  * @param {Object} config Configuration options
21068  */
21069 Roo.menu.ColorItem = function(config){
21070     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21071     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21072     this.palette = this.component;
21073     this.relayEvents(this.palette, ["select"]);
21074     if(this.selectHandler){
21075         this.on('select', this.selectHandler, this.scope);
21076     }
21077 };
21078 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21079  * Based on:
21080  * Ext JS Library 1.1.1
21081  * Copyright(c) 2006-2007, Ext JS, LLC.
21082  *
21083  * Originally Released Under LGPL - original licence link has changed is not relivant.
21084  *
21085  * Fork - LGPL
21086  * <script type="text/javascript">
21087  */
21088  
21089
21090 /**
21091  * @class Roo.menu.DateMenu
21092  * @extends Roo.menu.Menu
21093  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21094  * @constructor
21095  * Creates a new DateMenu
21096  * @param {Object} config Configuration options
21097  */
21098 Roo.menu.DateMenu = function(config){
21099     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21100     this.plain = true;
21101     var di = new Roo.menu.DateItem(config);
21102     this.add(di);
21103     /**
21104      * The {@link Roo.DatePicker} instance for this DateMenu
21105      * @type DatePicker
21106      */
21107     this.picker = di.picker;
21108     /**
21109      * @event select
21110      * @param {DatePicker} picker
21111      * @param {Date} date
21112      */
21113     this.relayEvents(di, ["select"]);
21114     this.on('beforeshow', function(){
21115         if(this.picker){
21116             this.picker.hideMonthPicker(false);
21117         }
21118     }, this);
21119 };
21120 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21121     cls:'x-date-menu'
21122 });/*
21123  * Based on:
21124  * Ext JS Library 1.1.1
21125  * Copyright(c) 2006-2007, Ext JS, LLC.
21126  *
21127  * Originally Released Under LGPL - original licence link has changed is not relivant.
21128  *
21129  * Fork - LGPL
21130  * <script type="text/javascript">
21131  */
21132  
21133
21134 /**
21135  * @class Roo.menu.ColorMenu
21136  * @extends Roo.menu.Menu
21137  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21138  * @constructor
21139  * Creates a new ColorMenu
21140  * @param {Object} config Configuration options
21141  */
21142 Roo.menu.ColorMenu = function(config){
21143     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21144     this.plain = true;
21145     var ci = new Roo.menu.ColorItem(config);
21146     this.add(ci);
21147     /**
21148      * The {@link Roo.ColorPalette} instance for this ColorMenu
21149      * @type ColorPalette
21150      */
21151     this.palette = ci.palette;
21152     /**
21153      * @event select
21154      * @param {ColorPalette} palette
21155      * @param {String} color
21156      */
21157     this.relayEvents(ci, ["select"]);
21158 };
21159 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21160  * Based on:
21161  * Ext JS Library 1.1.1
21162  * Copyright(c) 2006-2007, Ext JS, LLC.
21163  *
21164  * Originally Released Under LGPL - original licence link has changed is not relivant.
21165  *
21166  * Fork - LGPL
21167  * <script type="text/javascript">
21168  */
21169  
21170 /**
21171  * @class Roo.form.Field
21172  * @extends Roo.BoxComponent
21173  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21174  * @constructor
21175  * Creates a new Field
21176  * @param {Object} config Configuration options
21177  */
21178 Roo.form.Field = function(config){
21179     Roo.form.Field.superclass.constructor.call(this, config);
21180 };
21181
21182 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21183     /**
21184      * @cfg {String} fieldLabel Label to use when rendering a form.
21185      */
21186        /**
21187      * @cfg {String} qtip Mouse over tip
21188      */
21189      
21190     /**
21191      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21192      */
21193     invalidClass : "x-form-invalid",
21194     /**
21195      * @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")
21196      */
21197     invalidText : "The value in this field is invalid",
21198     /**
21199      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21200      */
21201     focusClass : "x-form-focus",
21202     /**
21203      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21204       automatic validation (defaults to "keyup").
21205      */
21206     validationEvent : "keyup",
21207     /**
21208      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21209      */
21210     validateOnBlur : true,
21211     /**
21212      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21213      */
21214     validationDelay : 250,
21215     /**
21216      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21217      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21218      */
21219     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21220     /**
21221      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21222      */
21223     fieldClass : "x-form-field",
21224     /**
21225      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21226      *<pre>
21227 Value         Description
21228 -----------   ----------------------------------------------------------------------
21229 qtip          Display a quick tip when the user hovers over the field
21230 title         Display a default browser title attribute popup
21231 under         Add a block div beneath the field containing the error text
21232 side          Add an error icon to the right of the field with a popup on hover
21233 [element id]  Add the error text directly to the innerHTML of the specified element
21234 </pre>
21235      */
21236     msgTarget : 'qtip',
21237     /**
21238      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21239      */
21240     msgFx : 'normal',
21241
21242     /**
21243      * @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.
21244      */
21245     readOnly : false,
21246
21247     /**
21248      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21249      */
21250     disabled : false,
21251
21252     /**
21253      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21254      */
21255     inputType : undefined,
21256     
21257     /**
21258      * @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).
21259          */
21260         tabIndex : undefined,
21261         
21262     // private
21263     isFormField : true,
21264
21265     // private
21266     hasFocus : false,
21267     /**
21268      * @property {Roo.Element} fieldEl
21269      * Element Containing the rendered Field (with label etc.)
21270      */
21271     /**
21272      * @cfg {Mixed} value A value to initialize this field with.
21273      */
21274     value : undefined,
21275
21276     /**
21277      * @cfg {String} name The field's HTML name attribute.
21278      */
21279     /**
21280      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21281      */
21282
21283         // private ??
21284         initComponent : function(){
21285         Roo.form.Field.superclass.initComponent.call(this);
21286         this.addEvents({
21287             /**
21288              * @event focus
21289              * Fires when this field receives input focus.
21290              * @param {Roo.form.Field} this
21291              */
21292             focus : true,
21293             /**
21294              * @event blur
21295              * Fires when this field loses input focus.
21296              * @param {Roo.form.Field} this
21297              */
21298             blur : true,
21299             /**
21300              * @event specialkey
21301              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21302              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21303              * @param {Roo.form.Field} this
21304              * @param {Roo.EventObject} e The event object
21305              */
21306             specialkey : true,
21307             /**
21308              * @event change
21309              * Fires just before the field blurs if the field value has changed.
21310              * @param {Roo.form.Field} this
21311              * @param {Mixed} newValue The new value
21312              * @param {Mixed} oldValue The original value
21313              */
21314             change : true,
21315             /**
21316              * @event invalid
21317              * Fires after the field has been marked as invalid.
21318              * @param {Roo.form.Field} this
21319              * @param {String} msg The validation message
21320              */
21321             invalid : true,
21322             /**
21323              * @event valid
21324              * Fires after the field has been validated with no errors.
21325              * @param {Roo.form.Field} this
21326              */
21327             valid : true,
21328              /**
21329              * @event keyup
21330              * Fires after the key up
21331              * @param {Roo.form.Field} this
21332              * @param {Roo.EventObject}  e The event Object
21333              */
21334             keyup : true
21335         });
21336     },
21337
21338     /**
21339      * Returns the name attribute of the field if available
21340      * @return {String} name The field name
21341      */
21342     getName: function(){
21343          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21344     },
21345
21346     // private
21347     onRender : function(ct, position){
21348         Roo.form.Field.superclass.onRender.call(this, ct, position);
21349         if(!this.el){
21350             var cfg = this.getAutoCreate();
21351             if(!cfg.name){
21352                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21353             }
21354             if (!cfg.name.length) {
21355                 delete cfg.name;
21356             }
21357             if(this.inputType){
21358                 cfg.type = this.inputType;
21359             }
21360             this.el = ct.createChild(cfg, position);
21361         }
21362         var type = this.el.dom.type;
21363         if(type){
21364             if(type == 'password'){
21365                 type = 'text';
21366             }
21367             this.el.addClass('x-form-'+type);
21368         }
21369         if(this.readOnly){
21370             this.el.dom.readOnly = true;
21371         }
21372         if(this.tabIndex !== undefined){
21373             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21374         }
21375
21376         this.el.addClass([this.fieldClass, this.cls]);
21377         this.initValue();
21378     },
21379
21380     /**
21381      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21382      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21383      * @return {Roo.form.Field} this
21384      */
21385     applyTo : function(target){
21386         this.allowDomMove = false;
21387         this.el = Roo.get(target);
21388         this.render(this.el.dom.parentNode);
21389         return this;
21390     },
21391
21392     // private
21393     initValue : function(){
21394         if(this.value !== undefined){
21395             this.setValue(this.value);
21396         }else if(this.el.dom.value.length > 0){
21397             this.setValue(this.el.dom.value);
21398         }
21399     },
21400
21401     /**
21402      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21403      */
21404     isDirty : function() {
21405         if(this.disabled) {
21406             return false;
21407         }
21408         return String(this.getValue()) !== String(this.originalValue);
21409     },
21410
21411     // private
21412     afterRender : function(){
21413         Roo.form.Field.superclass.afterRender.call(this);
21414         this.initEvents();
21415     },
21416
21417     // private
21418     fireKey : function(e){
21419         //Roo.log('field ' + e.getKey());
21420         if(e.isNavKeyPress()){
21421             this.fireEvent("specialkey", this, e);
21422         }
21423     },
21424
21425     /**
21426      * Resets the current field value to the originally loaded value and clears any validation messages
21427      */
21428     reset : function(){
21429         this.setValue(this.originalValue);
21430         this.clearInvalid();
21431     },
21432
21433     // private
21434     initEvents : function(){
21435         // safari killled keypress - so keydown is now used..
21436         this.el.on("keydown" , this.fireKey,  this);
21437         this.el.on("focus", this.onFocus,  this);
21438         this.el.on("blur", this.onBlur,  this);
21439         this.el.relayEvent('keyup', this);
21440
21441         // reference to original value for reset
21442         this.originalValue = this.getValue();
21443     },
21444
21445     // private
21446     onFocus : function(){
21447         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21448             this.el.addClass(this.focusClass);
21449         }
21450         if(!this.hasFocus){
21451             this.hasFocus = true;
21452             this.startValue = this.getValue();
21453             this.fireEvent("focus", this);
21454         }
21455     },
21456
21457     beforeBlur : Roo.emptyFn,
21458
21459     // private
21460     onBlur : function(){
21461         this.beforeBlur();
21462         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21463             this.el.removeClass(this.focusClass);
21464         }
21465         this.hasFocus = false;
21466         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21467             this.validate();
21468         }
21469         var v = this.getValue();
21470         if(String(v) !== String(this.startValue)){
21471             this.fireEvent('change', this, v, this.startValue);
21472         }
21473         this.fireEvent("blur", this);
21474     },
21475
21476     /**
21477      * Returns whether or not the field value is currently valid
21478      * @param {Boolean} preventMark True to disable marking the field invalid
21479      * @return {Boolean} True if the value is valid, else false
21480      */
21481     isValid : function(preventMark){
21482         if(this.disabled){
21483             return true;
21484         }
21485         var restore = this.preventMark;
21486         this.preventMark = preventMark === true;
21487         var v = this.validateValue(this.processValue(this.getRawValue()));
21488         this.preventMark = restore;
21489         return v;
21490     },
21491
21492     /**
21493      * Validates the field value
21494      * @return {Boolean} True if the value is valid, else false
21495      */
21496     validate : function(){
21497         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21498             this.clearInvalid();
21499             return true;
21500         }
21501         return false;
21502     },
21503
21504     processValue : function(value){
21505         return value;
21506     },
21507
21508     // private
21509     // Subclasses should provide the validation implementation by overriding this
21510     validateValue : function(value){
21511         return true;
21512     },
21513
21514     /**
21515      * Mark this field as invalid
21516      * @param {String} msg The validation message
21517      */
21518     markInvalid : function(msg){
21519         if(!this.rendered || this.preventMark){ // not rendered
21520             return;
21521         }
21522         this.el.addClass(this.invalidClass);
21523         msg = msg || this.invalidText;
21524         switch(this.msgTarget){
21525             case 'qtip':
21526                 this.el.dom.qtip = msg;
21527                 this.el.dom.qclass = 'x-form-invalid-tip';
21528                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21529                     Roo.QuickTips.enable();
21530                 }
21531                 break;
21532             case 'title':
21533                 this.el.dom.title = msg;
21534                 break;
21535             case 'under':
21536                 if(!this.errorEl){
21537                     var elp = this.el.findParent('.x-form-element', 5, true);
21538                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21539                     this.errorEl.setWidth(elp.getWidth(true)-20);
21540                 }
21541                 this.errorEl.update(msg);
21542                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21543                 break;
21544             case 'side':
21545                 if(!this.errorIcon){
21546                     var elp = this.el.findParent('.x-form-element', 5, true);
21547                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21548                 }
21549                 this.alignErrorIcon();
21550                 this.errorIcon.dom.qtip = msg;
21551                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21552                 this.errorIcon.show();
21553                 this.on('resize', this.alignErrorIcon, this);
21554                 break;
21555             default:
21556                 var t = Roo.getDom(this.msgTarget);
21557                 t.innerHTML = msg;
21558                 t.style.display = this.msgDisplay;
21559                 break;
21560         }
21561         this.fireEvent('invalid', this, msg);
21562     },
21563
21564     // private
21565     alignErrorIcon : function(){
21566         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21567     },
21568
21569     /**
21570      * Clear any invalid styles/messages for this field
21571      */
21572     clearInvalid : function(){
21573         if(!this.rendered || this.preventMark){ // not rendered
21574             return;
21575         }
21576         this.el.removeClass(this.invalidClass);
21577         switch(this.msgTarget){
21578             case 'qtip':
21579                 this.el.dom.qtip = '';
21580                 break;
21581             case 'title':
21582                 this.el.dom.title = '';
21583                 break;
21584             case 'under':
21585                 if(this.errorEl){
21586                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21587                 }
21588                 break;
21589             case 'side':
21590                 if(this.errorIcon){
21591                     this.errorIcon.dom.qtip = '';
21592                     this.errorIcon.hide();
21593                     this.un('resize', this.alignErrorIcon, this);
21594                 }
21595                 break;
21596             default:
21597                 var t = Roo.getDom(this.msgTarget);
21598                 t.innerHTML = '';
21599                 t.style.display = 'none';
21600                 break;
21601         }
21602         this.fireEvent('valid', this);
21603     },
21604
21605     /**
21606      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21607      * @return {Mixed} value The field value
21608      */
21609     getRawValue : function(){
21610         var v = this.el.getValue();
21611         if(v === this.emptyText){
21612             v = '';
21613         }
21614         return v;
21615     },
21616
21617     /**
21618      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21619      * @return {Mixed} value The field value
21620      */
21621     getValue : function(){
21622         var v = this.el.getValue();
21623         if(v === this.emptyText || v === undefined){
21624             v = '';
21625         }
21626         return v;
21627     },
21628
21629     /**
21630      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21631      * @param {Mixed} value The value to set
21632      */
21633     setRawValue : function(v){
21634         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21635     },
21636
21637     /**
21638      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21639      * @param {Mixed} value The value to set
21640      */
21641     setValue : function(v){
21642         this.value = v;
21643         if(this.rendered){
21644             this.el.dom.value = (v === null || v === undefined ? '' : v);
21645              this.validate();
21646         }
21647     },
21648
21649     adjustSize : function(w, h){
21650         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21651         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21652         return s;
21653     },
21654
21655     adjustWidth : function(tag, w){
21656         tag = tag.toLowerCase();
21657         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21658             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21659                 if(tag == 'input'){
21660                     return w + 2;
21661                 }
21662                 if(tag = 'textarea'){
21663                     return w-2;
21664                 }
21665             }else if(Roo.isOpera){
21666                 if(tag == 'input'){
21667                     return w + 2;
21668                 }
21669                 if(tag = 'textarea'){
21670                     return w-2;
21671                 }
21672             }
21673         }
21674         return w;
21675     }
21676 });
21677
21678
21679 // anything other than normal should be considered experimental
21680 Roo.form.Field.msgFx = {
21681     normal : {
21682         show: function(msgEl, f){
21683             msgEl.setDisplayed('block');
21684         },
21685
21686         hide : function(msgEl, f){
21687             msgEl.setDisplayed(false).update('');
21688         }
21689     },
21690
21691     slide : {
21692         show: function(msgEl, f){
21693             msgEl.slideIn('t', {stopFx:true});
21694         },
21695
21696         hide : function(msgEl, f){
21697             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21698         }
21699     },
21700
21701     slideRight : {
21702         show: function(msgEl, f){
21703             msgEl.fixDisplay();
21704             msgEl.alignTo(f.el, 'tl-tr');
21705             msgEl.slideIn('l', {stopFx:true});
21706         },
21707
21708         hide : function(msgEl, f){
21709             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21710         }
21711     }
21712 };/*
21713  * Based on:
21714  * Ext JS Library 1.1.1
21715  * Copyright(c) 2006-2007, Ext JS, LLC.
21716  *
21717  * Originally Released Under LGPL - original licence link has changed is not relivant.
21718  *
21719  * Fork - LGPL
21720  * <script type="text/javascript">
21721  */
21722  
21723
21724 /**
21725  * @class Roo.form.TextField
21726  * @extends Roo.form.Field
21727  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21728  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21729  * @constructor
21730  * Creates a new TextField
21731  * @param {Object} config Configuration options
21732  */
21733 Roo.form.TextField = function(config){
21734     Roo.form.TextField.superclass.constructor.call(this, config);
21735     this.addEvents({
21736         /**
21737          * @event autosize
21738          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21739          * according to the default logic, but this event provides a hook for the developer to apply additional
21740          * logic at runtime to resize the field if needed.
21741              * @param {Roo.form.Field} this This text field
21742              * @param {Number} width The new field width
21743              */
21744         autosize : true
21745     });
21746 };
21747
21748 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21749     /**
21750      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21751      */
21752     grow : false,
21753     /**
21754      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21755      */
21756     growMin : 30,
21757     /**
21758      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21759      */
21760     growMax : 800,
21761     /**
21762      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21763      */
21764     vtype : null,
21765     /**
21766      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21767      */
21768     maskRe : null,
21769     /**
21770      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21771      */
21772     disableKeyFilter : false,
21773     /**
21774      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21775      */
21776     allowBlank : true,
21777     /**
21778      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21779      */
21780     minLength : 0,
21781     /**
21782      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21783      */
21784     maxLength : Number.MAX_VALUE,
21785     /**
21786      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21787      */
21788     minLengthText : "The minimum length for this field is {0}",
21789     /**
21790      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21791      */
21792     maxLengthText : "The maximum length for this field is {0}",
21793     /**
21794      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21795      */
21796     selectOnFocus : false,
21797     /**
21798      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21799      */
21800     blankText : "This field is required",
21801     /**
21802      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21803      * If available, this function will be called only after the basic validators all return true, and will be passed the
21804      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21805      */
21806     validator : null,
21807     /**
21808      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21809      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21810      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21811      */
21812     regex : null,
21813     /**
21814      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21815      */
21816     regexText : "",
21817     /**
21818      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21819      */
21820     emptyText : null,
21821     /**
21822      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21823      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21824      */
21825     emptyClass : 'x-form-empty-field',
21826
21827     // private
21828     initEvents : function(){
21829         Roo.form.TextField.superclass.initEvents.call(this);
21830         if(this.validationEvent == 'keyup'){
21831             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21832             this.el.on('keyup', this.filterValidation, this);
21833         }
21834         else if(this.validationEvent !== false){
21835             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21836         }
21837         if(this.selectOnFocus || this.emptyText){
21838             this.on("focus", this.preFocus, this);
21839             if(this.emptyText){
21840                 this.on('blur', this.postBlur, this);
21841                 this.applyEmptyText();
21842             }
21843         }
21844         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21845             this.el.on("keypress", this.filterKeys, this);
21846         }
21847         if(this.grow){
21848             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21849             this.el.on("click", this.autoSize,  this);
21850         }
21851         if(this.el.is('input[type=password]') && Roo.isSafari){
21852             this.el.on('keydown', this.SafariOnKeyDown, this);
21853         }
21854     },
21855
21856     processValue : function(value){
21857         if(this.stripCharsRe){
21858             var newValue = value.replace(this.stripCharsRe, '');
21859             if(newValue !== value){
21860                 this.setRawValue(newValue);
21861                 return newValue;
21862             }
21863         }
21864         return value;
21865     },
21866
21867     filterValidation : function(e){
21868         if(!e.isNavKeyPress()){
21869             this.validationTask.delay(this.validationDelay);
21870         }
21871     },
21872
21873     // private
21874     onKeyUp : function(e){
21875         if(!e.isNavKeyPress()){
21876             this.autoSize();
21877         }
21878     },
21879
21880     /**
21881      * Resets the current field value to the originally-loaded value and clears any validation messages.
21882      * Also adds emptyText and emptyClass if the original value was blank.
21883      */
21884     reset : function(){
21885         Roo.form.TextField.superclass.reset.call(this);
21886         this.applyEmptyText();
21887     },
21888
21889     applyEmptyText : function(){
21890         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21891             this.setRawValue(this.emptyText);
21892             this.el.addClass(this.emptyClass);
21893         }
21894     },
21895
21896     // private
21897     preFocus : function(){
21898         if(this.emptyText){
21899             if(this.el.dom.value == this.emptyText){
21900                 this.setRawValue('');
21901             }
21902             this.el.removeClass(this.emptyClass);
21903         }
21904         if(this.selectOnFocus){
21905             this.el.dom.select();
21906         }
21907     },
21908
21909     // private
21910     postBlur : function(){
21911         this.applyEmptyText();
21912     },
21913
21914     // private
21915     filterKeys : function(e){
21916         var k = e.getKey();
21917         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21918             return;
21919         }
21920         var c = e.getCharCode(), cc = String.fromCharCode(c);
21921         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21922             return;
21923         }
21924         if(!this.maskRe.test(cc)){
21925             e.stopEvent();
21926         }
21927     },
21928
21929     setValue : function(v){
21930         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21931             this.el.removeClass(this.emptyClass);
21932         }
21933         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21934         this.applyEmptyText();
21935         this.autoSize();
21936     },
21937
21938     /**
21939      * Validates a value according to the field's validation rules and marks the field as invalid
21940      * if the validation fails
21941      * @param {Mixed} value The value to validate
21942      * @return {Boolean} True if the value is valid, else false
21943      */
21944     validateValue : function(value){
21945         if(value.length < 1 || value === this.emptyText){ // if it's blank
21946              if(this.allowBlank){
21947                 this.clearInvalid();
21948                 return true;
21949              }else{
21950                 this.markInvalid(this.blankText);
21951                 return false;
21952              }
21953         }
21954         if(value.length < this.minLength){
21955             this.markInvalid(String.format(this.minLengthText, this.minLength));
21956             return false;
21957         }
21958         if(value.length > this.maxLength){
21959             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21960             return false;
21961         }
21962         if(this.vtype){
21963             var vt = Roo.form.VTypes;
21964             if(!vt[this.vtype](value, this)){
21965                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21966                 return false;
21967             }
21968         }
21969         if(typeof this.validator == "function"){
21970             var msg = this.validator(value);
21971             if(msg !== true){
21972                 this.markInvalid(msg);
21973                 return false;
21974             }
21975         }
21976         if(this.regex && !this.regex.test(value)){
21977             this.markInvalid(this.regexText);
21978             return false;
21979         }
21980         return true;
21981     },
21982
21983     /**
21984      * Selects text in this field
21985      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21986      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21987      */
21988     selectText : function(start, end){
21989         var v = this.getRawValue();
21990         if(v.length > 0){
21991             start = start === undefined ? 0 : start;
21992             end = end === undefined ? v.length : end;
21993             var d = this.el.dom;
21994             if(d.setSelectionRange){
21995                 d.setSelectionRange(start, end);
21996             }else if(d.createTextRange){
21997                 var range = d.createTextRange();
21998                 range.moveStart("character", start);
21999                 range.moveEnd("character", v.length-end);
22000                 range.select();
22001             }
22002         }
22003     },
22004
22005     /**
22006      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22007      * This only takes effect if grow = true, and fires the autosize event.
22008      */
22009     autoSize : function(){
22010         if(!this.grow || !this.rendered){
22011             return;
22012         }
22013         if(!this.metrics){
22014             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22015         }
22016         var el = this.el;
22017         var v = el.dom.value;
22018         var d = document.createElement('div');
22019         d.appendChild(document.createTextNode(v));
22020         v = d.innerHTML;
22021         d = null;
22022         v += "&#160;";
22023         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22024         this.el.setWidth(w);
22025         this.fireEvent("autosize", this, w);
22026     },
22027     
22028     // private
22029     SafariOnKeyDown : function(event)
22030     {
22031         // this is a workaround for a password hang bug on chrome/ webkit.
22032         
22033         var isSelectAll = false;
22034         
22035         if(this.el.dom.selectionEnd > 0){
22036             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22037         }
22038         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22039             event.preventDefault();
22040             this.setValue('');
22041             return;
22042         }
22043         
22044         if(isSelectAll){ // backspace and delete key
22045             
22046             event.preventDefault();
22047             // this is very hacky as keydown always get's upper case.
22048             //
22049             var cc = String.fromCharCode(event.getCharCode());
22050             this.setValue( this.shiftKey ?  cc : cc.toLowerCase());
22051             
22052         }
22053         
22054         
22055     }
22056 });/*
22057  * Based on:
22058  * Ext JS Library 1.1.1
22059  * Copyright(c) 2006-2007, Ext JS, LLC.
22060  *
22061  * Originally Released Under LGPL - original licence link has changed is not relivant.
22062  *
22063  * Fork - LGPL
22064  * <script type="text/javascript">
22065  */
22066  
22067 /**
22068  * @class Roo.form.Hidden
22069  * @extends Roo.form.TextField
22070  * Simple Hidden element used on forms 
22071  * 
22072  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22073  * 
22074  * @constructor
22075  * Creates a new Hidden form element.
22076  * @param {Object} config Configuration options
22077  */
22078
22079
22080
22081 // easy hidden field...
22082 Roo.form.Hidden = function(config){
22083     Roo.form.Hidden.superclass.constructor.call(this, config);
22084 };
22085   
22086 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22087     fieldLabel:      '',
22088     inputType:      'hidden',
22089     width:          50,
22090     allowBlank:     true,
22091     labelSeparator: '',
22092     hidden:         true,
22093     itemCls :       'x-form-item-display-none'
22094
22095
22096 });
22097
22098
22099 /*
22100  * Based on:
22101  * Ext JS Library 1.1.1
22102  * Copyright(c) 2006-2007, Ext JS, LLC.
22103  *
22104  * Originally Released Under LGPL - original licence link has changed is not relivant.
22105  *
22106  * Fork - LGPL
22107  * <script type="text/javascript">
22108  */
22109  
22110 /**
22111  * @class Roo.form.TriggerField
22112  * @extends Roo.form.TextField
22113  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22114  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22115  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22116  * for which you can provide a custom implementation.  For example:
22117  * <pre><code>
22118 var trigger = new Roo.form.TriggerField();
22119 trigger.onTriggerClick = myTriggerFn;
22120 trigger.applyTo('my-field');
22121 </code></pre>
22122  *
22123  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22124  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22125  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22126  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22127  * @constructor
22128  * Create a new TriggerField.
22129  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22130  * to the base TextField)
22131  */
22132 Roo.form.TriggerField = function(config){
22133     this.mimicing = false;
22134     Roo.form.TriggerField.superclass.constructor.call(this, config);
22135 };
22136
22137 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22138     /**
22139      * @cfg {String} triggerClass A CSS class to apply to the trigger
22140      */
22141     /**
22142      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22143      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22144      */
22145     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22146     /**
22147      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22148      */
22149     hideTrigger:false,
22150
22151     /** @cfg {Boolean} grow @hide */
22152     /** @cfg {Number} growMin @hide */
22153     /** @cfg {Number} growMax @hide */
22154
22155     /**
22156      * @hide 
22157      * @method
22158      */
22159     autoSize: Roo.emptyFn,
22160     // private
22161     monitorTab : true,
22162     // private
22163     deferHeight : true,
22164
22165     
22166     actionMode : 'wrap',
22167     // private
22168     onResize : function(w, h){
22169         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22170         if(typeof w == 'number'){
22171             var x = w - this.trigger.getWidth();
22172             this.el.setWidth(this.adjustWidth('input', x));
22173             this.trigger.setStyle('left', x+'px');
22174         }
22175     },
22176
22177     // private
22178     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22179
22180     // private
22181     getResizeEl : function(){
22182         return this.wrap;
22183     },
22184
22185     // private
22186     getPositionEl : function(){
22187         return this.wrap;
22188     },
22189
22190     // private
22191     alignErrorIcon : function(){
22192         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22193     },
22194
22195     // private
22196     onRender : function(ct, position){
22197         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22198         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22199         this.trigger = this.wrap.createChild(this.triggerConfig ||
22200                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22201         if(this.hideTrigger){
22202             this.trigger.setDisplayed(false);
22203         }
22204         this.initTrigger();
22205         if(!this.width){
22206             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22207         }
22208     },
22209
22210     // private
22211     initTrigger : function(){
22212         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22213         this.trigger.addClassOnOver('x-form-trigger-over');
22214         this.trigger.addClassOnClick('x-form-trigger-click');
22215     },
22216
22217     // private
22218     onDestroy : function(){
22219         if(this.trigger){
22220             this.trigger.removeAllListeners();
22221             this.trigger.remove();
22222         }
22223         if(this.wrap){
22224             this.wrap.remove();
22225         }
22226         Roo.form.TriggerField.superclass.onDestroy.call(this);
22227     },
22228
22229     // private
22230     onFocus : function(){
22231         Roo.form.TriggerField.superclass.onFocus.call(this);
22232         if(!this.mimicing){
22233             this.wrap.addClass('x-trigger-wrap-focus');
22234             this.mimicing = true;
22235             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22236             if(this.monitorTab){
22237                 this.el.on("keydown", this.checkTab, this);
22238             }
22239         }
22240     },
22241
22242     // private
22243     checkTab : function(e){
22244         if(e.getKey() == e.TAB){
22245             this.triggerBlur();
22246         }
22247     },
22248
22249     // private
22250     onBlur : function(){
22251         // do nothing
22252     },
22253
22254     // private
22255     mimicBlur : function(e, t){
22256         if(!this.wrap.contains(t) && this.validateBlur()){
22257             this.triggerBlur();
22258         }
22259     },
22260
22261     // private
22262     triggerBlur : function(){
22263         this.mimicing = false;
22264         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22265         if(this.monitorTab){
22266             this.el.un("keydown", this.checkTab, this);
22267         }
22268         this.wrap.removeClass('x-trigger-wrap-focus');
22269         Roo.form.TriggerField.superclass.onBlur.call(this);
22270     },
22271
22272     // private
22273     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22274     validateBlur : function(e, t){
22275         return true;
22276     },
22277
22278     // private
22279     onDisable : function(){
22280         Roo.form.TriggerField.superclass.onDisable.call(this);
22281         if(this.wrap){
22282             this.wrap.addClass('x-item-disabled');
22283         }
22284     },
22285
22286     // private
22287     onEnable : function(){
22288         Roo.form.TriggerField.superclass.onEnable.call(this);
22289         if(this.wrap){
22290             this.wrap.removeClass('x-item-disabled');
22291         }
22292     },
22293
22294     // private
22295     onShow : function(){
22296         var ae = this.getActionEl();
22297         
22298         if(ae){
22299             ae.dom.style.display = '';
22300             ae.dom.style.visibility = 'visible';
22301         }
22302     },
22303
22304     // private
22305     
22306     onHide : function(){
22307         var ae = this.getActionEl();
22308         ae.dom.style.display = 'none';
22309     },
22310
22311     /**
22312      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22313      * by an implementing function.
22314      * @method
22315      * @param {EventObject} e
22316      */
22317     onTriggerClick : Roo.emptyFn
22318 });
22319
22320 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22321 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22322 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22323 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22324     initComponent : function(){
22325         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22326
22327         this.triggerConfig = {
22328             tag:'span', cls:'x-form-twin-triggers', cn:[
22329             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22330             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22331         ]};
22332     },
22333
22334     getTrigger : function(index){
22335         return this.triggers[index];
22336     },
22337
22338     initTrigger : function(){
22339         var ts = this.trigger.select('.x-form-trigger', true);
22340         this.wrap.setStyle('overflow', 'hidden');
22341         var triggerField = this;
22342         ts.each(function(t, all, index){
22343             t.hide = function(){
22344                 var w = triggerField.wrap.getWidth();
22345                 this.dom.style.display = 'none';
22346                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22347             };
22348             t.show = function(){
22349                 var w = triggerField.wrap.getWidth();
22350                 this.dom.style.display = '';
22351                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22352             };
22353             var triggerIndex = 'Trigger'+(index+1);
22354
22355             if(this['hide'+triggerIndex]){
22356                 t.dom.style.display = 'none';
22357             }
22358             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22359             t.addClassOnOver('x-form-trigger-over');
22360             t.addClassOnClick('x-form-trigger-click');
22361         }, this);
22362         this.triggers = ts.elements;
22363     },
22364
22365     onTrigger1Click : Roo.emptyFn,
22366     onTrigger2Click : Roo.emptyFn
22367 });/*
22368  * Based on:
22369  * Ext JS Library 1.1.1
22370  * Copyright(c) 2006-2007, Ext JS, LLC.
22371  *
22372  * Originally Released Under LGPL - original licence link has changed is not relivant.
22373  *
22374  * Fork - LGPL
22375  * <script type="text/javascript">
22376  */
22377  
22378 /**
22379  * @class Roo.form.TextArea
22380  * @extends Roo.form.TextField
22381  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22382  * support for auto-sizing.
22383  * @constructor
22384  * Creates a new TextArea
22385  * @param {Object} config Configuration options
22386  */
22387 Roo.form.TextArea = function(config){
22388     Roo.form.TextArea.superclass.constructor.call(this, config);
22389     // these are provided exchanges for backwards compat
22390     // minHeight/maxHeight were replaced by growMin/growMax to be
22391     // compatible with TextField growing config values
22392     if(this.minHeight !== undefined){
22393         this.growMin = this.minHeight;
22394     }
22395     if(this.maxHeight !== undefined){
22396         this.growMax = this.maxHeight;
22397     }
22398 };
22399
22400 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22401     /**
22402      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22403      */
22404     growMin : 60,
22405     /**
22406      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22407      */
22408     growMax: 1000,
22409     /**
22410      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22411      * in the field (equivalent to setting overflow: hidden, defaults to false)
22412      */
22413     preventScrollbars: false,
22414     /**
22415      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22416      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22417      */
22418
22419     // private
22420     onRender : function(ct, position){
22421         if(!this.el){
22422             this.defaultAutoCreate = {
22423                 tag: "textarea",
22424                 style:"width:300px;height:60px;",
22425                 autocomplete: "off"
22426             };
22427         }
22428         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22429         if(this.grow){
22430             this.textSizeEl = Roo.DomHelper.append(document.body, {
22431                 tag: "pre", cls: "x-form-grow-sizer"
22432             });
22433             if(this.preventScrollbars){
22434                 this.el.setStyle("overflow", "hidden");
22435             }
22436             this.el.setHeight(this.growMin);
22437         }
22438     },
22439
22440     onDestroy : function(){
22441         if(this.textSizeEl){
22442             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22443         }
22444         Roo.form.TextArea.superclass.onDestroy.call(this);
22445     },
22446
22447     // private
22448     onKeyUp : function(e){
22449         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22450             this.autoSize();
22451         }
22452     },
22453
22454     /**
22455      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22456      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22457      */
22458     autoSize : function(){
22459         if(!this.grow || !this.textSizeEl){
22460             return;
22461         }
22462         var el = this.el;
22463         var v = el.dom.value;
22464         var ts = this.textSizeEl;
22465
22466         ts.innerHTML = '';
22467         ts.appendChild(document.createTextNode(v));
22468         v = ts.innerHTML;
22469
22470         Roo.fly(ts).setWidth(this.el.getWidth());
22471         if(v.length < 1){
22472             v = "&#160;&#160;";
22473         }else{
22474             if(Roo.isIE){
22475                 v = v.replace(/\n/g, '<p>&#160;</p>');
22476             }
22477             v += "&#160;\n&#160;";
22478         }
22479         ts.innerHTML = v;
22480         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22481         if(h != this.lastHeight){
22482             this.lastHeight = h;
22483             this.el.setHeight(h);
22484             this.fireEvent("autosize", this, h);
22485         }
22486     }
22487 });/*
22488  * Based on:
22489  * Ext JS Library 1.1.1
22490  * Copyright(c) 2006-2007, Ext JS, LLC.
22491  *
22492  * Originally Released Under LGPL - original licence link has changed is not relivant.
22493  *
22494  * Fork - LGPL
22495  * <script type="text/javascript">
22496  */
22497  
22498
22499 /**
22500  * @class Roo.form.NumberField
22501  * @extends Roo.form.TextField
22502  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22503  * @constructor
22504  * Creates a new NumberField
22505  * @param {Object} config Configuration options
22506  */
22507 Roo.form.NumberField = function(config){
22508     Roo.form.NumberField.superclass.constructor.call(this, config);
22509 };
22510
22511 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22512     /**
22513      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22514      */
22515     fieldClass: "x-form-field x-form-num-field",
22516     /**
22517      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22518      */
22519     allowDecimals : true,
22520     /**
22521      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22522      */
22523     decimalSeparator : ".",
22524     /**
22525      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22526      */
22527     decimalPrecision : 2,
22528     /**
22529      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22530      */
22531     allowNegative : true,
22532     /**
22533      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22534      */
22535     minValue : Number.NEGATIVE_INFINITY,
22536     /**
22537      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22538      */
22539     maxValue : Number.MAX_VALUE,
22540     /**
22541      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22542      */
22543     minText : "The minimum value for this field is {0}",
22544     /**
22545      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22546      */
22547     maxText : "The maximum value for this field is {0}",
22548     /**
22549      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22550      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22551      */
22552     nanText : "{0} is not a valid number",
22553
22554     // private
22555     initEvents : function(){
22556         Roo.form.NumberField.superclass.initEvents.call(this);
22557         var allowed = "0123456789";
22558         if(this.allowDecimals){
22559             allowed += this.decimalSeparator;
22560         }
22561         if(this.allowNegative){
22562             allowed += "-";
22563         }
22564         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22565         var keyPress = function(e){
22566             var k = e.getKey();
22567             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22568                 return;
22569             }
22570             var c = e.getCharCode();
22571             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22572                 e.stopEvent();
22573             }
22574         };
22575         this.el.on("keypress", keyPress, this);
22576     },
22577
22578     // private
22579     validateValue : function(value){
22580         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22581             return false;
22582         }
22583         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22584              return true;
22585         }
22586         var num = this.parseValue(value);
22587         if(isNaN(num)){
22588             this.markInvalid(String.format(this.nanText, value));
22589             return false;
22590         }
22591         if(num < this.minValue){
22592             this.markInvalid(String.format(this.minText, this.minValue));
22593             return false;
22594         }
22595         if(num > this.maxValue){
22596             this.markInvalid(String.format(this.maxText, this.maxValue));
22597             return false;
22598         }
22599         return true;
22600     },
22601
22602     getValue : function(){
22603         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22604     },
22605
22606     // private
22607     parseValue : function(value){
22608         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22609         return isNaN(value) ? '' : value;
22610     },
22611
22612     // private
22613     fixPrecision : function(value){
22614         var nan = isNaN(value);
22615         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22616             return nan ? '' : value;
22617         }
22618         return parseFloat(value).toFixed(this.decimalPrecision);
22619     },
22620
22621     setValue : function(v){
22622         v = this.fixPrecision(v);
22623         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22624     },
22625
22626     // private
22627     decimalPrecisionFcn : function(v){
22628         return Math.floor(v);
22629     },
22630
22631     beforeBlur : function(){
22632         var v = this.parseValue(this.getRawValue());
22633         if(v){
22634             this.setValue(v);
22635         }
22636     }
22637 });/*
22638  * Based on:
22639  * Ext JS Library 1.1.1
22640  * Copyright(c) 2006-2007, Ext JS, LLC.
22641  *
22642  * Originally Released Under LGPL - original licence link has changed is not relivant.
22643  *
22644  * Fork - LGPL
22645  * <script type="text/javascript">
22646  */
22647  
22648 /**
22649  * @class Roo.form.DateField
22650  * @extends Roo.form.TriggerField
22651  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22652 * @constructor
22653 * Create a new DateField
22654 * @param {Object} config
22655  */
22656 Roo.form.DateField = function(config){
22657     Roo.form.DateField.superclass.constructor.call(this, config);
22658     
22659       this.addEvents({
22660          
22661         /**
22662          * @event select
22663          * Fires when a date is selected
22664              * @param {Roo.form.DateField} combo This combo box
22665              * @param {Date} date The date selected
22666              */
22667         'select' : true
22668          
22669     });
22670     
22671     
22672     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22673     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22674     this.ddMatch = null;
22675     if(this.disabledDates){
22676         var dd = this.disabledDates;
22677         var re = "(?:";
22678         for(var i = 0; i < dd.length; i++){
22679             re += dd[i];
22680             if(i != dd.length-1) re += "|";
22681         }
22682         this.ddMatch = new RegExp(re + ")");
22683     }
22684 };
22685
22686 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22687     /**
22688      * @cfg {String} format
22689      * The default date format string which can be overriden for localization support.  The format must be
22690      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22691      */
22692     format : "m/d/y",
22693     /**
22694      * @cfg {String} altFormats
22695      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22696      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22697      */
22698     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22699     /**
22700      * @cfg {Array} disabledDays
22701      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22702      */
22703     disabledDays : null,
22704     /**
22705      * @cfg {String} disabledDaysText
22706      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22707      */
22708     disabledDaysText : "Disabled",
22709     /**
22710      * @cfg {Array} disabledDates
22711      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22712      * expression so they are very powerful. Some examples:
22713      * <ul>
22714      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22715      * <li>["03/08", "09/16"] would disable those days for every year</li>
22716      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22717      * <li>["03/../2006"] would disable every day in March 2006</li>
22718      * <li>["^03"] would disable every day in every March</li>
22719      * </ul>
22720      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22721      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22722      */
22723     disabledDates : null,
22724     /**
22725      * @cfg {String} disabledDatesText
22726      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22727      */
22728     disabledDatesText : "Disabled",
22729     /**
22730      * @cfg {Date/String} minValue
22731      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22732      * valid format (defaults to null).
22733      */
22734     minValue : null,
22735     /**
22736      * @cfg {Date/String} maxValue
22737      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22738      * valid format (defaults to null).
22739      */
22740     maxValue : null,
22741     /**
22742      * @cfg {String} minText
22743      * The error text to display when the date in the cell is before minValue (defaults to
22744      * 'The date in this field must be after {minValue}').
22745      */
22746     minText : "The date in this field must be equal to or after {0}",
22747     /**
22748      * @cfg {String} maxText
22749      * The error text to display when the date in the cell is after maxValue (defaults to
22750      * 'The date in this field must be before {maxValue}').
22751      */
22752     maxText : "The date in this field must be equal to or before {0}",
22753     /**
22754      * @cfg {String} invalidText
22755      * The error text to display when the date in the field is invalid (defaults to
22756      * '{value} is not a valid date - it must be in the format {format}').
22757      */
22758     invalidText : "{0} is not a valid date - it must be in the format {1}",
22759     /**
22760      * @cfg {String} triggerClass
22761      * An additional CSS class used to style the trigger button.  The trigger will always get the
22762      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22763      * which displays a calendar icon).
22764      */
22765     triggerClass : 'x-form-date-trigger',
22766     
22767
22768     /**
22769      * @cfg {Boolean} useIso
22770      * if enabled, then the date field will use a hidden field to store the 
22771      * real value as iso formated date. default (false)
22772      */ 
22773     useIso : false,
22774     /**
22775      * @cfg {String/Object} autoCreate
22776      * A DomHelper element spec, or true for a default element spec (defaults to
22777      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22778      */ 
22779     // private
22780     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22781     
22782     // private
22783     hiddenField: false,
22784     
22785     onRender : function(ct, position)
22786     {
22787         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22788         if (this.useIso) {
22789             //this.el.dom.removeAttribute('name'); 
22790             Roo.log("Changing name?");
22791             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22792             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22793                     'before', true);
22794             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22795             // prevent input submission
22796             this.hiddenName = this.name;
22797         }
22798             
22799             
22800     },
22801     
22802     // private
22803     validateValue : function(value)
22804     {
22805         value = this.formatDate(value);
22806         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22807             Roo.log('super failed');
22808             return false;
22809         }
22810         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22811              return true;
22812         }
22813         var svalue = value;
22814         value = this.parseDate(value);
22815         if(!value){
22816             Roo.log('parse date failed' + svalue);
22817             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22818             return false;
22819         }
22820         var time = value.getTime();
22821         if(this.minValue && time < this.minValue.getTime()){
22822             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22823             return false;
22824         }
22825         if(this.maxValue && time > this.maxValue.getTime()){
22826             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22827             return false;
22828         }
22829         if(this.disabledDays){
22830             var day = value.getDay();
22831             for(var i = 0; i < this.disabledDays.length; i++) {
22832                 if(day === this.disabledDays[i]){
22833                     this.markInvalid(this.disabledDaysText);
22834                     return false;
22835                 }
22836             }
22837         }
22838         var fvalue = this.formatDate(value);
22839         if(this.ddMatch && this.ddMatch.test(fvalue)){
22840             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22841             return false;
22842         }
22843         return true;
22844     },
22845
22846     // private
22847     // Provides logic to override the default TriggerField.validateBlur which just returns true
22848     validateBlur : function(){
22849         return !this.menu || !this.menu.isVisible();
22850     },
22851     
22852     getName: function()
22853     {
22854         // returns hidden if it's set..
22855         if (!this.rendered) {return ''};
22856         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22857         
22858     },
22859
22860     /**
22861      * Returns the current date value of the date field.
22862      * @return {Date} The date value
22863      */
22864     getValue : function(){
22865         
22866         return  this.hiddenField ?
22867                 this.hiddenField.value :
22868                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22869     },
22870
22871     /**
22872      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22873      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22874      * (the default format used is "m/d/y").
22875      * <br />Usage:
22876      * <pre><code>
22877 //All of these calls set the same date value (May 4, 2006)
22878
22879 //Pass a date object:
22880 var dt = new Date('5/4/06');
22881 dateField.setValue(dt);
22882
22883 //Pass a date string (default format):
22884 dateField.setValue('5/4/06');
22885
22886 //Pass a date string (custom format):
22887 dateField.format = 'Y-m-d';
22888 dateField.setValue('2006-5-4');
22889 </code></pre>
22890      * @param {String/Date} date The date or valid date string
22891      */
22892     setValue : function(date){
22893         if (this.hiddenField) {
22894             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22895         }
22896         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22897         // make sure the value field is always stored as a date..
22898         this.value = this.parseDate(date);
22899         
22900         
22901     },
22902
22903     // private
22904     parseDate : function(value){
22905         if(!value || value instanceof Date){
22906             return value;
22907         }
22908         var v = Date.parseDate(value, this.format);
22909          if (!v && this.useIso) {
22910             v = Date.parseDate(value, 'Y-m-d');
22911         }
22912         if(!v && this.altFormats){
22913             if(!this.altFormatsArray){
22914                 this.altFormatsArray = this.altFormats.split("|");
22915             }
22916             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22917                 v = Date.parseDate(value, this.altFormatsArray[i]);
22918             }
22919         }
22920         return v;
22921     },
22922
22923     // private
22924     formatDate : function(date, fmt){
22925         return (!date || !(date instanceof Date)) ?
22926                date : date.dateFormat(fmt || this.format);
22927     },
22928
22929     // private
22930     menuListeners : {
22931         select: function(m, d){
22932             
22933             this.setValue(d);
22934             this.fireEvent('select', this, d);
22935         },
22936         show : function(){ // retain focus styling
22937             this.onFocus();
22938         },
22939         hide : function(){
22940             this.focus.defer(10, this);
22941             var ml = this.menuListeners;
22942             this.menu.un("select", ml.select,  this);
22943             this.menu.un("show", ml.show,  this);
22944             this.menu.un("hide", ml.hide,  this);
22945         }
22946     },
22947
22948     // private
22949     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22950     onTriggerClick : function(){
22951         if(this.disabled){
22952             return;
22953         }
22954         if(this.menu == null){
22955             this.menu = new Roo.menu.DateMenu();
22956         }
22957         Roo.apply(this.menu.picker,  {
22958             showClear: this.allowBlank,
22959             minDate : this.minValue,
22960             maxDate : this.maxValue,
22961             disabledDatesRE : this.ddMatch,
22962             disabledDatesText : this.disabledDatesText,
22963             disabledDays : this.disabledDays,
22964             disabledDaysText : this.disabledDaysText,
22965             format : this.useIso ? 'Y-m-d' : this.format,
22966             minText : String.format(this.minText, this.formatDate(this.minValue)),
22967             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22968         });
22969         this.menu.on(Roo.apply({}, this.menuListeners, {
22970             scope:this
22971         }));
22972         this.menu.picker.setValue(this.getValue() || new Date());
22973         this.menu.show(this.el, "tl-bl?");
22974     },
22975
22976     beforeBlur : function(){
22977         var v = this.parseDate(this.getRawValue());
22978         if(v){
22979             this.setValue(v);
22980         }
22981     }
22982
22983     /** @cfg {Boolean} grow @hide */
22984     /** @cfg {Number} growMin @hide */
22985     /** @cfg {Number} growMax @hide */
22986     /**
22987      * @hide
22988      * @method autoSize
22989      */
22990 });/*
22991  * Based on:
22992  * Ext JS Library 1.1.1
22993  * Copyright(c) 2006-2007, Ext JS, LLC.
22994  *
22995  * Originally Released Under LGPL - original licence link has changed is not relivant.
22996  *
22997  * Fork - LGPL
22998  * <script type="text/javascript">
22999  */
23000  
23001 /**
23002  * @class Roo.form.MonthField
23003  * @extends Roo.form.TriggerField
23004  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23005 * @constructor
23006 * Create a new MonthField
23007 * @param {Object} config
23008  */
23009 Roo.form.MonthField = function(config){
23010     
23011     Roo.form.MonthField.superclass.constructor.call(this, config);
23012     
23013       this.addEvents({
23014          
23015         /**
23016          * @event select
23017          * Fires when a date is selected
23018              * @param {Roo.form.MonthFieeld} combo This combo box
23019              * @param {Date} date The date selected
23020              */
23021         'select' : true
23022          
23023     });
23024     
23025     
23026     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23027     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23028     this.ddMatch = null;
23029     if(this.disabledDates){
23030         var dd = this.disabledDates;
23031         var re = "(?:";
23032         for(var i = 0; i < dd.length; i++){
23033             re += dd[i];
23034             if(i != dd.length-1) re += "|";
23035         }
23036         this.ddMatch = new RegExp(re + ")");
23037     }
23038 };
23039
23040 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23041     /**
23042      * @cfg {String} format
23043      * The default date format string which can be overriden for localization support.  The format must be
23044      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23045      */
23046     format : "M Y",
23047     /**
23048      * @cfg {String} altFormats
23049      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23050      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23051      */
23052     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23053     /**
23054      * @cfg {Array} disabledDays
23055      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23056      */
23057     disabledDays : [0,1,2,3,4,5,6],
23058     /**
23059      * @cfg {String} disabledDaysText
23060      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23061      */
23062     disabledDaysText : "Disabled",
23063     /**
23064      * @cfg {Array} disabledDates
23065      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23066      * expression so they are very powerful. Some examples:
23067      * <ul>
23068      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23069      * <li>["03/08", "09/16"] would disable those days for every year</li>
23070      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23071      * <li>["03/../2006"] would disable every day in March 2006</li>
23072      * <li>["^03"] would disable every day in every March</li>
23073      * </ul>
23074      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23075      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23076      */
23077     disabledDates : null,
23078     /**
23079      * @cfg {String} disabledDatesText
23080      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23081      */
23082     disabledDatesText : "Disabled",
23083     /**
23084      * @cfg {Date/String} minValue
23085      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23086      * valid format (defaults to null).
23087      */
23088     minValue : null,
23089     /**
23090      * @cfg {Date/String} maxValue
23091      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23092      * valid format (defaults to null).
23093      */
23094     maxValue : null,
23095     /**
23096      * @cfg {String} minText
23097      * The error text to display when the date in the cell is before minValue (defaults to
23098      * 'The date in this field must be after {minValue}').
23099      */
23100     minText : "The date in this field must be equal to or after {0}",
23101     /**
23102      * @cfg {String} maxTextf
23103      * The error text to display when the date in the cell is after maxValue (defaults to
23104      * 'The date in this field must be before {maxValue}').
23105      */
23106     maxText : "The date in this field must be equal to or before {0}",
23107     /**
23108      * @cfg {String} invalidText
23109      * The error text to display when the date in the field is invalid (defaults to
23110      * '{value} is not a valid date - it must be in the format {format}').
23111      */
23112     invalidText : "{0} is not a valid date - it must be in the format {1}",
23113     /**
23114      * @cfg {String} triggerClass
23115      * An additional CSS class used to style the trigger button.  The trigger will always get the
23116      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23117      * which displays a calendar icon).
23118      */
23119     triggerClass : 'x-form-date-trigger',
23120     
23121
23122     /**
23123      * @cfg {Boolean} useIso
23124      * if enabled, then the date field will use a hidden field to store the 
23125      * real value as iso formated date. default (true)
23126      */ 
23127     useIso : true,
23128     /**
23129      * @cfg {String/Object} autoCreate
23130      * A DomHelper element spec, or true for a default element spec (defaults to
23131      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23132      */ 
23133     // private
23134     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23135     
23136     // private
23137     hiddenField: false,
23138     
23139     hideMonthPicker : false,
23140     
23141     onRender : function(ct, position)
23142     {
23143         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23144         if (this.useIso) {
23145             this.el.dom.removeAttribute('name'); 
23146             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23147                     'before', true);
23148             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23149             // prevent input submission
23150             this.hiddenName = this.name;
23151         }
23152             
23153             
23154     },
23155     
23156     // private
23157     validateValue : function(value)
23158     {
23159         value = this.formatDate(value);
23160         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23161             return false;
23162         }
23163         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23164              return true;
23165         }
23166         var svalue = value;
23167         value = this.parseDate(value);
23168         if(!value){
23169             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23170             return false;
23171         }
23172         var time = value.getTime();
23173         if(this.minValue && time < this.minValue.getTime()){
23174             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23175             return false;
23176         }
23177         if(this.maxValue && time > this.maxValue.getTime()){
23178             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23179             return false;
23180         }
23181         /*if(this.disabledDays){
23182             var day = value.getDay();
23183             for(var i = 0; i < this.disabledDays.length; i++) {
23184                 if(day === this.disabledDays[i]){
23185                     this.markInvalid(this.disabledDaysText);
23186                     return false;
23187                 }
23188             }
23189         }
23190         */
23191         var fvalue = this.formatDate(value);
23192         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23193             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23194             return false;
23195         }
23196         */
23197         return true;
23198     },
23199
23200     // private
23201     // Provides logic to override the default TriggerField.validateBlur which just returns true
23202     validateBlur : function(){
23203         return !this.menu || !this.menu.isVisible();
23204     },
23205
23206     /**
23207      * Returns the current date value of the date field.
23208      * @return {Date} The date value
23209      */
23210     getValue : function(){
23211         
23212         
23213         
23214         return  this.hiddenField ?
23215                 this.hiddenField.value :
23216                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23217     },
23218
23219     /**
23220      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23221      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23222      * (the default format used is "m/d/y").
23223      * <br />Usage:
23224      * <pre><code>
23225 //All of these calls set the same date value (May 4, 2006)
23226
23227 //Pass a date object:
23228 var dt = new Date('5/4/06');
23229 monthField.setValue(dt);
23230
23231 //Pass a date string (default format):
23232 monthField.setValue('5/4/06');
23233
23234 //Pass a date string (custom format):
23235 monthField.format = 'Y-m-d';
23236 monthField.setValue('2006-5-4');
23237 </code></pre>
23238      * @param {String/Date} date The date or valid date string
23239      */
23240     setValue : function(date){
23241         Roo.log('month setValue' + date);
23242         // can only be first of month..
23243         
23244         var val = this.parseDate(date);
23245         
23246         if (this.hiddenField) {
23247             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23248         }
23249         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23250         this.value = this.parseDate(date);
23251     },
23252
23253     // private
23254     parseDate : function(value){
23255         if(!value || value instanceof Date){
23256             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23257             return value;
23258         }
23259         var v = Date.parseDate(value, this.format);
23260         if (!v && this.useIso) {
23261             v = Date.parseDate(value, 'Y-m-d');
23262         }
23263         if (v) {
23264             // 
23265             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23266         }
23267         
23268         
23269         if(!v && this.altFormats){
23270             if(!this.altFormatsArray){
23271                 this.altFormatsArray = this.altFormats.split("|");
23272             }
23273             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23274                 v = Date.parseDate(value, this.altFormatsArray[i]);
23275             }
23276         }
23277         return v;
23278     },
23279
23280     // private
23281     formatDate : function(date, fmt){
23282         return (!date || !(date instanceof Date)) ?
23283                date : date.dateFormat(fmt || this.format);
23284     },
23285
23286     // private
23287     menuListeners : {
23288         select: function(m, d){
23289             this.setValue(d);
23290             this.fireEvent('select', this, d);
23291         },
23292         show : function(){ // retain focus styling
23293             this.onFocus();
23294         },
23295         hide : function(){
23296             this.focus.defer(10, this);
23297             var ml = this.menuListeners;
23298             this.menu.un("select", ml.select,  this);
23299             this.menu.un("show", ml.show,  this);
23300             this.menu.un("hide", ml.hide,  this);
23301         }
23302     },
23303     // private
23304     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23305     onTriggerClick : function(){
23306         if(this.disabled){
23307             return;
23308         }
23309         if(this.menu == null){
23310             this.menu = new Roo.menu.DateMenu();
23311            
23312         }
23313         
23314         Roo.apply(this.menu.picker,  {
23315             
23316             showClear: this.allowBlank,
23317             minDate : this.minValue,
23318             maxDate : this.maxValue,
23319             disabledDatesRE : this.ddMatch,
23320             disabledDatesText : this.disabledDatesText,
23321             
23322             format : this.useIso ? 'Y-m-d' : this.format,
23323             minText : String.format(this.minText, this.formatDate(this.minValue)),
23324             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23325             
23326         });
23327          this.menu.on(Roo.apply({}, this.menuListeners, {
23328             scope:this
23329         }));
23330        
23331         
23332         var m = this.menu;
23333         var p = m.picker;
23334         
23335         // hide month picker get's called when we called by 'before hide';
23336         
23337         var ignorehide = true;
23338         p.hideMonthPicker  = function(disableAnim){
23339             if (ignorehide) {
23340                 return;
23341             }
23342              if(this.monthPicker){
23343                 Roo.log("hideMonthPicker called");
23344                 if(disableAnim === true){
23345                     this.monthPicker.hide();
23346                 }else{
23347                     this.monthPicker.slideOut('t', {duration:.2});
23348                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23349                     p.fireEvent("select", this, this.value);
23350                     m.hide();
23351                 }
23352             }
23353         }
23354         
23355         Roo.log('picker set value');
23356         Roo.log(this.getValue());
23357         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23358         m.show(this.el, 'tl-bl?');
23359         ignorehide  = false;
23360         // this will trigger hideMonthPicker..
23361         
23362         
23363         // hidden the day picker
23364         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23365         
23366         
23367         
23368       
23369         
23370         p.showMonthPicker.defer(100, p);
23371     
23372         
23373        
23374     },
23375
23376     beforeBlur : function(){
23377         var v = this.parseDate(this.getRawValue());
23378         if(v){
23379             this.setValue(v);
23380         }
23381     }
23382
23383     /** @cfg {Boolean} grow @hide */
23384     /** @cfg {Number} growMin @hide */
23385     /** @cfg {Number} growMax @hide */
23386     /**
23387      * @hide
23388      * @method autoSize
23389      */
23390 });/*
23391  * Based on:
23392  * Ext JS Library 1.1.1
23393  * Copyright(c) 2006-2007, Ext JS, LLC.
23394  *
23395  * Originally Released Under LGPL - original licence link has changed is not relivant.
23396  *
23397  * Fork - LGPL
23398  * <script type="text/javascript">
23399  */
23400  
23401
23402 /**
23403  * @class Roo.form.ComboBox
23404  * @extends Roo.form.TriggerField
23405  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23406  * @constructor
23407  * Create a new ComboBox.
23408  * @param {Object} config Configuration options
23409  */
23410 Roo.form.ComboBox = function(config){
23411     Roo.form.ComboBox.superclass.constructor.call(this, config);
23412     this.addEvents({
23413         /**
23414          * @event expand
23415          * Fires when the dropdown list is expanded
23416              * @param {Roo.form.ComboBox} combo This combo box
23417              */
23418         'expand' : true,
23419         /**
23420          * @event collapse
23421          * Fires when the dropdown list is collapsed
23422              * @param {Roo.form.ComboBox} combo This combo box
23423              */
23424         'collapse' : true,
23425         /**
23426          * @event beforeselect
23427          * Fires before a list item is selected. Return false to cancel the selection.
23428              * @param {Roo.form.ComboBox} combo This combo box
23429              * @param {Roo.data.Record} record The data record returned from the underlying store
23430              * @param {Number} index The index of the selected item in the dropdown list
23431              */
23432         'beforeselect' : true,
23433         /**
23434          * @event select
23435          * Fires when a list item is selected
23436              * @param {Roo.form.ComboBox} combo This combo box
23437              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23438              * @param {Number} index The index of the selected item in the dropdown list
23439              */
23440         'select' : true,
23441         /**
23442          * @event beforequery
23443          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23444          * The event object passed has these properties:
23445              * @param {Roo.form.ComboBox} combo This combo box
23446              * @param {String} query The query
23447              * @param {Boolean} forceAll true to force "all" query
23448              * @param {Boolean} cancel true to cancel the query
23449              * @param {Object} e The query event object
23450              */
23451         'beforequery': true,
23452          /**
23453          * @event add
23454          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23455              * @param {Roo.form.ComboBox} combo This combo box
23456              */
23457         'add' : true,
23458         /**
23459          * @event edit
23460          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23461              * @param {Roo.form.ComboBox} combo This combo box
23462              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23463              */
23464         'edit' : true
23465         
23466         
23467     });
23468     if(this.transform){
23469         this.allowDomMove = false;
23470         var s = Roo.getDom(this.transform);
23471         if(!this.hiddenName){
23472             this.hiddenName = s.name;
23473         }
23474         if(!this.store){
23475             this.mode = 'local';
23476             var d = [], opts = s.options;
23477             for(var i = 0, len = opts.length;i < len; i++){
23478                 var o = opts[i];
23479                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23480                 if(o.selected) {
23481                     this.value = value;
23482                 }
23483                 d.push([value, o.text]);
23484             }
23485             this.store = new Roo.data.SimpleStore({
23486                 'id': 0,
23487                 fields: ['value', 'text'],
23488                 data : d
23489             });
23490             this.valueField = 'value';
23491             this.displayField = 'text';
23492         }
23493         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23494         if(!this.lazyRender){
23495             this.target = true;
23496             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23497             s.parentNode.removeChild(s); // remove it
23498             this.render(this.el.parentNode);
23499         }else{
23500             s.parentNode.removeChild(s); // remove it
23501         }
23502
23503     }
23504     if (this.store) {
23505         this.store = Roo.factory(this.store, Roo.data);
23506     }
23507     
23508     this.selectedIndex = -1;
23509     if(this.mode == 'local'){
23510         if(config.queryDelay === undefined){
23511             this.queryDelay = 10;
23512         }
23513         if(config.minChars === undefined){
23514             this.minChars = 0;
23515         }
23516     }
23517 };
23518
23519 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23520     /**
23521      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23522      */
23523     /**
23524      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23525      * rendering into an Roo.Editor, defaults to false)
23526      */
23527     /**
23528      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23529      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23530      */
23531     /**
23532      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23533      */
23534     /**
23535      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23536      * the dropdown list (defaults to undefined, with no header element)
23537      */
23538
23539      /**
23540      * @cfg {String/Roo.Template} tpl The template to use to render the output
23541      */
23542      
23543     // private
23544     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23545     /**
23546      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23547      */
23548     listWidth: undefined,
23549     /**
23550      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23551      * mode = 'remote' or 'text' if mode = 'local')
23552      */
23553     displayField: undefined,
23554     /**
23555      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23556      * mode = 'remote' or 'value' if mode = 'local'). 
23557      * Note: use of a valueField requires the user make a selection
23558      * in order for a value to be mapped.
23559      */
23560     valueField: undefined,
23561     
23562     
23563     /**
23564      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23565      * field's data value (defaults to the underlying DOM element's name)
23566      */
23567     hiddenName: undefined,
23568     /**
23569      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23570      */
23571     listClass: '',
23572     /**
23573      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23574      */
23575     selectedClass: 'x-combo-selected',
23576     /**
23577      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23578      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23579      * which displays a downward arrow icon).
23580      */
23581     triggerClass : 'x-form-arrow-trigger',
23582     /**
23583      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23584      */
23585     shadow:'sides',
23586     /**
23587      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23588      * anchor positions (defaults to 'tl-bl')
23589      */
23590     listAlign: 'tl-bl?',
23591     /**
23592      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23593      */
23594     maxHeight: 300,
23595     /**
23596      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23597      * query specified by the allQuery config option (defaults to 'query')
23598      */
23599     triggerAction: 'query',
23600     /**
23601      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23602      * (defaults to 4, does not apply if editable = false)
23603      */
23604     minChars : 4,
23605     /**
23606      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23607      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23608      */
23609     typeAhead: false,
23610     /**
23611      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23612      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23613      */
23614     queryDelay: 500,
23615     /**
23616      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23617      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23618      */
23619     pageSize: 0,
23620     /**
23621      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23622      * when editable = true (defaults to false)
23623      */
23624     selectOnFocus:false,
23625     /**
23626      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23627      */
23628     queryParam: 'query',
23629     /**
23630      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23631      * when mode = 'remote' (defaults to 'Loading...')
23632      */
23633     loadingText: 'Loading...',
23634     /**
23635      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23636      */
23637     resizable: false,
23638     /**
23639      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23640      */
23641     handleHeight : 8,
23642     /**
23643      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23644      * traditional select (defaults to true)
23645      */
23646     editable: true,
23647     /**
23648      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23649      */
23650     allQuery: '',
23651     /**
23652      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23653      */
23654     mode: 'remote',
23655     /**
23656      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23657      * listWidth has a higher value)
23658      */
23659     minListWidth : 70,
23660     /**
23661      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23662      * allow the user to set arbitrary text into the field (defaults to false)
23663      */
23664     forceSelection:false,
23665     /**
23666      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23667      * if typeAhead = true (defaults to 250)
23668      */
23669     typeAheadDelay : 250,
23670     /**
23671      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23672      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23673      */
23674     valueNotFoundText : undefined,
23675     /**
23676      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23677      */
23678     blockFocus : false,
23679     
23680     /**
23681      * @cfg {Boolean} disableClear Disable showing of clear button.
23682      */
23683     disableClear : false,
23684     /**
23685      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23686      */
23687     alwaysQuery : false,
23688     
23689     //private
23690     addicon : false,
23691     editicon: false,
23692     
23693     // element that contains real text value.. (when hidden is used..)
23694      
23695     // private
23696     onRender : function(ct, position){
23697         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23698         if(this.hiddenName){
23699             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23700                     'before', true);
23701             this.hiddenField.value =
23702                 this.hiddenValue !== undefined ? this.hiddenValue :
23703                 this.value !== undefined ? this.value : '';
23704
23705             // prevent input submission
23706             this.el.dom.removeAttribute('name');
23707              
23708              
23709         }
23710         if(Roo.isGecko){
23711             this.el.dom.setAttribute('autocomplete', 'off');
23712         }
23713
23714         var cls = 'x-combo-list';
23715
23716         this.list = new Roo.Layer({
23717             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23718         });
23719
23720         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23721         this.list.setWidth(lw);
23722         this.list.swallowEvent('mousewheel');
23723         this.assetHeight = 0;
23724
23725         if(this.title){
23726             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23727             this.assetHeight += this.header.getHeight();
23728         }
23729
23730         this.innerList = this.list.createChild({cls:cls+'-inner'});
23731         this.innerList.on('mouseover', this.onViewOver, this);
23732         this.innerList.on('mousemove', this.onViewMove, this);
23733         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23734         
23735         if(this.allowBlank && !this.pageSize && !this.disableClear){
23736             this.footer = this.list.createChild({cls:cls+'-ft'});
23737             this.pageTb = new Roo.Toolbar(this.footer);
23738            
23739         }
23740         if(this.pageSize){
23741             this.footer = this.list.createChild({cls:cls+'-ft'});
23742             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23743                     {pageSize: this.pageSize});
23744             
23745         }
23746         
23747         if (this.pageTb && this.allowBlank && !this.disableClear) {
23748             var _this = this;
23749             this.pageTb.add(new Roo.Toolbar.Fill(), {
23750                 cls: 'x-btn-icon x-btn-clear',
23751                 text: '&#160;',
23752                 handler: function()
23753                 {
23754                     _this.collapse();
23755                     _this.clearValue();
23756                     _this.onSelect(false, -1);
23757                 }
23758             });
23759         }
23760         if (this.footer) {
23761             this.assetHeight += this.footer.getHeight();
23762         }
23763         
23764
23765         if(!this.tpl){
23766             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23767         }
23768
23769         this.view = new Roo.View(this.innerList, this.tpl, {
23770             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23771         });
23772
23773         this.view.on('click', this.onViewClick, this);
23774
23775         this.store.on('beforeload', this.onBeforeLoad, this);
23776         this.store.on('load', this.onLoad, this);
23777         this.store.on('loadexception', this.onLoadException, this);
23778
23779         if(this.resizable){
23780             this.resizer = new Roo.Resizable(this.list,  {
23781                pinned:true, handles:'se'
23782             });
23783             this.resizer.on('resize', function(r, w, h){
23784                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23785                 this.listWidth = w;
23786                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23787                 this.restrictHeight();
23788             }, this);
23789             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23790         }
23791         if(!this.editable){
23792             this.editable = true;
23793             this.setEditable(false);
23794         }  
23795         
23796         
23797         if (typeof(this.events.add.listeners) != 'undefined') {
23798             
23799             this.addicon = this.wrap.createChild(
23800                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23801        
23802             this.addicon.on('click', function(e) {
23803                 this.fireEvent('add', this);
23804             }, this);
23805         }
23806         if (typeof(this.events.edit.listeners) != 'undefined') {
23807             
23808             this.editicon = this.wrap.createChild(
23809                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23810             if (this.addicon) {
23811                 this.editicon.setStyle('margin-left', '40px');
23812             }
23813             this.editicon.on('click', function(e) {
23814                 
23815                 // we fire even  if inothing is selected..
23816                 this.fireEvent('edit', this, this.lastData );
23817                 
23818             }, this);
23819         }
23820         
23821         
23822         
23823     },
23824
23825     // private
23826     initEvents : function(){
23827         Roo.form.ComboBox.superclass.initEvents.call(this);
23828
23829         this.keyNav = new Roo.KeyNav(this.el, {
23830             "up" : function(e){
23831                 this.inKeyMode = true;
23832                 this.selectPrev();
23833             },
23834
23835             "down" : function(e){
23836                 if(!this.isExpanded()){
23837                     this.onTriggerClick();
23838                 }else{
23839                     this.inKeyMode = true;
23840                     this.selectNext();
23841                 }
23842             },
23843
23844             "enter" : function(e){
23845                 this.onViewClick();
23846                 //return true;
23847             },
23848
23849             "esc" : function(e){
23850                 this.collapse();
23851             },
23852
23853             "tab" : function(e){
23854                 this.onViewClick(false);
23855                 this.fireEvent("specialkey", this, e);
23856                 return true;
23857             },
23858
23859             scope : this,
23860
23861             doRelay : function(foo, bar, hname){
23862                 if(hname == 'down' || this.scope.isExpanded()){
23863                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23864                 }
23865                 return true;
23866             },
23867
23868             forceKeyDown: true
23869         });
23870         this.queryDelay = Math.max(this.queryDelay || 10,
23871                 this.mode == 'local' ? 10 : 250);
23872         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23873         if(this.typeAhead){
23874             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23875         }
23876         if(this.editable !== false){
23877             this.el.on("keyup", this.onKeyUp, this);
23878         }
23879         if(this.forceSelection){
23880             this.on('blur', this.doForce, this);
23881         }
23882     },
23883
23884     onDestroy : function(){
23885         if(this.view){
23886             this.view.setStore(null);
23887             this.view.el.removeAllListeners();
23888             this.view.el.remove();
23889             this.view.purgeListeners();
23890         }
23891         if(this.list){
23892             this.list.destroy();
23893         }
23894         if(this.store){
23895             this.store.un('beforeload', this.onBeforeLoad, this);
23896             this.store.un('load', this.onLoad, this);
23897             this.store.un('loadexception', this.onLoadException, this);
23898         }
23899         Roo.form.ComboBox.superclass.onDestroy.call(this);
23900     },
23901
23902     // private
23903     fireKey : function(e){
23904         if(e.isNavKeyPress() && !this.list.isVisible()){
23905             this.fireEvent("specialkey", this, e);
23906         }
23907     },
23908
23909     // private
23910     onResize: function(w, h){
23911         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23912         
23913         if(typeof w != 'number'){
23914             // we do not handle it!?!?
23915             return;
23916         }
23917         var tw = this.trigger.getWidth();
23918         tw += this.addicon ? this.addicon.getWidth() : 0;
23919         tw += this.editicon ? this.editicon.getWidth() : 0;
23920         var x = w - tw;
23921         this.el.setWidth( this.adjustWidth('input', x));
23922             
23923         this.trigger.setStyle('left', x+'px');
23924         
23925         if(this.list && this.listWidth === undefined){
23926             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23927             this.list.setWidth(lw);
23928             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23929         }
23930         
23931     
23932         
23933     },
23934
23935     /**
23936      * Allow or prevent the user from directly editing the field text.  If false is passed,
23937      * the user will only be able to select from the items defined in the dropdown list.  This method
23938      * is the runtime equivalent of setting the 'editable' config option at config time.
23939      * @param {Boolean} value True to allow the user to directly edit the field text
23940      */
23941     setEditable : function(value){
23942         if(value == this.editable){
23943             return;
23944         }
23945         this.editable = value;
23946         if(!value){
23947             this.el.dom.setAttribute('readOnly', true);
23948             this.el.on('mousedown', this.onTriggerClick,  this);
23949             this.el.addClass('x-combo-noedit');
23950         }else{
23951             this.el.dom.setAttribute('readOnly', false);
23952             this.el.un('mousedown', this.onTriggerClick,  this);
23953             this.el.removeClass('x-combo-noedit');
23954         }
23955     },
23956
23957     // private
23958     onBeforeLoad : function(){
23959         if(!this.hasFocus){
23960             return;
23961         }
23962         this.innerList.update(this.loadingText ?
23963                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23964         this.restrictHeight();
23965         this.selectedIndex = -1;
23966     },
23967
23968     // private
23969     onLoad : function(){
23970         if(!this.hasFocus){
23971             return;
23972         }
23973         if(this.store.getCount() > 0){
23974             this.expand();
23975             this.restrictHeight();
23976             if(this.lastQuery == this.allQuery){
23977                 if(this.editable){
23978                     this.el.dom.select();
23979                 }
23980                 if(!this.selectByValue(this.value, true)){
23981                     this.select(0, true);
23982                 }
23983             }else{
23984                 this.selectNext();
23985                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23986                     this.taTask.delay(this.typeAheadDelay);
23987                 }
23988             }
23989         }else{
23990             this.onEmptyResults();
23991         }
23992         //this.el.focus();
23993     },
23994     // private
23995     onLoadException : function()
23996     {
23997         this.collapse();
23998         Roo.log(this.store.reader.jsonData);
23999         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24000             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24001         }
24002         
24003         
24004     },
24005     // private
24006     onTypeAhead : function(){
24007         if(this.store.getCount() > 0){
24008             var r = this.store.getAt(0);
24009             var newValue = r.data[this.displayField];
24010             var len = newValue.length;
24011             var selStart = this.getRawValue().length;
24012             if(selStart != len){
24013                 this.setRawValue(newValue);
24014                 this.selectText(selStart, newValue.length);
24015             }
24016         }
24017     },
24018
24019     // private
24020     onSelect : function(record, index){
24021         if(this.fireEvent('beforeselect', this, record, index) !== false){
24022             this.setFromData(index > -1 ? record.data : false);
24023             this.collapse();
24024             this.fireEvent('select', this, record, index);
24025         }
24026     },
24027
24028     /**
24029      * Returns the currently selected field value or empty string if no value is set.
24030      * @return {String} value The selected value
24031      */
24032     getValue : function(){
24033         if(this.valueField){
24034             return typeof this.value != 'undefined' ? this.value : '';
24035         }else{
24036             return Roo.form.ComboBox.superclass.getValue.call(this);
24037         }
24038     },
24039
24040     /**
24041      * Clears any text/value currently set in the field
24042      */
24043     clearValue : function(){
24044         if(this.hiddenField){
24045             this.hiddenField.value = '';
24046         }
24047         this.value = '';
24048         this.setRawValue('');
24049         this.lastSelectionText = '';
24050         this.applyEmptyText();
24051     },
24052
24053     /**
24054      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24055      * will be displayed in the field.  If the value does not match the data value of an existing item,
24056      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24057      * Otherwise the field will be blank (although the value will still be set).
24058      * @param {String} value The value to match
24059      */
24060     setValue : function(v){
24061         var text = v;
24062         if(this.valueField){
24063             var r = this.findRecord(this.valueField, v);
24064             if(r){
24065                 text = r.data[this.displayField];
24066             }else if(this.valueNotFoundText !== undefined){
24067                 text = this.valueNotFoundText;
24068             }
24069         }
24070         this.lastSelectionText = text;
24071         if(this.hiddenField){
24072             this.hiddenField.value = v;
24073         }
24074         Roo.form.ComboBox.superclass.setValue.call(this, text);
24075         this.value = v;
24076     },
24077     /**
24078      * @property {Object} the last set data for the element
24079      */
24080     
24081     lastData : false,
24082     /**
24083      * Sets the value of the field based on a object which is related to the record format for the store.
24084      * @param {Object} value the value to set as. or false on reset?
24085      */
24086     setFromData : function(o){
24087         var dv = ''; // display value
24088         var vv = ''; // value value..
24089         this.lastData = o;
24090         if (this.displayField) {
24091             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24092         } else {
24093             // this is an error condition!!!
24094             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24095         }
24096         
24097         if(this.valueField){
24098             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24099         }
24100         if(this.hiddenField){
24101             this.hiddenField.value = vv;
24102             
24103             this.lastSelectionText = dv;
24104             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24105             this.value = vv;
24106             return;
24107         }
24108         // no hidden field.. - we store the value in 'value', but still display
24109         // display field!!!!
24110         this.lastSelectionText = dv;
24111         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24112         this.value = vv;
24113         
24114         
24115     },
24116     // private
24117     reset : function(){
24118         // overridden so that last data is reset..
24119         this.setValue(this.originalValue);
24120         this.clearInvalid();
24121         this.lastData = false;
24122         if (this.view) {
24123             this.view.clearSelections();
24124         }
24125     },
24126     // private
24127     findRecord : function(prop, value){
24128         var record;
24129         if(this.store.getCount() > 0){
24130             this.store.each(function(r){
24131                 if(r.data[prop] == value){
24132                     record = r;
24133                     return false;
24134                 }
24135                 return true;
24136             });
24137         }
24138         return record;
24139     },
24140     
24141     getName: function()
24142     {
24143         // returns hidden if it's set..
24144         if (!this.rendered) {return ''};
24145         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24146         
24147     },
24148     // private
24149     onViewMove : function(e, t){
24150         this.inKeyMode = false;
24151     },
24152
24153     // private
24154     onViewOver : function(e, t){
24155         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24156             return;
24157         }
24158         var item = this.view.findItemFromChild(t);
24159         if(item){
24160             var index = this.view.indexOf(item);
24161             this.select(index, false);
24162         }
24163     },
24164
24165     // private
24166     onViewClick : function(doFocus)
24167     {
24168         var index = this.view.getSelectedIndexes()[0];
24169         var r = this.store.getAt(index);
24170         if(r){
24171             this.onSelect(r, index);
24172         }
24173         if(doFocus !== false && !this.blockFocus){
24174             this.el.focus();
24175         }
24176     },
24177
24178     // private
24179     restrictHeight : function(){
24180         this.innerList.dom.style.height = '';
24181         var inner = this.innerList.dom;
24182         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24183         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24184         this.list.beginUpdate();
24185         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24186         this.list.alignTo(this.el, this.listAlign);
24187         this.list.endUpdate();
24188     },
24189
24190     // private
24191     onEmptyResults : function(){
24192         this.collapse();
24193     },
24194
24195     /**
24196      * Returns true if the dropdown list is expanded, else false.
24197      */
24198     isExpanded : function(){
24199         return this.list.isVisible();
24200     },
24201
24202     /**
24203      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24204      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24205      * @param {String} value The data value of the item to select
24206      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24207      * selected item if it is not currently in view (defaults to true)
24208      * @return {Boolean} True if the value matched an item in the list, else false
24209      */
24210     selectByValue : function(v, scrollIntoView){
24211         if(v !== undefined && v !== null){
24212             var r = this.findRecord(this.valueField || this.displayField, v);
24213             if(r){
24214                 this.select(this.store.indexOf(r), scrollIntoView);
24215                 return true;
24216             }
24217         }
24218         return false;
24219     },
24220
24221     /**
24222      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24223      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24224      * @param {Number} index The zero-based index of the list item to select
24225      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24226      * selected item if it is not currently in view (defaults to true)
24227      */
24228     select : function(index, scrollIntoView){
24229         this.selectedIndex = index;
24230         this.view.select(index);
24231         if(scrollIntoView !== false){
24232             var el = this.view.getNode(index);
24233             if(el){
24234                 this.innerList.scrollChildIntoView(el, false);
24235             }
24236         }
24237     },
24238
24239     // private
24240     selectNext : function(){
24241         var ct = this.store.getCount();
24242         if(ct > 0){
24243             if(this.selectedIndex == -1){
24244                 this.select(0);
24245             }else if(this.selectedIndex < ct-1){
24246                 this.select(this.selectedIndex+1);
24247             }
24248         }
24249     },
24250
24251     // private
24252     selectPrev : function(){
24253         var ct = this.store.getCount();
24254         if(ct > 0){
24255             if(this.selectedIndex == -1){
24256                 this.select(0);
24257             }else if(this.selectedIndex != 0){
24258                 this.select(this.selectedIndex-1);
24259             }
24260         }
24261     },
24262
24263     // private
24264     onKeyUp : function(e){
24265         if(this.editable !== false && !e.isSpecialKey()){
24266             this.lastKey = e.getKey();
24267             this.dqTask.delay(this.queryDelay);
24268         }
24269     },
24270
24271     // private
24272     validateBlur : function(){
24273         return !this.list || !this.list.isVisible();   
24274     },
24275
24276     // private
24277     initQuery : function(){
24278         this.doQuery(this.getRawValue());
24279     },
24280
24281     // private
24282     doForce : function(){
24283         if(this.el.dom.value.length > 0){
24284             this.el.dom.value =
24285                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24286             this.applyEmptyText();
24287         }
24288     },
24289
24290     /**
24291      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24292      * query allowing the query action to be canceled if needed.
24293      * @param {String} query The SQL query to execute
24294      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24295      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24296      * saved in the current store (defaults to false)
24297      */
24298     doQuery : function(q, forceAll){
24299         if(q === undefined || q === null){
24300             q = '';
24301         }
24302         var qe = {
24303             query: q,
24304             forceAll: forceAll,
24305             combo: this,
24306             cancel:false
24307         };
24308         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24309             return false;
24310         }
24311         q = qe.query;
24312         forceAll = qe.forceAll;
24313         if(forceAll === true || (q.length >= this.minChars)){
24314             if(this.lastQuery != q || this.alwaysQuery){
24315                 this.lastQuery = q;
24316                 if(this.mode == 'local'){
24317                     this.selectedIndex = -1;
24318                     if(forceAll){
24319                         this.store.clearFilter();
24320                     }else{
24321                         this.store.filter(this.displayField, q);
24322                     }
24323                     this.onLoad();
24324                 }else{
24325                     this.store.baseParams[this.queryParam] = q;
24326                     this.store.load({
24327                         params: this.getParams(q)
24328                     });
24329                     this.expand();
24330                 }
24331             }else{
24332                 this.selectedIndex = -1;
24333                 this.onLoad();   
24334             }
24335         }
24336     },
24337
24338     // private
24339     getParams : function(q){
24340         var p = {};
24341         //p[this.queryParam] = q;
24342         if(this.pageSize){
24343             p.start = 0;
24344             p.limit = this.pageSize;
24345         }
24346         return p;
24347     },
24348
24349     /**
24350      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24351      */
24352     collapse : function(){
24353         if(!this.isExpanded()){
24354             return;
24355         }
24356         this.list.hide();
24357         Roo.get(document).un('mousedown', this.collapseIf, this);
24358         Roo.get(document).un('mousewheel', this.collapseIf, this);
24359         if (!this.editable) {
24360             Roo.get(document).un('keydown', this.listKeyPress, this);
24361         }
24362         this.fireEvent('collapse', this);
24363     },
24364
24365     // private
24366     collapseIf : function(e){
24367         if(!e.within(this.wrap) && !e.within(this.list)){
24368             this.collapse();
24369         }
24370     },
24371
24372     /**
24373      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24374      */
24375     expand : function(){
24376         if(this.isExpanded() || !this.hasFocus){
24377             return;
24378         }
24379         this.list.alignTo(this.el, this.listAlign);
24380         this.list.show();
24381         Roo.get(document).on('mousedown', this.collapseIf, this);
24382         Roo.get(document).on('mousewheel', this.collapseIf, this);
24383         if (!this.editable) {
24384             Roo.get(document).on('keydown', this.listKeyPress, this);
24385         }
24386         
24387         this.fireEvent('expand', this);
24388     },
24389
24390     // private
24391     // Implements the default empty TriggerField.onTriggerClick function
24392     onTriggerClick : function(){
24393         if(this.disabled){
24394             return;
24395         }
24396         if(this.isExpanded()){
24397             this.collapse();
24398             if (!this.blockFocus) {
24399                 this.el.focus();
24400             }
24401             
24402         }else {
24403             this.hasFocus = true;
24404             if(this.triggerAction == 'all') {
24405                 this.doQuery(this.allQuery, true);
24406             } else {
24407                 this.doQuery(this.getRawValue());
24408             }
24409             if (!this.blockFocus) {
24410                 this.el.focus();
24411             }
24412         }
24413     },
24414     listKeyPress : function(e)
24415     {
24416         //Roo.log('listkeypress');
24417         // scroll to first matching element based on key pres..
24418         if (e.isSpecialKey()) {
24419             return false;
24420         }
24421         var k = String.fromCharCode(e.getKey()).toUpperCase();
24422         //Roo.log(k);
24423         var match  = false;
24424         var csel = this.view.getSelectedNodes();
24425         var cselitem = false;
24426         if (csel.length) {
24427             var ix = this.view.indexOf(csel[0]);
24428             cselitem  = this.store.getAt(ix);
24429             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24430                 cselitem = false;
24431             }
24432             
24433         }
24434         
24435         this.store.each(function(v) { 
24436             if (cselitem) {
24437                 // start at existing selection.
24438                 if (cselitem.id == v.id) {
24439                     cselitem = false;
24440                 }
24441                 return;
24442             }
24443                 
24444             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24445                 match = this.store.indexOf(v);
24446                 return false;
24447             }
24448         }, this);
24449         
24450         if (match === false) {
24451             return true; // no more action?
24452         }
24453         // scroll to?
24454         this.view.select(match);
24455         var sn = Roo.get(this.view.getSelectedNodes()[0])
24456         sn.scrollIntoView(sn.dom.parentNode, false);
24457     }
24458
24459     /** 
24460     * @cfg {Boolean} grow 
24461     * @hide 
24462     */
24463     /** 
24464     * @cfg {Number} growMin 
24465     * @hide 
24466     */
24467     /** 
24468     * @cfg {Number} growMax 
24469     * @hide 
24470     */
24471     /**
24472      * @hide
24473      * @method autoSize
24474      */
24475 });/*
24476  * Copyright(c) 2010-2012, Roo J Solutions Limited
24477  *
24478  * Licence LGPL
24479  *
24480  */
24481
24482 /**
24483  * @class Roo.form.ComboBoxArray
24484  * @extends Roo.form.TextField
24485  * A facebook style adder... for lists of email / people / countries  etc...
24486  * pick multiple items from a combo box, and shows each one.
24487  *
24488  *  Fred [x]  Brian [x]  [Pick another |v]
24489  *
24490  *
24491  *  For this to work: it needs various extra information
24492  *    - normal combo problay has
24493  *      name, hiddenName
24494  *    + displayField, valueField
24495  *
24496  *    For our purpose...
24497  *
24498  *
24499  *   If we change from 'extends' to wrapping...
24500  *   
24501  *  
24502  *
24503  
24504  
24505  * @constructor
24506  * Create a new ComboBoxArray.
24507  * @param {Object} config Configuration options
24508  */
24509  
24510
24511 Roo.form.ComboBoxArray = function(config)
24512 {
24513     
24514     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24515     
24516     this.items = new Roo.util.MixedCollection(false);
24517     
24518     // construct the child combo...
24519     
24520     
24521     
24522     
24523    
24524     
24525 }
24526
24527  
24528 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24529
24530     /**
24531      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24532      */
24533     
24534     lastData : false,
24535     
24536     // behavies liek a hiddne field
24537     inputType:      'hidden',
24538     /**
24539      * @cfg {Number} width The width of the box that displays the selected element
24540      */ 
24541     width:          300,
24542
24543     
24544     
24545     /**
24546      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24547      */
24548     name : false,
24549     /**
24550      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24551      */
24552     hiddenName : false,
24553     
24554     
24555     // private the array of items that are displayed..
24556     items  : false,
24557     // private - the hidden field el.
24558     hiddenEl : false,
24559     // private - the filed el..
24560     el : false,
24561     
24562     //validateValue : function() { return true; }, // all values are ok!
24563     //onAddClick: function() { },
24564     
24565     onRender : function(ct, position) 
24566     {
24567         
24568         // create the standard hidden element
24569         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24570         
24571         
24572         // give fake names to child combo;
24573         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24574         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24575         
24576         this.combo = Roo.factory(this.combo, Roo.form);
24577         this.combo.onRender(ct, position);
24578         this.combo.initEvents();
24579         
24580         // assigned so form know we need to do this..
24581         this.store          = this.combo.store;
24582         this.valueField     = this.combo.valueField;
24583         this.displayField   = this.combo.displayField ;
24584         
24585         
24586         this.combo.wrap.addClass('x-cbarray-grp');
24587         
24588         var cbwrap = this.combo.wrap.createChild(
24589             {tag: 'div', cls: 'x-cbarray-cb'},
24590             this.combo.el.dom
24591         );
24592         
24593              
24594         this.hiddenEl = this.combo.wrap.createChild({
24595             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24596         });
24597         this.el = this.combo.wrap.createChild({
24598             tag: 'input',  type:'hidden' , name: this.name, value : ''
24599         });
24600          //   this.el.dom.removeAttribute("name");
24601         
24602         
24603         this.outerWrap = this.combo.wrap;
24604         this.wrap = cbwrap;
24605         
24606         this.outerWrap.setWidth(this.width);
24607         this.outerWrap.dom.removeChild(this.el.dom);
24608         
24609         this.wrap.dom.appendChild(this.el.dom);
24610         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24611         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24612         
24613         this.combo.trigger.setStyle('position','relative');
24614         this.combo.trigger.setStyle('left', '0px');
24615         this.combo.trigger.setStyle('top', '2px');
24616         
24617         this.combo.el.setStyle('vertical-align', 'text-bottom');
24618         
24619         //this.trigger.setStyle('vertical-align', 'top');
24620         
24621         // this should use the code from combo really... on('add' ....)
24622         if (this.adder) {
24623             
24624         
24625             this.adder = this.outerWrap.createChild(
24626                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24627             var _t = this;
24628             this.adder.on('click', function(e) {
24629                 _t.fireEvent('adderclick', this, e);
24630             }, _t);
24631         }
24632         //var _t = this;
24633         //this.adder.on('click', this.onAddClick, _t);
24634         
24635         
24636         this.combo.on('select', function(cb, rec, ix) {
24637             this.addItem(rec.data);
24638             
24639             cb.setValue('');
24640             cb.el.dom.value = '';
24641             //cb.lastData = rec.data;
24642             // add to list
24643             
24644         }, this);
24645         
24646         
24647     },
24648     
24649     
24650     getName: function()
24651     {
24652         // returns hidden if it's set..
24653         if (!this.rendered) {return ''};
24654         return  this.hiddenName ? this.hiddenName : this.name;
24655         
24656     },
24657     
24658     
24659     onResize: function(w, h){
24660         
24661         return;
24662         // not sure if this is needed..
24663         //this.combo.onResize(w,h);
24664         
24665         if(typeof w != 'number'){
24666             // we do not handle it!?!?
24667             return;
24668         }
24669         var tw = this.combo.trigger.getWidth();
24670         tw += this.addicon ? this.addicon.getWidth() : 0;
24671         tw += this.editicon ? this.editicon.getWidth() : 0;
24672         var x = w - tw;
24673         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24674             
24675         this.combo.trigger.setStyle('left', '0px');
24676         
24677         if(this.list && this.listWidth === undefined){
24678             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24679             this.list.setWidth(lw);
24680             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24681         }
24682         
24683     
24684         
24685     },
24686     
24687     addItem: function(rec)
24688     {
24689         var valueField = this.combo.valueField;
24690         var displayField = this.combo.displayField;
24691         if (this.items.indexOfKey(rec[valueField]) > -1) {
24692             //console.log("GOT " + rec.data.id);
24693             return;
24694         }
24695         
24696         var x = new Roo.form.ComboBoxArray.Item({
24697             //id : rec[this.idField],
24698             data : rec,
24699             displayField : displayField ,
24700             tipField : displayField ,
24701             cb : this
24702         });
24703         // use the 
24704         this.items.add(rec[valueField],x);
24705         // add it before the element..
24706         this.updateHiddenEl();
24707         x.render(this.outerWrap, this.wrap.dom);
24708         // add the image handler..
24709     },
24710     
24711     updateHiddenEl : function()
24712     {
24713         this.validate();
24714         if (!this.hiddenEl) {
24715             return;
24716         }
24717         var ar = [];
24718         var idField = this.combo.valueField;
24719         
24720         this.items.each(function(f) {
24721             ar.push(f.data[idField]);
24722            
24723         });
24724         this.hiddenEl.dom.value = ar.join(',');
24725         this.validate();
24726     },
24727     
24728     reset : function()
24729     {
24730         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24731         this.items.each(function(f) {
24732            f.remove(); 
24733         });
24734         this.el.dom.value = '';
24735         if (this.hiddenEl) {
24736             this.hiddenEl.dom.value = '';
24737         }
24738         
24739     },
24740     getValue: function()
24741     {
24742         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24743     },
24744     setValue: function(v) // not a valid action - must use addItems..
24745     {
24746          
24747         this.reset();
24748         
24749         
24750         
24751         if (this.store.isLocal && (typeof(v) == 'string')) {
24752             // then we can use the store to find the values..
24753             // comma seperated at present.. this needs to allow JSON based encoding..
24754             this.hiddenEl.value  = v;
24755             var v_ar = [];
24756             Roo.each(v.split(','), function(k) {
24757                 Roo.log("CHECK " + this.valueField + ',' + k);
24758                 var li = this.store.query(this.valueField, k);
24759                 if (!li.length) {
24760                     return;
24761                 }
24762                 add = {};
24763                 add[this.valueField] = k;
24764                 add[this.displayField] = li.item(0).data[this.displayField];
24765                 
24766                 this.addItem(add);
24767             }, this) 
24768              
24769         }
24770         if (typeof(v) == 'object') {
24771             // then let's assume it's an array of objects..
24772             Roo.each(v, function(l) {
24773                 this.addItem(l);
24774             }, this);
24775              
24776         }
24777         
24778         
24779     },
24780     setFromData: function(v)
24781     {
24782         // this recieves an object, if setValues is called.
24783         this.reset();
24784         this.el.dom.value = v[this.displayField];
24785         this.hiddenEl.dom.value = v[this.valueField];
24786         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24787             return;
24788         }
24789         var kv = v[this.valueField];
24790         var dv = v[this.displayField];
24791         kv = typeof(kv) != 'string' ? '' : kv;
24792         dv = typeof(dv) != 'string' ? '' : dv;
24793         
24794         
24795         var keys = kv.split(',');
24796         var display = dv.split(',');
24797         for (var i = 0 ; i < keys.length; i++) {
24798             
24799             add = {};
24800             add[this.valueField] = keys[i];
24801             add[this.displayField] = display[i];
24802             this.addItem(add);
24803         }
24804       
24805         
24806     },
24807     
24808     
24809     validateValue : function(value){
24810         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24811         
24812     }
24813     
24814 });
24815
24816
24817
24818 /**
24819  * @class Roo.form.ComboBoxArray.Item
24820  * @extends Roo.BoxComponent
24821  * A selected item in the list
24822  *  Fred [x]  Brian [x]  [Pick another |v]
24823  * 
24824  * @constructor
24825  * Create a new item.
24826  * @param {Object} config Configuration options
24827  */
24828  
24829 Roo.form.ComboBoxArray.Item = function(config) {
24830     config.id = Roo.id();
24831     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24832 }
24833
24834 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24835     data : {},
24836     cb: false,
24837     displayField : false,
24838     tipField : false,
24839     
24840     
24841     defaultAutoCreate : {
24842         tag: 'div',
24843         cls: 'x-cbarray-item',
24844         cn : [ 
24845             { tag: 'div' },
24846             {
24847                 tag: 'img',
24848                 width:16,
24849                 height : 16,
24850                 src : Roo.BLANK_IMAGE_URL ,
24851                 align: 'center'
24852             }
24853         ]
24854         
24855     },
24856     
24857  
24858     onRender : function(ct, position)
24859     {
24860         Roo.form.Field.superclass.onRender.call(this, ct, position);
24861         
24862         if(!this.el){
24863             var cfg = this.getAutoCreate();
24864             this.el = ct.createChild(cfg, position);
24865         }
24866         
24867         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24868         
24869         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24870             this.cb.renderer(this.data) :
24871             String.format('{0}',this.data[this.displayField]);
24872         
24873             
24874         this.el.child('div').dom.setAttribute('qtip',
24875                         String.format('{0}',this.data[this.tipField])
24876         );
24877         
24878         this.el.child('img').on('click', this.remove, this);
24879         
24880     },
24881    
24882     remove : function()
24883     {
24884         
24885         this.cb.items.remove(this);
24886         this.el.child('img').un('click', this.remove, this);
24887         this.el.remove();
24888         this.cb.updateHiddenEl();
24889     }
24890     
24891     
24892 });/*
24893  * Based on:
24894  * Ext JS Library 1.1.1
24895  * Copyright(c) 2006-2007, Ext JS, LLC.
24896  *
24897  * Originally Released Under LGPL - original licence link has changed is not relivant.
24898  *
24899  * Fork - LGPL
24900  * <script type="text/javascript">
24901  */
24902 /**
24903  * @class Roo.form.Checkbox
24904  * @extends Roo.form.Field
24905  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24906  * @constructor
24907  * Creates a new Checkbox
24908  * @param {Object} config Configuration options
24909  */
24910 Roo.form.Checkbox = function(config){
24911     Roo.form.Checkbox.superclass.constructor.call(this, config);
24912     this.addEvents({
24913         /**
24914          * @event check
24915          * Fires when the checkbox is checked or unchecked.
24916              * @param {Roo.form.Checkbox} this This checkbox
24917              * @param {Boolean} checked The new checked value
24918              */
24919         check : true
24920     });
24921 };
24922
24923 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24924     /**
24925      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24926      */
24927     focusClass : undefined,
24928     /**
24929      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24930      */
24931     fieldClass: "x-form-field",
24932     /**
24933      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24934      */
24935     checked: false,
24936     /**
24937      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24938      * {tag: "input", type: "checkbox", autocomplete: "off"})
24939      */
24940     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24941     /**
24942      * @cfg {String} boxLabel The text that appears beside the checkbox
24943      */
24944     boxLabel : "",
24945     /**
24946      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24947      */  
24948     inputValue : '1',
24949     /**
24950      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24951      */
24952      valueOff: '0', // value when not checked..
24953
24954     actionMode : 'viewEl', 
24955     //
24956     // private
24957     itemCls : 'x-menu-check-item x-form-item',
24958     groupClass : 'x-menu-group-item',
24959     inputType : 'hidden',
24960     
24961     
24962     inSetChecked: false, // check that we are not calling self...
24963     
24964     inputElement: false, // real input element?
24965     basedOn: false, // ????
24966     
24967     isFormField: true, // not sure where this is needed!!!!
24968
24969     onResize : function(){
24970         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24971         if(!this.boxLabel){
24972             this.el.alignTo(this.wrap, 'c-c');
24973         }
24974     },
24975
24976     initEvents : function(){
24977         Roo.form.Checkbox.superclass.initEvents.call(this);
24978         this.el.on("click", this.onClick,  this);
24979         this.el.on("change", this.onClick,  this);
24980     },
24981
24982
24983     getResizeEl : function(){
24984         return this.wrap;
24985     },
24986
24987     getPositionEl : function(){
24988         return this.wrap;
24989     },
24990
24991     // private
24992     onRender : function(ct, position){
24993         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24994         /*
24995         if(this.inputValue !== undefined){
24996             this.el.dom.value = this.inputValue;
24997         }
24998         */
24999         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25000         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25001         var viewEl = this.wrap.createChild({ 
25002             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25003         this.viewEl = viewEl;   
25004         this.wrap.on('click', this.onClick,  this); 
25005         
25006         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25007         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25008         
25009         
25010         
25011         if(this.boxLabel){
25012             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25013         //    viewEl.on('click', this.onClick,  this); 
25014         }
25015         //if(this.checked){
25016             this.setChecked(this.checked);
25017         //}else{
25018             //this.checked = this.el.dom;
25019         //}
25020
25021     },
25022
25023     // private
25024     initValue : Roo.emptyFn,
25025
25026     /**
25027      * Returns the checked state of the checkbox.
25028      * @return {Boolean} True if checked, else false
25029      */
25030     getValue : function(){
25031         if(this.el){
25032             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25033         }
25034         return this.valueOff;
25035         
25036     },
25037
25038         // private
25039     onClick : function(){ 
25040         this.setChecked(!this.checked);
25041
25042         //if(this.el.dom.checked != this.checked){
25043         //    this.setValue(this.el.dom.checked);
25044        // }
25045     },
25046
25047     /**
25048      * Sets the checked state of the checkbox.
25049      * On is always based on a string comparison between inputValue and the param.
25050      * @param {Boolean/String} value - the value to set 
25051      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25052      */
25053     setValue : function(v,suppressEvent){
25054         
25055         
25056         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25057         //if(this.el && this.el.dom){
25058         //    this.el.dom.checked = this.checked;
25059         //    this.el.dom.defaultChecked = this.checked;
25060         //}
25061         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25062         //this.fireEvent("check", this, this.checked);
25063     },
25064     // private..
25065     setChecked : function(state,suppressEvent)
25066     {
25067         if (this.inSetChecked) {
25068             this.checked = state;
25069             return;
25070         }
25071         
25072     
25073         if(this.wrap){
25074             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25075         }
25076         this.checked = state;
25077         if(suppressEvent !== true){
25078             this.fireEvent('check', this, state);
25079         }
25080         this.inSetChecked = true;
25081         this.el.dom.value = state ? this.inputValue : this.valueOff;
25082         this.inSetChecked = false;
25083         
25084     },
25085     // handle setting of hidden value by some other method!!?!?
25086     setFromHidden: function()
25087     {
25088         if(!this.el){
25089             return;
25090         }
25091         //console.log("SET FROM HIDDEN");
25092         //alert('setFrom hidden');
25093         this.setValue(this.el.dom.value);
25094     },
25095     
25096     onDestroy : function()
25097     {
25098         if(this.viewEl){
25099             Roo.get(this.viewEl).remove();
25100         }
25101          
25102         Roo.form.Checkbox.superclass.onDestroy.call(this);
25103     }
25104
25105 });/*
25106  * Based on:
25107  * Ext JS Library 1.1.1
25108  * Copyright(c) 2006-2007, Ext JS, LLC.
25109  *
25110  * Originally Released Under LGPL - original licence link has changed is not relivant.
25111  *
25112  * Fork - LGPL
25113  * <script type="text/javascript">
25114  */
25115  
25116 /**
25117  * @class Roo.form.Radio
25118  * @extends Roo.form.Checkbox
25119  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25120  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25121  * @constructor
25122  * Creates a new Radio
25123  * @param {Object} config Configuration options
25124  */
25125 Roo.form.Radio = function(){
25126     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25127 };
25128 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25129     inputType: 'radio',
25130
25131     /**
25132      * If this radio is part of a group, it will return the selected value
25133      * @return {String}
25134      */
25135     getGroupValue : function(){
25136         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25137     }
25138 });//<script type="text/javascript">
25139
25140 /*
25141  * Ext JS Library 1.1.1
25142  * Copyright(c) 2006-2007, Ext JS, LLC.
25143  * licensing@extjs.com
25144  * 
25145  * http://www.extjs.com/license
25146  */
25147  
25148  /*
25149   * 
25150   * Known bugs:
25151   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25152   * - IE ? - no idea how much works there.
25153   * 
25154   * 
25155   * 
25156   */
25157  
25158
25159 /**
25160  * @class Ext.form.HtmlEditor
25161  * @extends Ext.form.Field
25162  * Provides a lightweight HTML Editor component.
25163  *
25164  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25165  * 
25166  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25167  * supported by this editor.</b><br/><br/>
25168  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25169  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25170  */
25171 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25172       /**
25173      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25174      */
25175     toolbars : false,
25176     /**
25177      * @cfg {String} createLinkText The default text for the create link prompt
25178      */
25179     createLinkText : 'Please enter the URL for the link:',
25180     /**
25181      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25182      */
25183     defaultLinkValue : 'http:/'+'/',
25184    
25185      /**
25186      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25187      *                        Roo.resizable.
25188      */
25189     resizable : false,
25190      /**
25191      * @cfg {Number} height (in pixels)
25192      */   
25193     height: 300,
25194    /**
25195      * @cfg {Number} width (in pixels)
25196      */   
25197     width: 500,
25198     
25199     /**
25200      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25201      * 
25202      */
25203     stylesheets: false,
25204     
25205     // id of frame..
25206     frameId: false,
25207     
25208     // private properties
25209     validationEvent : false,
25210     deferHeight: true,
25211     initialized : false,
25212     activated : false,
25213     sourceEditMode : false,
25214     onFocus : Roo.emptyFn,
25215     iframePad:3,
25216     hideMode:'offsets',
25217     
25218     defaultAutoCreate : { // modified by initCompnoent..
25219         tag: "textarea",
25220         style:"width:500px;height:300px;",
25221         autocomplete: "off"
25222     },
25223
25224     // private
25225     initComponent : function(){
25226         this.addEvents({
25227             /**
25228              * @event initialize
25229              * Fires when the editor is fully initialized (including the iframe)
25230              * @param {HtmlEditor} this
25231              */
25232             initialize: true,
25233             /**
25234              * @event activate
25235              * Fires when the editor is first receives the focus. Any insertion must wait
25236              * until after this event.
25237              * @param {HtmlEditor} this
25238              */
25239             activate: true,
25240              /**
25241              * @event beforesync
25242              * Fires before the textarea is updated with content from the editor iframe. Return false
25243              * to cancel the sync.
25244              * @param {HtmlEditor} this
25245              * @param {String} html
25246              */
25247             beforesync: true,
25248              /**
25249              * @event beforepush
25250              * Fires before the iframe editor is updated with content from the textarea. Return false
25251              * to cancel the push.
25252              * @param {HtmlEditor} this
25253              * @param {String} html
25254              */
25255             beforepush: true,
25256              /**
25257              * @event sync
25258              * Fires when the textarea is updated with content from the editor iframe.
25259              * @param {HtmlEditor} this
25260              * @param {String} html
25261              */
25262             sync: true,
25263              /**
25264              * @event push
25265              * Fires when the iframe editor is updated with content from the textarea.
25266              * @param {HtmlEditor} this
25267              * @param {String} html
25268              */
25269             push: true,
25270              /**
25271              * @event editmodechange
25272              * Fires when the editor switches edit modes
25273              * @param {HtmlEditor} this
25274              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25275              */
25276             editmodechange: true,
25277             /**
25278              * @event editorevent
25279              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25280              * @param {HtmlEditor} this
25281              */
25282             editorevent: true
25283         });
25284         this.defaultAutoCreate =  {
25285             tag: "textarea",
25286             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25287             autocomplete: "off"
25288         };
25289     },
25290
25291     /**
25292      * Protected method that will not generally be called directly. It
25293      * is called when the editor creates its toolbar. Override this method if you need to
25294      * add custom toolbar buttons.
25295      * @param {HtmlEditor} editor
25296      */
25297     createToolbar : function(editor){
25298         if (!editor.toolbars || !editor.toolbars.length) {
25299             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25300         }
25301         
25302         for (var i =0 ; i < editor.toolbars.length;i++) {
25303             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
25304             editor.toolbars[i].init(editor);
25305         }
25306          
25307         
25308     },
25309
25310     /**
25311      * Protected method that will not generally be called directly. It
25312      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25313      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25314      */
25315     getDocMarkup : function(){
25316         // body styles..
25317         var st = '';
25318         if (this.stylesheets === false) {
25319             
25320             Roo.get(document.head).select('style').each(function(node) {
25321                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25322             });
25323             
25324             Roo.get(document.head).select('link').each(function(node) { 
25325                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25326             });
25327             
25328         } else if (!this.stylesheets.length) {
25329                 // simple..
25330                 st = '<style type="text/css">' +
25331                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25332                    '</style>';
25333         } else {
25334             Roo.each(this.stylesheets, function(s) {
25335                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25336             });
25337             
25338         }
25339         
25340         st +=  '<style type="text/css">' +
25341             'IMG { cursor: pointer } ' +
25342         '</style>';
25343
25344         
25345         return '<html><head>' + st  +
25346             //<style type="text/css">' +
25347             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25348             //'</style>' +
25349             ' </head><body class="roo-htmleditor-body"></body></html>';
25350     },
25351
25352     // private
25353     onRender : function(ct, position)
25354     {
25355         var _t = this;
25356         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25357         this.el.dom.style.border = '0 none';
25358         this.el.dom.setAttribute('tabIndex', -1);
25359         this.el.addClass('x-hidden');
25360         if(Roo.isIE){ // fix IE 1px bogus margin
25361             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25362         }
25363         this.wrap = this.el.wrap({
25364             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25365         });
25366         
25367         if (this.resizable) {
25368             this.resizeEl = new Roo.Resizable(this.wrap, {
25369                 pinned : true,
25370                 wrap: true,
25371                 dynamic : true,
25372                 minHeight : this.height,
25373                 height: this.height,
25374                 handles : this.resizable,
25375                 width: this.width,
25376                 listeners : {
25377                     resize : function(r, w, h) {
25378                         _t.onResize(w,h); // -something
25379                     }
25380                 }
25381             });
25382             
25383         }
25384
25385         this.frameId = Roo.id();
25386         
25387         this.createToolbar(this);
25388         
25389       
25390         
25391         var iframe = this.wrap.createChild({
25392             tag: 'iframe',
25393             id: this.frameId,
25394             name: this.frameId,
25395             frameBorder : 'no',
25396             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25397         }, this.el
25398         );
25399         
25400        // console.log(iframe);
25401         //this.wrap.dom.appendChild(iframe);
25402
25403         this.iframe = iframe.dom;
25404
25405          this.assignDocWin();
25406         
25407         this.doc.designMode = 'on';
25408        
25409         this.doc.open();
25410         this.doc.write(this.getDocMarkup());
25411         this.doc.close();
25412
25413         
25414         var task = { // must defer to wait for browser to be ready
25415             run : function(){
25416                 //console.log("run task?" + this.doc.readyState);
25417                 this.assignDocWin();
25418                 if(this.doc.body || this.doc.readyState == 'complete'){
25419                     try {
25420                         this.doc.designMode="on";
25421                     } catch (e) {
25422                         return;
25423                     }
25424                     Roo.TaskMgr.stop(task);
25425                     this.initEditor.defer(10, this);
25426                 }
25427             },
25428             interval : 10,
25429             duration:10000,
25430             scope: this
25431         };
25432         Roo.TaskMgr.start(task);
25433
25434         if(!this.width){
25435             this.setSize(this.wrap.getSize());
25436         }
25437         if (this.resizeEl) {
25438             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25439             // should trigger onReize..
25440         }
25441     },
25442
25443     // private
25444     onResize : function(w, h)
25445     {
25446         //Roo.log('resize: ' +w + ',' + h );
25447         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25448         if(this.el && this.iframe){
25449             if(typeof w == 'number'){
25450                 var aw = w - this.wrap.getFrameWidth('lr');
25451                 this.el.setWidth(this.adjustWidth('textarea', aw));
25452                 this.iframe.style.width = aw + 'px';
25453             }
25454             if(typeof h == 'number'){
25455                 var tbh = 0;
25456                 for (var i =0; i < this.toolbars.length;i++) {
25457                     // fixme - ask toolbars for heights?
25458                     tbh += this.toolbars[i].tb.el.getHeight();
25459                     if (this.toolbars[i].footer) {
25460                         tbh += this.toolbars[i].footer.el.getHeight();
25461                     }
25462                 }
25463                 
25464                 
25465                 
25466                 
25467                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25468                 ah -= 5; // knock a few pixes off for look..
25469                 this.el.setHeight(this.adjustWidth('textarea', ah));
25470                 this.iframe.style.height = ah + 'px';
25471                 if(this.doc){
25472                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25473                 }
25474             }
25475         }
25476     },
25477
25478     /**
25479      * Toggles the editor between standard and source edit mode.
25480      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25481      */
25482     toggleSourceEdit : function(sourceEditMode){
25483         
25484         this.sourceEditMode = sourceEditMode === true;
25485         
25486         if(this.sourceEditMode){
25487 //            Roo.log('in');
25488 //            Roo.log(this.syncValue());
25489             this.syncValue();
25490             this.iframe.className = 'x-hidden';
25491             this.el.removeClass('x-hidden');
25492             this.el.dom.removeAttribute('tabIndex');
25493             this.el.focus();
25494         }else{
25495 //            Roo.log('out')
25496 //            Roo.log(this.pushValue()); 
25497             this.pushValue();
25498             this.iframe.className = '';
25499             this.el.addClass('x-hidden');
25500             this.el.dom.setAttribute('tabIndex', -1);
25501             this.deferFocus();
25502         }
25503         this.setSize(this.wrap.getSize());
25504         this.fireEvent('editmodechange', this, this.sourceEditMode);
25505     },
25506
25507     // private used internally
25508     createLink : function(){
25509         var url = prompt(this.createLinkText, this.defaultLinkValue);
25510         if(url && url != 'http:/'+'/'){
25511             this.relayCmd('createlink', url);
25512         }
25513     },
25514
25515     // private (for BoxComponent)
25516     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25517
25518     // private (for BoxComponent)
25519     getResizeEl : function(){
25520         return this.wrap;
25521     },
25522
25523     // private (for BoxComponent)
25524     getPositionEl : function(){
25525         return this.wrap;
25526     },
25527
25528     // private
25529     initEvents : function(){
25530         this.originalValue = this.getValue();
25531     },
25532
25533     /**
25534      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25535      * @method
25536      */
25537     markInvalid : Roo.emptyFn,
25538     /**
25539      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25540      * @method
25541      */
25542     clearInvalid : Roo.emptyFn,
25543
25544     setValue : function(v){
25545         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25546         this.pushValue();
25547     },
25548
25549     /**
25550      * Protected method that will not generally be called directly. If you need/want
25551      * custom HTML cleanup, this is the method you should override.
25552      * @param {String} html The HTML to be cleaned
25553      * return {String} The cleaned HTML
25554      */
25555     cleanHtml : function(html){
25556         html = String(html);
25557         if(html.length > 5){
25558             if(Roo.isSafari){ // strip safari nonsense
25559                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25560             }
25561         }
25562         if(html == '&nbsp;'){
25563             html = '';
25564         }
25565         return html;
25566     },
25567
25568     /**
25569      * Protected method that will not generally be called directly. Syncs the contents
25570      * of the editor iframe with the textarea.
25571      */
25572     syncValue : function(){
25573         if(this.initialized){
25574             var bd = (this.doc.body || this.doc.documentElement);
25575             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25576             var html = bd.innerHTML;
25577             if(Roo.isSafari){
25578                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25579                 var m = bs.match(/text-align:(.*?);/i);
25580                 if(m && m[1]){
25581                     html = '<div style="'+m[0]+'">' + html + '</div>';
25582                 }
25583             }
25584             html = this.cleanHtml(html);
25585             // fix up the special chars.. normaly like back quotes in word...
25586             // however we do not want to do this with chinese..
25587             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25588                 var cc = b.charCodeAt();
25589                 if (
25590                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25591                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25592                     (cc >= 0xf900 && cc < 0xfb00 )
25593                 ) {
25594                         return b;
25595                 }
25596                 return "&#"+cc+";" 
25597             });
25598             if(this.fireEvent('beforesync', this, html) !== false){
25599                 this.el.dom.value = html;
25600                 this.fireEvent('sync', this, html);
25601             }
25602         }
25603     },
25604
25605     /**
25606      * Protected method that will not generally be called directly. Pushes the value of the textarea
25607      * into the iframe editor.
25608      */
25609     pushValue : function(){
25610         if(this.initialized){
25611             var v = this.el.dom.value;
25612             
25613             if(v.length < 1){
25614                 v = '&#160;';
25615             }
25616             
25617             if(this.fireEvent('beforepush', this, v) !== false){
25618                 var d = (this.doc.body || this.doc.documentElement);
25619                 d.innerHTML = v;
25620                 this.cleanUpPaste();
25621                 this.el.dom.value = d.innerHTML;
25622                 this.fireEvent('push', this, v);
25623             }
25624         }
25625     },
25626
25627     // private
25628     deferFocus : function(){
25629         this.focus.defer(10, this);
25630     },
25631
25632     // doc'ed in Field
25633     focus : function(){
25634         if(this.win && !this.sourceEditMode){
25635             this.win.focus();
25636         }else{
25637             this.el.focus();
25638         }
25639     },
25640     
25641     assignDocWin: function()
25642     {
25643         var iframe = this.iframe;
25644         
25645          if(Roo.isIE){
25646             this.doc = iframe.contentWindow.document;
25647             this.win = iframe.contentWindow;
25648         } else {
25649             if (!Roo.get(this.frameId)) {
25650                 return;
25651             }
25652             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25653             this.win = Roo.get(this.frameId).dom.contentWindow;
25654         }
25655     },
25656     
25657     // private
25658     initEditor : function(){
25659         //console.log("INIT EDITOR");
25660         this.assignDocWin();
25661         
25662         
25663         
25664         this.doc.designMode="on";
25665         this.doc.open();
25666         this.doc.write(this.getDocMarkup());
25667         this.doc.close();
25668         
25669         var dbody = (this.doc.body || this.doc.documentElement);
25670         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25671         // this copies styles from the containing element into thsi one..
25672         // not sure why we need all of this..
25673         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25674         ss['background-attachment'] = 'fixed'; // w3c
25675         dbody.bgProperties = 'fixed'; // ie
25676         Roo.DomHelper.applyStyles(dbody, ss);
25677         Roo.EventManager.on(this.doc, {
25678             //'mousedown': this.onEditorEvent,
25679             'mouseup': this.onEditorEvent,
25680             'dblclick': this.onEditorEvent,
25681             'click': this.onEditorEvent,
25682             'keyup': this.onEditorEvent,
25683             buffer:100,
25684             scope: this
25685         });
25686         if(Roo.isGecko){
25687             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25688         }
25689         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25690             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25691         }
25692         this.initialized = true;
25693
25694         this.fireEvent('initialize', this);
25695         this.pushValue();
25696     },
25697
25698     // private
25699     onDestroy : function(){
25700         
25701         
25702         
25703         if(this.rendered){
25704             
25705             for (var i =0; i < this.toolbars.length;i++) {
25706                 // fixme - ask toolbars for heights?
25707                 this.toolbars[i].onDestroy();
25708             }
25709             
25710             this.wrap.dom.innerHTML = '';
25711             this.wrap.remove();
25712         }
25713     },
25714
25715     // private
25716     onFirstFocus : function(){
25717         
25718         this.assignDocWin();
25719         
25720         
25721         this.activated = true;
25722         for (var i =0; i < this.toolbars.length;i++) {
25723             this.toolbars[i].onFirstFocus();
25724         }
25725        
25726         if(Roo.isGecko){ // prevent silly gecko errors
25727             this.win.focus();
25728             var s = this.win.getSelection();
25729             if(!s.focusNode || s.focusNode.nodeType != 3){
25730                 var r = s.getRangeAt(0);
25731                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25732                 r.collapse(true);
25733                 this.deferFocus();
25734             }
25735             try{
25736                 this.execCmd('useCSS', true);
25737                 this.execCmd('styleWithCSS', false);
25738             }catch(e){}
25739         }
25740         this.fireEvent('activate', this);
25741     },
25742
25743     // private
25744     adjustFont: function(btn){
25745         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25746         //if(Roo.isSafari){ // safari
25747         //    adjust *= 2;
25748        // }
25749         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25750         if(Roo.isSafari){ // safari
25751             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25752             v =  (v < 10) ? 10 : v;
25753             v =  (v > 48) ? 48 : v;
25754             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25755             
25756         }
25757         
25758         
25759         v = Math.max(1, v+adjust);
25760         
25761         this.execCmd('FontSize', v  );
25762     },
25763
25764     onEditorEvent : function(e){
25765         this.fireEvent('editorevent', this, e);
25766       //  this.updateToolbar();
25767         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25768     },
25769
25770     insertTag : function(tg)
25771     {
25772         // could be a bit smarter... -> wrap the current selected tRoo..
25773         
25774         this.execCmd("formatblock",   tg);
25775         
25776     },
25777     
25778     insertText : function(txt)
25779     {
25780         
25781         
25782         range = this.createRange();
25783         range.deleteContents();
25784                //alert(Sender.getAttribute('label'));
25785                
25786         range.insertNode(this.doc.createTextNode(txt));
25787     } ,
25788     
25789     // private
25790     relayBtnCmd : function(btn){
25791         this.relayCmd(btn.cmd);
25792     },
25793
25794     /**
25795      * Executes a Midas editor command on the editor document and performs necessary focus and
25796      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25797      * @param {String} cmd The Midas command
25798      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25799      */
25800     relayCmd : function(cmd, value){
25801         this.win.focus();
25802         this.execCmd(cmd, value);
25803         this.fireEvent('editorevent', this);
25804         //this.updateToolbar();
25805         this.deferFocus();
25806     },
25807
25808     /**
25809      * Executes a Midas editor command directly on the editor document.
25810      * For visual commands, you should use {@link #relayCmd} instead.
25811      * <b>This should only be called after the editor is initialized.</b>
25812      * @param {String} cmd The Midas command
25813      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25814      */
25815     execCmd : function(cmd, value){
25816         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25817         this.syncValue();
25818     },
25819  
25820  
25821    
25822     /**
25823      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25824      * to insert tRoo.
25825      * @param {String} text | dom node.. 
25826      */
25827     insertAtCursor : function(text)
25828     {
25829         
25830         
25831         
25832         if(!this.activated){
25833             return;
25834         }
25835         /*
25836         if(Roo.isIE){
25837             this.win.focus();
25838             var r = this.doc.selection.createRange();
25839             if(r){
25840                 r.collapse(true);
25841                 r.pasteHTML(text);
25842                 this.syncValue();
25843                 this.deferFocus();
25844             
25845             }
25846             return;
25847         }
25848         */
25849         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25850             this.win.focus();
25851             
25852             
25853             // from jquery ui (MIT licenced)
25854             var range, node;
25855             var win = this.win;
25856             
25857             if (win.getSelection && win.getSelection().getRangeAt) {
25858                 range = win.getSelection().getRangeAt(0);
25859                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25860                 range.insertNode(node);
25861             } else if (win.document.selection && win.document.selection.createRange) {
25862                 // no firefox support
25863                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25864                 win.document.selection.createRange().pasteHTML(txt);
25865             } else {
25866                 // no firefox support
25867                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25868                 this.execCmd('InsertHTML', txt);
25869             } 
25870             
25871             this.syncValue();
25872             
25873             this.deferFocus();
25874         }
25875     },
25876  // private
25877     mozKeyPress : function(e){
25878         if(e.ctrlKey){
25879             var c = e.getCharCode(), cmd;
25880           
25881             if(c > 0){
25882                 c = String.fromCharCode(c).toLowerCase();
25883                 switch(c){
25884                     case 'b':
25885                         cmd = 'bold';
25886                         break;
25887                     case 'i':
25888                         cmd = 'italic';
25889                         break;
25890                     
25891                     case 'u':
25892                         cmd = 'underline';
25893                         break;
25894                     
25895                     case 'v':
25896                         this.cleanUpPaste.defer(100, this);
25897                         return;
25898                         
25899                 }
25900                 if(cmd){
25901                     this.win.focus();
25902                     this.execCmd(cmd);
25903                     this.deferFocus();
25904                     e.preventDefault();
25905                 }
25906                 
25907             }
25908         }
25909     },
25910
25911     // private
25912     fixKeys : function(){ // load time branching for fastest keydown performance
25913         if(Roo.isIE){
25914             return function(e){
25915                 var k = e.getKey(), r;
25916                 if(k == e.TAB){
25917                     e.stopEvent();
25918                     r = this.doc.selection.createRange();
25919                     if(r){
25920                         r.collapse(true);
25921                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25922                         this.deferFocus();
25923                     }
25924                     return;
25925                 }
25926                 
25927                 if(k == e.ENTER){
25928                     r = this.doc.selection.createRange();
25929                     if(r){
25930                         var target = r.parentElement();
25931                         if(!target || target.tagName.toLowerCase() != 'li'){
25932                             e.stopEvent();
25933                             r.pasteHTML('<br />');
25934                             r.collapse(false);
25935                             r.select();
25936                         }
25937                     }
25938                 }
25939                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25940                     this.cleanUpPaste.defer(100, this);
25941                     return;
25942                 }
25943                 
25944                 
25945             };
25946         }else if(Roo.isOpera){
25947             return function(e){
25948                 var k = e.getKey();
25949                 if(k == e.TAB){
25950                     e.stopEvent();
25951                     this.win.focus();
25952                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25953                     this.deferFocus();
25954                 }
25955                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25956                     this.cleanUpPaste.defer(100, this);
25957                     return;
25958                 }
25959                 
25960             };
25961         }else if(Roo.isSafari){
25962             return function(e){
25963                 var k = e.getKey();
25964                 
25965                 if(k == e.TAB){
25966                     e.stopEvent();
25967                     this.execCmd('InsertText','\t');
25968                     this.deferFocus();
25969                     return;
25970                 }
25971                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25972                     this.cleanUpPaste.defer(100, this);
25973                     return;
25974                 }
25975                 
25976              };
25977         }
25978     }(),
25979     
25980     getAllAncestors: function()
25981     {
25982         var p = this.getSelectedNode();
25983         var a = [];
25984         if (!p) {
25985             a.push(p); // push blank onto stack..
25986             p = this.getParentElement();
25987         }
25988         
25989         
25990         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25991             a.push(p);
25992             p = p.parentNode;
25993         }
25994         a.push(this.doc.body);
25995         return a;
25996     },
25997     lastSel : false,
25998     lastSelNode : false,
25999     
26000     
26001     getSelection : function() 
26002     {
26003         this.assignDocWin();
26004         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26005     },
26006     
26007     getSelectedNode: function() 
26008     {
26009         // this may only work on Gecko!!!
26010         
26011         // should we cache this!!!!
26012         
26013         
26014         
26015          
26016         var range = this.createRange(this.getSelection()).cloneRange();
26017         
26018         if (Roo.isIE) {
26019             var parent = range.parentElement();
26020             while (true) {
26021                 var testRange = range.duplicate();
26022                 testRange.moveToElementText(parent);
26023                 if (testRange.inRange(range)) {
26024                     break;
26025                 }
26026                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26027                     break;
26028                 }
26029                 parent = parent.parentElement;
26030             }
26031             return parent;
26032         }
26033         
26034         // is ancestor a text element.
26035         var ac =  range.commonAncestorContainer;
26036         if (ac.nodeType == 3) {
26037             ac = ac.parentNode;
26038         }
26039         
26040         var ar = ac.childNodes;
26041          
26042         var nodes = [];
26043         var other_nodes = [];
26044         var has_other_nodes = false;
26045         for (var i=0;i<ar.length;i++) {
26046             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26047                 continue;
26048             }
26049             // fullly contained node.
26050             
26051             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26052                 nodes.push(ar[i]);
26053                 continue;
26054             }
26055             
26056             // probably selected..
26057             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26058                 other_nodes.push(ar[i]);
26059                 continue;
26060             }
26061             // outer..
26062             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26063                 continue;
26064             }
26065             
26066             
26067             has_other_nodes = true;
26068         }
26069         if (!nodes.length && other_nodes.length) {
26070             nodes= other_nodes;
26071         }
26072         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26073             return false;
26074         }
26075         
26076         return nodes[0];
26077     },
26078     createRange: function(sel)
26079     {
26080         // this has strange effects when using with 
26081         // top toolbar - not sure if it's a great idea.
26082         //this.editor.contentWindow.focus();
26083         if (typeof sel != "undefined") {
26084             try {
26085                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26086             } catch(e) {
26087                 return this.doc.createRange();
26088             }
26089         } else {
26090             return this.doc.createRange();
26091         }
26092     },
26093     getParentElement: function()
26094     {
26095         
26096         this.assignDocWin();
26097         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26098         
26099         var range = this.createRange(sel);
26100          
26101         try {
26102             var p = range.commonAncestorContainer;
26103             while (p.nodeType == 3) { // text node
26104                 p = p.parentNode;
26105             }
26106             return p;
26107         } catch (e) {
26108             return null;
26109         }
26110     
26111     },
26112     /***
26113      *
26114      * Range intersection.. the hard stuff...
26115      *  '-1' = before
26116      *  '0' = hits..
26117      *  '1' = after.
26118      *         [ -- selected range --- ]
26119      *   [fail]                        [fail]
26120      *
26121      *    basically..
26122      *      if end is before start or  hits it. fail.
26123      *      if start is after end or hits it fail.
26124      *
26125      *   if either hits (but other is outside. - then it's not 
26126      *   
26127      *    
26128      **/
26129     
26130     
26131     // @see http://www.thismuchiknow.co.uk/?p=64.
26132     rangeIntersectsNode : function(range, node)
26133     {
26134         var nodeRange = node.ownerDocument.createRange();
26135         try {
26136             nodeRange.selectNode(node);
26137         } catch (e) {
26138             nodeRange.selectNodeContents(node);
26139         }
26140     
26141         var rangeStartRange = range.cloneRange();
26142         rangeStartRange.collapse(true);
26143     
26144         var rangeEndRange = range.cloneRange();
26145         rangeEndRange.collapse(false);
26146     
26147         var nodeStartRange = nodeRange.cloneRange();
26148         nodeStartRange.collapse(true);
26149     
26150         var nodeEndRange = nodeRange.cloneRange();
26151         nodeEndRange.collapse(false);
26152     
26153         return rangeStartRange.compareBoundaryPoints(
26154                  Range.START_TO_START, nodeEndRange) == -1 &&
26155                rangeEndRange.compareBoundaryPoints(
26156                  Range.START_TO_START, nodeStartRange) == 1;
26157         
26158          
26159     },
26160     rangeCompareNode : function(range, node)
26161     {
26162         var nodeRange = node.ownerDocument.createRange();
26163         try {
26164             nodeRange.selectNode(node);
26165         } catch (e) {
26166             nodeRange.selectNodeContents(node);
26167         }
26168         
26169         
26170         range.collapse(true);
26171     
26172         nodeRange.collapse(true);
26173      
26174         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26175         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26176          
26177         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26178         
26179         var nodeIsBefore   =  ss == 1;
26180         var nodeIsAfter    = ee == -1;
26181         
26182         if (nodeIsBefore && nodeIsAfter)
26183             return 0; // outer
26184         if (!nodeIsBefore && nodeIsAfter)
26185             return 1; //right trailed.
26186         
26187         if (nodeIsBefore && !nodeIsAfter)
26188             return 2;  // left trailed.
26189         // fully contined.
26190         return 3;
26191     },
26192
26193     // private? - in a new class?
26194     cleanUpPaste :  function()
26195     {
26196         // cleans up the whole document..
26197          Roo.log('cleanuppaste');
26198         this.cleanUpChildren(this.doc.body);
26199         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26200         if (clean != this.doc.body.innerHTML) {
26201             this.doc.body.innerHTML = clean;
26202         }
26203         
26204     },
26205     
26206     cleanWordChars : function(input) {// change the chars to hex code
26207         var he = Roo.form.HtmlEditor;
26208         
26209         var output = input;
26210         Roo.each(he.swapCodes, function(sw) { 
26211             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26212             
26213             output = output.replace(swapper, sw[1]);
26214         });
26215         
26216         return output;
26217     },
26218     
26219     
26220     cleanUpChildren : function (n)
26221     {
26222         if (!n.childNodes.length) {
26223             return;
26224         }
26225         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26226            this.cleanUpChild(n.childNodes[i]);
26227         }
26228     },
26229     
26230     
26231         
26232     
26233     cleanUpChild : function (node)
26234     {
26235         var ed = this;
26236         //console.log(node);
26237         if (node.nodeName == "#text") {
26238             // clean up silly Windows -- stuff?
26239             return; 
26240         }
26241         if (node.nodeName == "#comment") {
26242             node.parentNode.removeChild(node);
26243             // clean up silly Windows -- stuff?
26244             return; 
26245         }
26246         
26247         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26248             // remove node.
26249             node.parentNode.removeChild(node);
26250             return;
26251             
26252         }
26253         
26254         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26255         
26256         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26257         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26258         
26259         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26260         //    remove_keep_children = true;
26261         //}
26262         
26263         if (remove_keep_children) {
26264             this.cleanUpChildren(node);
26265             // inserts everything just before this node...
26266             while (node.childNodes.length) {
26267                 var cn = node.childNodes[0];
26268                 node.removeChild(cn);
26269                 node.parentNode.insertBefore(cn, node);
26270             }
26271             node.parentNode.removeChild(node);
26272             return;
26273         }
26274         
26275         if (!node.attributes || !node.attributes.length) {
26276             this.cleanUpChildren(node);
26277             return;
26278         }
26279         
26280         function cleanAttr(n,v)
26281         {
26282             
26283             if (v.match(/^\./) || v.match(/^\//)) {
26284                 return;
26285             }
26286             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26287                 return;
26288             }
26289             if (v.match(/^#/)) {
26290                 return;
26291             }
26292 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26293             node.removeAttribute(n);
26294             
26295         }
26296         
26297         function cleanStyle(n,v)
26298         {
26299             if (v.match(/expression/)) { //XSS?? should we even bother..
26300                 node.removeAttribute(n);
26301                 return;
26302             }
26303             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26304             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26305             
26306             var parts = v.split(/;/);
26307             var clean = [];
26308             
26309             Roo.each(parts, function(p) {
26310                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26311                 if (!p.length) {
26312                     return true;
26313                 }
26314                 var l = p.split(':').shift().replace(/\s+/g,'');
26315                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26316                 
26317                 // only allow 'c whitelisted system attributes'
26318                 if ( cwhite.indexOf(l) < 0) {
26319 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26320                     //node.removeAttribute(n);
26321                     return true;
26322                 }
26323                 
26324                 if ( cblack.indexOf(l) < 0) {
26325 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26326                     //node.removeAttribute(n);
26327                     return true;
26328                 }
26329                 
26330 //                if(l == 'font-size'){
26331 ////                    Roo.log('(REMOVE FONT SIZE)' + node.tagName +'.' + n + ':'+l + '=' + v);
26332 //                    return true;
26333 //                }
26334                 
26335                 clean.push(p);
26336                 return true;
26337             });
26338             if (clean.length) { 
26339                 node.setAttribute(n, clean.join(';'));
26340             } else {
26341                 node.removeAttribute(n);
26342             }
26343             
26344         }
26345         
26346         
26347         for (var i = node.attributes.length-1; i > -1 ; i--) {
26348             var a = node.attributes[i];
26349             //console.log(a);
26350             
26351             if (a.name.toLowerCase().substr(0,2)=='on')  {
26352                 node.removeAttribute(a.name);
26353                 continue;
26354             }
26355             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26356                 node.removeAttribute(a.name);
26357                 continue;
26358             }
26359             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26360                 cleanAttr(a.name,a.value); // fixme..
26361                 continue;
26362             }
26363             if (a.name == 'style') {
26364                 cleanStyle(a.name,a.value);
26365                 continue;
26366             }
26367             /// clean up MS crap..
26368             // tecnically this should be a list of valid class'es..
26369             
26370             
26371             if (a.name == 'class') {
26372                 if (a.value.match(/^Mso/)) {
26373                     node.className = '';
26374                 }
26375                 
26376                 if (a.value.match(/body/)) {
26377                     node.className = '';
26378                 }
26379                 continue;
26380             }
26381             
26382             // style cleanup!?
26383             // class cleanup?
26384             
26385         }
26386         
26387         
26388         this.cleanUpChildren(node);
26389         
26390         
26391     }
26392     
26393     
26394     // hide stuff that is not compatible
26395     /**
26396      * @event blur
26397      * @hide
26398      */
26399     /**
26400      * @event change
26401      * @hide
26402      */
26403     /**
26404      * @event focus
26405      * @hide
26406      */
26407     /**
26408      * @event specialkey
26409      * @hide
26410      */
26411     /**
26412      * @cfg {String} fieldClass @hide
26413      */
26414     /**
26415      * @cfg {String} focusClass @hide
26416      */
26417     /**
26418      * @cfg {String} autoCreate @hide
26419      */
26420     /**
26421      * @cfg {String} inputType @hide
26422      */
26423     /**
26424      * @cfg {String} invalidClass @hide
26425      */
26426     /**
26427      * @cfg {String} invalidText @hide
26428      */
26429     /**
26430      * @cfg {String} msgFx @hide
26431      */
26432     /**
26433      * @cfg {String} validateOnBlur @hide
26434      */
26435 });
26436
26437 Roo.form.HtmlEditor.white = [
26438         'area', 'br', 'img', 'input', 'hr', 'wbr',
26439         
26440        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26441        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26442        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26443        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26444        'table',   'ul',         'xmp', 
26445        
26446        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26447       'thead',   'tr', 
26448      
26449       'dir', 'menu', 'ol', 'ul', 'dl',
26450        
26451       'embed',  'object'
26452 ];
26453
26454
26455 Roo.form.HtmlEditor.black = [
26456     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26457         'applet', // 
26458         'base',   'basefont', 'bgsound', 'blink',  'body', 
26459         'frame',  'frameset', 'head',    'html',   'ilayer', 
26460         'iframe', 'layer',  'link',     'meta',    'object',   
26461         'script', 'style' ,'title',  'xml' // clean later..
26462 ];
26463 Roo.form.HtmlEditor.clean = [
26464     'script', 'style', 'title', 'xml'
26465 ];
26466 Roo.form.HtmlEditor.remove = [
26467     'font'
26468 ];
26469 // attributes..
26470
26471 Roo.form.HtmlEditor.ablack = [
26472     'on'
26473 ];
26474     
26475 Roo.form.HtmlEditor.aclean = [ 
26476     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26477 ];
26478
26479 // protocols..
26480 Roo.form.HtmlEditor.pwhite= [
26481         'http',  'https',  'mailto'
26482 ];
26483
26484 // white listed style attributes.
26485 Roo.form.HtmlEditor.cwhite= [
26486         'text-align'
26487 //        'font-size'//??
26488 ];
26489
26490 // black listed style attributes.
26491 Roo.form.HtmlEditor.cblack= [
26492       //  'font-size' -- this can be set by the project 
26493 ];
26494
26495
26496 Roo.form.HtmlEditor.swapCodes   =[ 
26497     [    8211, "--" ], 
26498     [    8212, "--" ], 
26499     [    8216,  "'" ],  
26500     [    8217, "'" ],  
26501     [    8220, '"' ],  
26502     [    8221, '"' ],  
26503     [    8226, "*" ],  
26504     [    8230, "..." ]
26505 ]; 
26506
26507     // <script type="text/javascript">
26508 /*
26509  * Based on
26510  * Ext JS Library 1.1.1
26511  * Copyright(c) 2006-2007, Ext JS, LLC.
26512  *  
26513  
26514  */
26515
26516 /**
26517  * @class Roo.form.HtmlEditorToolbar1
26518  * Basic Toolbar
26519  * 
26520  * Usage:
26521  *
26522  new Roo.form.HtmlEditor({
26523     ....
26524     toolbars : [
26525         new Roo.form.HtmlEditorToolbar1({
26526             disable : { fonts: 1 , format: 1, ..., ... , ...],
26527             btns : [ .... ]
26528         })
26529     }
26530      
26531  * 
26532  * @cfg {Object} disable List of elements to disable..
26533  * @cfg {Array} btns List of additional buttons.
26534  * 
26535  * 
26536  * NEEDS Extra CSS? 
26537  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26538  */
26539  
26540 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26541 {
26542     
26543     Roo.apply(this, config);
26544     
26545     // default disabled, based on 'good practice'..
26546     this.disable = this.disable || {};
26547     Roo.applyIf(this.disable, {
26548         fontSize : true,
26549         colors : true,
26550         specialElements : true
26551     });
26552     
26553     
26554     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26555     // dont call parent... till later.
26556 }
26557
26558 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26559     
26560     tb: false,
26561     
26562     rendered: false,
26563     
26564     editor : false,
26565     /**
26566      * @cfg {Object} disable  List of toolbar elements to disable
26567          
26568      */
26569     disable : false,
26570       /**
26571      * @cfg {Array} fontFamilies An array of available font families
26572      */
26573     fontFamilies : [
26574         'Arial',
26575         'Courier New',
26576         'Tahoma',
26577         'Times New Roman',
26578         'Verdana'
26579     ],
26580     
26581     specialChars : [
26582            "&#169;",
26583           "&#174;",     
26584           "&#8482;",    
26585           "&#163;" ,    
26586          // "&#8212;",    
26587           "&#8230;",    
26588           "&#247;" ,    
26589         //  "&#225;" ,     ?? a acute?
26590            "&#8364;"    , //Euro
26591        //   "&#8220;"    ,
26592         //  "&#8221;"    ,
26593         //  "&#8226;"    ,
26594           "&#176;"  //   , // degrees
26595
26596          // "&#233;"     , // e ecute
26597          // "&#250;"     , // u ecute?
26598     ],
26599     
26600     specialElements : [
26601         {
26602             text: "Insert Table",
26603             xtype: 'MenuItem',
26604             xns : Roo.Menu,
26605             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26606                 
26607         },
26608         {    
26609             text: "Insert Image",
26610             xtype: 'MenuItem',
26611             xns : Roo.Menu,
26612             ihtml : '<img src="about:blank"/>'
26613             
26614         }
26615         
26616          
26617     ],
26618     
26619     
26620     inputElements : [ 
26621             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26622             "input:submit", "input:button", "select", "textarea", "label" ],
26623     formats : [
26624         ["p"] ,  
26625         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26626         ["pre"],[ "code"], 
26627         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26628     ],
26629      /**
26630      * @cfg {String} defaultFont default font to use.
26631      */
26632     defaultFont: 'tahoma',
26633    
26634     fontSelect : false,
26635     
26636     
26637     formatCombo : false,
26638     
26639     init : function(editor)
26640     {
26641         this.editor = editor;
26642         
26643         
26644         var fid = editor.frameId;
26645         var etb = this;
26646         function btn(id, toggle, handler){
26647             var xid = fid + '-'+ id ;
26648             return {
26649                 id : xid,
26650                 cmd : id,
26651                 cls : 'x-btn-icon x-edit-'+id,
26652                 enableToggle:toggle !== false,
26653                 scope: editor, // was editor...
26654                 handler:handler||editor.relayBtnCmd,
26655                 clickEvent:'mousedown',
26656                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26657                 tabIndex:-1
26658             };
26659         }
26660         
26661         
26662         
26663         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26664         this.tb = tb;
26665          // stop form submits
26666         tb.el.on('click', function(e){
26667             e.preventDefault(); // what does this do?
26668         });
26669
26670         if(!this.disable.font && !Roo.isSafari){
26671             /* why no safari for fonts
26672             editor.fontSelect = tb.el.createChild({
26673                 tag:'select',
26674                 tabIndex: -1,
26675                 cls:'x-font-select',
26676                 html: editor.createFontOptions()
26677             });
26678             editor.fontSelect.on('change', function(){
26679                 var font = editor.fontSelect.dom.value;
26680                 editor.relayCmd('fontname', font);
26681                 editor.deferFocus();
26682             }, editor);
26683             tb.add(
26684                 editor.fontSelect.dom,
26685                 '-'
26686             );
26687             */
26688         };
26689         if(!this.disable.formats){
26690             this.formatCombo = new Roo.form.ComboBox({
26691                 store: new Roo.data.SimpleStore({
26692                     id : 'tag',
26693                     fields: ['tag'],
26694                     data : this.formats // from states.js
26695                 }),
26696                 blockFocus : true,
26697                 name : '',
26698                 //autoCreate : {tag: "div",  size: "20"},
26699                 displayField:'tag',
26700                 typeAhead: false,
26701                 mode: 'local',
26702                 editable : false,
26703                 triggerAction: 'all',
26704                 emptyText:'Add tag',
26705                 selectOnFocus:true,
26706                 width:135,
26707                 listeners : {
26708                     'select': function(c, r, i) {
26709                         editor.insertTag(r.get('tag'));
26710                         editor.focus();
26711                     }
26712                 }
26713
26714             });
26715             tb.addField(this.formatCombo);
26716             
26717         }
26718         
26719         if(!this.disable.format){
26720             tb.add(
26721                 btn('bold'),
26722                 btn('italic'),
26723                 btn('underline')
26724             );
26725         };
26726         if(!this.disable.fontSize){
26727             tb.add(
26728                 '-',
26729                 
26730                 
26731                 btn('increasefontsize', false, editor.adjustFont),
26732                 btn('decreasefontsize', false, editor.adjustFont)
26733             );
26734         };
26735         
26736         
26737         if(!this.disable.colors){
26738             tb.add(
26739                 '-', {
26740                     id:editor.frameId +'-forecolor',
26741                     cls:'x-btn-icon x-edit-forecolor',
26742                     clickEvent:'mousedown',
26743                     tooltip: this.buttonTips['forecolor'] || undefined,
26744                     tabIndex:-1,
26745                     menu : new Roo.menu.ColorMenu({
26746                         allowReselect: true,
26747                         focus: Roo.emptyFn,
26748                         value:'000000',
26749                         plain:true,
26750                         selectHandler: function(cp, color){
26751                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26752                             editor.deferFocus();
26753                         },
26754                         scope: editor,
26755                         clickEvent:'mousedown'
26756                     })
26757                 }, {
26758                     id:editor.frameId +'backcolor',
26759                     cls:'x-btn-icon x-edit-backcolor',
26760                     clickEvent:'mousedown',
26761                     tooltip: this.buttonTips['backcolor'] || undefined,
26762                     tabIndex:-1,
26763                     menu : new Roo.menu.ColorMenu({
26764                         focus: Roo.emptyFn,
26765                         value:'FFFFFF',
26766                         plain:true,
26767                         allowReselect: true,
26768                         selectHandler: function(cp, color){
26769                             if(Roo.isGecko){
26770                                 editor.execCmd('useCSS', false);
26771                                 editor.execCmd('hilitecolor', color);
26772                                 editor.execCmd('useCSS', true);
26773                                 editor.deferFocus();
26774                             }else{
26775                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26776                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26777                                 editor.deferFocus();
26778                             }
26779                         },
26780                         scope:editor,
26781                         clickEvent:'mousedown'
26782                     })
26783                 }
26784             );
26785         };
26786         // now add all the items...
26787         
26788
26789         if(!this.disable.alignments){
26790             tb.add(
26791                 '-',
26792                 btn('justifyleft'),
26793                 btn('justifycenter'),
26794                 btn('justifyright')
26795             );
26796         };
26797
26798         //if(!Roo.isSafari){
26799             if(!this.disable.links){
26800                 tb.add(
26801                     '-',
26802                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26803                 );
26804             };
26805
26806             if(!this.disable.lists){
26807                 tb.add(
26808                     '-',
26809                     btn('insertorderedlist'),
26810                     btn('insertunorderedlist')
26811                 );
26812             }
26813             if(!this.disable.sourceEdit){
26814                 tb.add(
26815                     '-',
26816                     btn('sourceedit', true, function(btn){
26817                         this.toggleSourceEdit(btn.pressed);
26818                     })
26819                 );
26820             }
26821         //}
26822         
26823         var smenu = { };
26824         // special menu.. - needs to be tidied up..
26825         if (!this.disable.special) {
26826             smenu = {
26827                 text: "&#169;",
26828                 cls: 'x-edit-none',
26829                 
26830                 menu : {
26831                     items : []
26832                 }
26833             };
26834             for (var i =0; i < this.specialChars.length; i++) {
26835                 smenu.menu.items.push({
26836                     
26837                     html: this.specialChars[i],
26838                     handler: function(a,b) {
26839                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26840                         //editor.insertAtCursor(a.html);
26841                         
26842                     },
26843                     tabIndex:-1
26844                 });
26845             }
26846             
26847             
26848             tb.add(smenu);
26849             
26850             
26851         }
26852          
26853         if (!this.disable.specialElements) {
26854             var semenu = {
26855                 text: "Other;",
26856                 cls: 'x-edit-none',
26857                 menu : {
26858                     items : []
26859                 }
26860             };
26861             for (var i =0; i < this.specialElements.length; i++) {
26862                 semenu.menu.items.push(
26863                     Roo.apply({ 
26864                         handler: function(a,b) {
26865                             editor.insertAtCursor(this.ihtml);
26866                         }
26867                     }, this.specialElements[i])
26868                 );
26869                     
26870             }
26871             
26872             tb.add(semenu);
26873             
26874             
26875         }
26876          
26877         
26878         if (this.btns) {
26879             for(var i =0; i< this.btns.length;i++) {
26880                 var b = Roo.factory(this.btns[i],Roo.form);
26881                 b.cls =  'x-edit-none';
26882                 b.scope = editor;
26883                 tb.add(b);
26884             }
26885         
26886         }
26887         
26888         
26889         
26890         // disable everything...
26891         
26892         this.tb.items.each(function(item){
26893            if(item.id != editor.frameId+ '-sourceedit'){
26894                 item.disable();
26895             }
26896         });
26897         this.rendered = true;
26898         
26899         // the all the btns;
26900         editor.on('editorevent', this.updateToolbar, this);
26901         // other toolbars need to implement this..
26902         //editor.on('editmodechange', this.updateToolbar, this);
26903     },
26904     
26905     
26906     
26907     /**
26908      * Protected method that will not generally be called directly. It triggers
26909      * a toolbar update by reading the markup state of the current selection in the editor.
26910      */
26911     updateToolbar: function(){
26912
26913         if(!this.editor.activated){
26914             this.editor.onFirstFocus();
26915             return;
26916         }
26917
26918         var btns = this.tb.items.map, 
26919             doc = this.editor.doc,
26920             frameId = this.editor.frameId;
26921
26922         if(!this.disable.font && !Roo.isSafari){
26923             /*
26924             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26925             if(name != this.fontSelect.dom.value){
26926                 this.fontSelect.dom.value = name;
26927             }
26928             */
26929         }
26930         if(!this.disable.format){
26931             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26932             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26933             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26934         }
26935         if(!this.disable.alignments){
26936             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26937             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26938             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26939         }
26940         if(!Roo.isSafari && !this.disable.lists){
26941             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26942             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26943         }
26944         
26945         var ans = this.editor.getAllAncestors();
26946         if (this.formatCombo) {
26947             
26948             
26949             var store = this.formatCombo.store;
26950             this.formatCombo.setValue("");
26951             for (var i =0; i < ans.length;i++) {
26952                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26953                     // select it..
26954                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26955                     break;
26956                 }
26957             }
26958         }
26959         
26960         
26961         
26962         // hides menus... - so this cant be on a menu...
26963         Roo.menu.MenuMgr.hideAll();
26964
26965         //this.editorsyncValue();
26966     },
26967    
26968     
26969     createFontOptions : function(){
26970         var buf = [], fs = this.fontFamilies, ff, lc;
26971         for(var i = 0, len = fs.length; i< len; i++){
26972             ff = fs[i];
26973             lc = ff.toLowerCase();
26974             buf.push(
26975                 '<option value="',lc,'" style="font-family:',ff,';"',
26976                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26977                     ff,
26978                 '</option>'
26979             );
26980         }
26981         return buf.join('');
26982     },
26983     
26984     toggleSourceEdit : function(sourceEditMode){
26985         if(sourceEditMode === undefined){
26986             sourceEditMode = !this.sourceEditMode;
26987         }
26988         this.sourceEditMode = sourceEditMode === true;
26989         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26990         // just toggle the button?
26991         if(btn.pressed !== this.editor.sourceEditMode){
26992             btn.toggle(this.editor.sourceEditMode);
26993             return;
26994         }
26995         
26996         if(this.sourceEditMode){
26997             this.tb.items.each(function(item){
26998                 if(item.cmd != 'sourceedit'){
26999                     item.disable();
27000                 }
27001             });
27002           
27003         }else{
27004             if(this.initialized){
27005                 this.tb.items.each(function(item){
27006                     item.enable();
27007                 });
27008             }
27009             
27010         }
27011         // tell the editor that it's been pressed..
27012         this.editor.toggleSourceEdit(sourceEditMode);
27013        
27014     },
27015      /**
27016      * Object collection of toolbar tooltips for the buttons in the editor. The key
27017      * is the command id associated with that button and the value is a valid QuickTips object.
27018      * For example:
27019 <pre><code>
27020 {
27021     bold : {
27022         title: 'Bold (Ctrl+B)',
27023         text: 'Make the selected text bold.',
27024         cls: 'x-html-editor-tip'
27025     },
27026     italic : {
27027         title: 'Italic (Ctrl+I)',
27028         text: 'Make the selected text italic.',
27029         cls: 'x-html-editor-tip'
27030     },
27031     ...
27032 </code></pre>
27033     * @type Object
27034      */
27035     buttonTips : {
27036         bold : {
27037             title: 'Bold (Ctrl+B)',
27038             text: 'Make the selected text bold.',
27039             cls: 'x-html-editor-tip'
27040         },
27041         italic : {
27042             title: 'Italic (Ctrl+I)',
27043             text: 'Make the selected text italic.',
27044             cls: 'x-html-editor-tip'
27045         },
27046         underline : {
27047             title: 'Underline (Ctrl+U)',
27048             text: 'Underline the selected text.',
27049             cls: 'x-html-editor-tip'
27050         },
27051         increasefontsize : {
27052             title: 'Grow Text',
27053             text: 'Increase the font size.',
27054             cls: 'x-html-editor-tip'
27055         },
27056         decreasefontsize : {
27057             title: 'Shrink Text',
27058             text: 'Decrease the font size.',
27059             cls: 'x-html-editor-tip'
27060         },
27061         backcolor : {
27062             title: 'Text Highlight Color',
27063             text: 'Change the background color of the selected text.',
27064             cls: 'x-html-editor-tip'
27065         },
27066         forecolor : {
27067             title: 'Font Color',
27068             text: 'Change the color of the selected text.',
27069             cls: 'x-html-editor-tip'
27070         },
27071         justifyleft : {
27072             title: 'Align Text Left',
27073             text: 'Align text to the left.',
27074             cls: 'x-html-editor-tip'
27075         },
27076         justifycenter : {
27077             title: 'Center Text',
27078             text: 'Center text in the editor.',
27079             cls: 'x-html-editor-tip'
27080         },
27081         justifyright : {
27082             title: 'Align Text Right',
27083             text: 'Align text to the right.',
27084             cls: 'x-html-editor-tip'
27085         },
27086         insertunorderedlist : {
27087             title: 'Bullet List',
27088             text: 'Start a bulleted list.',
27089             cls: 'x-html-editor-tip'
27090         },
27091         insertorderedlist : {
27092             title: 'Numbered List',
27093             text: 'Start a numbered list.',
27094             cls: 'x-html-editor-tip'
27095         },
27096         createlink : {
27097             title: 'Hyperlink',
27098             text: 'Make the selected text a hyperlink.',
27099             cls: 'x-html-editor-tip'
27100         },
27101         sourceedit : {
27102             title: 'Source Edit',
27103             text: 'Switch to source editing mode.',
27104             cls: 'x-html-editor-tip'
27105         }
27106     },
27107     // private
27108     onDestroy : function(){
27109         if(this.rendered){
27110             
27111             this.tb.items.each(function(item){
27112                 if(item.menu){
27113                     item.menu.removeAll();
27114                     if(item.menu.el){
27115                         item.menu.el.destroy();
27116                     }
27117                 }
27118                 item.destroy();
27119             });
27120              
27121         }
27122     },
27123     onFirstFocus: function() {
27124         this.tb.items.each(function(item){
27125            item.enable();
27126         });
27127     }
27128 });
27129
27130
27131
27132
27133 // <script type="text/javascript">
27134 /*
27135  * Based on
27136  * Ext JS Library 1.1.1
27137  * Copyright(c) 2006-2007, Ext JS, LLC.
27138  *  
27139  
27140  */
27141
27142  
27143 /**
27144  * @class Roo.form.HtmlEditor.ToolbarContext
27145  * Context Toolbar
27146  * 
27147  * Usage:
27148  *
27149  new Roo.form.HtmlEditor({
27150     ....
27151     toolbars : [
27152         { xtype: 'ToolbarStandard', styles : {} }
27153         { xtype: 'ToolbarContext', disable : {} }
27154     ]
27155 })
27156
27157      
27158  * 
27159  * @config : {Object} disable List of elements to disable.. (not done yet.)
27160  * @config : {Object} styles  Map of styles available.
27161  * 
27162  */
27163
27164 Roo.form.HtmlEditor.ToolbarContext = function(config)
27165 {
27166     
27167     Roo.apply(this, config);
27168     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27169     // dont call parent... till later.
27170     this.styles = this.styles || {};
27171 }
27172 Roo.form.HtmlEditor.ToolbarContext.types = {
27173     'IMG' : {
27174         width : {
27175             title: "Width",
27176             width: 40
27177         },
27178         height:  {
27179             title: "Height",
27180             width: 40
27181         },
27182         align: {
27183             title: "Align",
27184             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27185             width : 80
27186             
27187         },
27188         border: {
27189             title: "Border",
27190             width: 40
27191         },
27192         alt: {
27193             title: "Alt",
27194             width: 120
27195         },
27196         src : {
27197             title: "Src",
27198             width: 220
27199         }
27200         
27201     },
27202     'A' : {
27203         name : {
27204             title: "Name",
27205             width: 50
27206         },
27207         href:  {
27208             title: "Href",
27209             width: 220
27210         } // border?
27211         
27212     },
27213     'TABLE' : {
27214         rows : {
27215             title: "Rows",
27216             width: 20
27217         },
27218         cols : {
27219             title: "Cols",
27220             width: 20
27221         },
27222         width : {
27223             title: "Width",
27224             width: 40
27225         },
27226         height : {
27227             title: "Height",
27228             width: 40
27229         },
27230         border : {
27231             title: "Border",
27232             width: 20
27233         }
27234     },
27235     'TD' : {
27236         width : {
27237             title: "Width",
27238             width: 40
27239         },
27240         height : {
27241             title: "Height",
27242             width: 40
27243         },   
27244         align: {
27245             title: "Align",
27246             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27247             width: 80
27248         },
27249         valign: {
27250             title: "Valign",
27251             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27252             width: 80
27253         },
27254         colspan: {
27255             title: "Colspan",
27256             width: 20
27257             
27258         }
27259     },
27260     'INPUT' : {
27261         name : {
27262             title: "name",
27263             width: 120
27264         },
27265         value : {
27266             title: "Value",
27267             width: 120
27268         },
27269         width : {
27270             title: "Width",
27271             width: 40
27272         }
27273     },
27274     'LABEL' : {
27275         'for' : {
27276             title: "For",
27277             width: 120
27278         }
27279     },
27280     'TEXTAREA' : {
27281           name : {
27282             title: "name",
27283             width: 120
27284         },
27285         rows : {
27286             title: "Rows",
27287             width: 20
27288         },
27289         cols : {
27290             title: "Cols",
27291             width: 20
27292         }
27293     },
27294     'SELECT' : {
27295         name : {
27296             title: "name",
27297             width: 120
27298         },
27299         selectoptions : {
27300             title: "Options",
27301             width: 200
27302         }
27303     },
27304     
27305     // should we really allow this??
27306     // should this just be 
27307     'BODY' : {
27308         title : {
27309             title: "title",
27310             width: 200,
27311             disabled : true
27312         }
27313     },
27314     '*' : {
27315         // empty..
27316     }
27317 };
27318
27319
27320
27321 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27322     
27323     tb: false,
27324     
27325     rendered: false,
27326     
27327     editor : false,
27328     /**
27329      * @cfg {Object} disable  List of toolbar elements to disable
27330          
27331      */
27332     disable : false,
27333     /**
27334      * @cfg {Object} styles List of styles 
27335      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27336      *
27337      * These must be defined in the page, so they get rendered correctly..
27338      * .headline { }
27339      * TD.underline { }
27340      * 
27341      */
27342     styles : false,
27343     
27344     
27345     
27346     toolbars : false,
27347     
27348     init : function(editor)
27349     {
27350         this.editor = editor;
27351         
27352         
27353         var fid = editor.frameId;
27354         var etb = this;
27355         function btn(id, toggle, handler){
27356             var xid = fid + '-'+ id ;
27357             return {
27358                 id : xid,
27359                 cmd : id,
27360                 cls : 'x-btn-icon x-edit-'+id,
27361                 enableToggle:toggle !== false,
27362                 scope: editor, // was editor...
27363                 handler:handler||editor.relayBtnCmd,
27364                 clickEvent:'mousedown',
27365                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27366                 tabIndex:-1
27367             };
27368         }
27369         // create a new element.
27370         var wdiv = editor.wrap.createChild({
27371                 tag: 'div'
27372             }, editor.wrap.dom.firstChild.nextSibling, true);
27373         
27374         // can we do this more than once??
27375         
27376          // stop form submits
27377       
27378  
27379         // disable everything...
27380         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27381         this.toolbars = {};
27382            
27383         for (var i in  ty) {
27384           
27385             this.toolbars[i] = this.buildToolbar(ty[i],i);
27386         }
27387         this.tb = this.toolbars.BODY;
27388         this.tb.el.show();
27389         this.buildFooter();
27390         this.footer.show();
27391         editor.on('hide', function( ) { this.footer.hide() }, this);
27392         editor.on('show', function( ) { this.footer.show() }, this);
27393         
27394          
27395         this.rendered = true;
27396         
27397         // the all the btns;
27398         editor.on('editorevent', this.updateToolbar, this);
27399         // other toolbars need to implement this..
27400         //editor.on('editmodechange', this.updateToolbar, this);
27401     },
27402     
27403     
27404     
27405     /**
27406      * Protected method that will not generally be called directly. It triggers
27407      * a toolbar update by reading the markup state of the current selection in the editor.
27408      */
27409     updateToolbar: function(editor,ev,sel){
27410
27411         //Roo.log(ev);
27412         // capture mouse up - this is handy for selecting images..
27413         // perhaps should go somewhere else...
27414         if(!this.editor.activated){
27415              this.editor.onFirstFocus();
27416             return;
27417         }
27418         
27419         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27420         // selectNode - might want to handle IE?
27421         if (ev &&
27422             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27423             ev.target && ev.target.tagName == 'IMG') {
27424             // they have click on an image...
27425             // let's see if we can change the selection...
27426             sel = ev.target;
27427          
27428               var nodeRange = sel.ownerDocument.createRange();
27429             try {
27430                 nodeRange.selectNode(sel);
27431             } catch (e) {
27432                 nodeRange.selectNodeContents(sel);
27433             }
27434             //nodeRange.collapse(true);
27435             var s = editor.win.getSelection();
27436             s.removeAllRanges();
27437             s.addRange(nodeRange);
27438         }  
27439         
27440       
27441         var updateFooter = sel ? false : true;
27442         
27443         
27444         var ans = this.editor.getAllAncestors();
27445         
27446         // pick
27447         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27448         
27449         if (!sel) { 
27450             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27451             sel = sel ? sel : this.editor.doc.body;
27452             sel = sel.tagName.length ? sel : this.editor.doc.body;
27453             
27454         }
27455         // pick a menu that exists..
27456         var tn = sel.tagName.toUpperCase();
27457         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27458         
27459         tn = sel.tagName.toUpperCase();
27460         
27461         var lastSel = this.tb.selectedNode
27462         
27463         this.tb.selectedNode = sel;
27464         
27465         // if current menu does not match..
27466         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27467                 
27468             this.tb.el.hide();
27469             ///console.log("show: " + tn);
27470             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27471             this.tb.el.show();
27472             // update name
27473             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27474             
27475             
27476             // update attributes
27477             if (this.tb.fields) {
27478                 this.tb.fields.each(function(e) {
27479                    e.setValue(sel.getAttribute(e.attrname));
27480                 });
27481             }
27482             
27483             var hasStyles = false;
27484             for(var i in this.styles) {
27485                 hasStyles = true;
27486                 break;
27487             }
27488             
27489             // update styles
27490             if (hasStyles) { 
27491                 var st = this.tb.fields.item(0);
27492                 
27493                 st.store.removeAll();
27494                
27495                 
27496                 var cn = sel.className.split(/\s+/);
27497                 
27498                 var avs = [];
27499                 if (this.styles['*']) {
27500                     
27501                     Roo.each(this.styles['*'], function(v) {
27502                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27503                     });
27504                 }
27505                 if (this.styles[tn]) { 
27506                     Roo.each(this.styles[tn], function(v) {
27507                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27508                     });
27509                 }
27510                 
27511                 st.store.loadData(avs);
27512                 st.collapse();
27513                 st.setValue(cn);
27514             }
27515             // flag our selected Node.
27516             this.tb.selectedNode = sel;
27517            
27518            
27519             Roo.menu.MenuMgr.hideAll();
27520
27521         }
27522         
27523         if (!updateFooter) {
27524             //this.footDisp.dom.innerHTML = ''; 
27525             return;
27526         }
27527         // update the footer
27528         //
27529         var html = '';
27530         
27531         this.footerEls = ans.reverse();
27532         Roo.each(this.footerEls, function(a,i) {
27533             if (!a) { return; }
27534             html += html.length ? ' &gt; '  :  '';
27535             
27536             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27537             
27538         });
27539        
27540         // 
27541         var sz = this.footDisp.up('td').getSize();
27542         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27543         this.footDisp.dom.style.marginLeft = '5px';
27544         
27545         this.footDisp.dom.style.overflow = 'hidden';
27546         
27547         this.footDisp.dom.innerHTML = html;
27548             
27549         //this.editorsyncValue();
27550     },
27551      
27552     
27553    
27554        
27555     // private
27556     onDestroy : function(){
27557         if(this.rendered){
27558             
27559             this.tb.items.each(function(item){
27560                 if(item.menu){
27561                     item.menu.removeAll();
27562                     if(item.menu.el){
27563                         item.menu.el.destroy();
27564                     }
27565                 }
27566                 item.destroy();
27567             });
27568              
27569         }
27570     },
27571     onFirstFocus: function() {
27572         // need to do this for all the toolbars..
27573         this.tb.items.each(function(item){
27574            item.enable();
27575         });
27576     },
27577     buildToolbar: function(tlist, nm)
27578     {
27579         var editor = this.editor;
27580          // create a new element.
27581         var wdiv = editor.wrap.createChild({
27582                 tag: 'div'
27583             }, editor.wrap.dom.firstChild.nextSibling, true);
27584         
27585        
27586         var tb = new Roo.Toolbar(wdiv);
27587         // add the name..
27588         
27589         tb.add(nm+ ":&nbsp;");
27590         
27591         var styles = [];
27592         for(var i in this.styles) {
27593             styles.push(i);
27594         }
27595         
27596         // styles...
27597         if (styles && styles.length) {
27598             
27599             // this needs a multi-select checkbox...
27600             tb.addField( new Roo.form.ComboBox({
27601                 store: new Roo.data.SimpleStore({
27602                     id : 'val',
27603                     fields: ['val', 'selected'],
27604                     data : [] 
27605                 }),
27606                 name : '-roo-edit-className',
27607                 attrname : 'className',
27608                 displayField:'val',
27609                 typeAhead: false,
27610                 mode: 'local',
27611                 editable : false,
27612                 triggerAction: 'all',
27613                 emptyText:'Select Style',
27614                 selectOnFocus:true,
27615                 width: 130,
27616                 listeners : {
27617                     'select': function(c, r, i) {
27618                         // initial support only for on class per el..
27619                         tb.selectedNode.className =  r ? r.get('val') : '';
27620                         editor.syncValue();
27621                     }
27622                 }
27623     
27624             }));
27625         }
27626             
27627         
27628         
27629         for (var i in tlist) {
27630             
27631             var item = tlist[i];
27632             tb.add(item.title + ":&nbsp;");
27633             
27634             
27635             
27636             
27637             if (item.opts) {
27638                 // opts == pulldown..
27639                 tb.addField( new Roo.form.ComboBox({
27640                     store: new Roo.data.SimpleStore({
27641                         id : 'val',
27642                         fields: ['val'],
27643                         data : item.opts  
27644                     }),
27645                     name : '-roo-edit-' + i,
27646                     attrname : i,
27647                     displayField:'val',
27648                     typeAhead: false,
27649                     mode: 'local',
27650                     editable : false,
27651                     triggerAction: 'all',
27652                     emptyText:'Select',
27653                     selectOnFocus:true,
27654                     width: item.width ? item.width  : 130,
27655                     listeners : {
27656                         'select': function(c, r, i) {
27657                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27658                         }
27659                     }
27660
27661                 }));
27662                 continue;
27663                     
27664                  
27665                 
27666                 tb.addField( new Roo.form.TextField({
27667                     name: i,
27668                     width: 100,
27669                     //allowBlank:false,
27670                     value: ''
27671                 }));
27672                 continue;
27673             }
27674             tb.addField( new Roo.form.TextField({
27675                 name: '-roo-edit-' + i,
27676                 attrname : i,
27677                 
27678                 width: item.width,
27679                 //allowBlank:true,
27680                 value: '',
27681                 listeners: {
27682                     'change' : function(f, nv, ov) {
27683                         tb.selectedNode.setAttribute(f.attrname, nv);
27684                     }
27685                 }
27686             }));
27687              
27688         }
27689         tb.addFill();
27690         var _this = this;
27691         tb.addButton( {
27692             text: 'Remove Tag',
27693     
27694             listeners : {
27695                 click : function ()
27696                 {
27697                     // remove
27698                     // undo does not work.
27699                      
27700                     var sn = tb.selectedNode;
27701                     Roo.log(sn);
27702                     var pn = sn.parentNode;
27703                     
27704                     var stn =  sn.childNodes[0];
27705                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27706                     while (sn.childNodes.length) {
27707                         var node = sn.childNodes[0];
27708                         sn.removeChild(node);
27709                         Roo.log(node);
27710                         pn.insertBefore(node, sn);
27711                         
27712                     }
27713                     pn.removeChild(sn);
27714                     var range = editor.createRange();
27715         
27716                     range.setStart(stn,0);
27717                     range.setEnd(en,0); //????
27718                     //range.selectNode(sel);
27719                     
27720                     
27721                     var selection = editor.getSelection();
27722                     selection.removeAllRanges();
27723                     selection.addRange(range);
27724                     
27725                     
27726                     
27727                     //_this.updateToolbar(null, null, pn);
27728                     _this.updateToolbar(null, null, null);
27729                     this.footDisp.dom.innerHTML = ''; 
27730                 }
27731             }
27732             
27733                     
27734                 
27735             
27736         });
27737         
27738         
27739         tb.el.on('click', function(e){
27740             e.preventDefault(); // what does this do?
27741         });
27742         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27743         tb.el.hide();
27744         tb.name = nm;
27745         // dont need to disable them... as they will get hidden
27746         return tb;
27747          
27748         
27749     },
27750     buildFooter : function()
27751     {
27752         
27753         var fel = this.editor.wrap.createChild();
27754         this.footer = new Roo.Toolbar(fel);
27755         // toolbar has scrolly on left / right?
27756         var footDisp= new Roo.Toolbar.Fill();
27757         var _t = this;
27758         this.footer.add(
27759             {
27760                 text : '&lt;',
27761                 xtype: 'Button',
27762                 handler : function() {
27763                     _t.footDisp.scrollTo('left',0,true)
27764                 }
27765             }
27766         );
27767         this.footer.add( footDisp );
27768         this.footer.add( 
27769             {
27770                 text : '&gt;',
27771                 xtype: 'Button',
27772                 handler : function() {
27773                     // no animation..
27774                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27775                 }
27776             }
27777         );
27778         var fel = Roo.get(footDisp.el);
27779         fel.addClass('x-editor-context');
27780         this.footDispWrap = fel; 
27781         this.footDispWrap.overflow  = 'hidden';
27782         
27783         this.footDisp = fel.createChild();
27784         this.footDispWrap.on('click', this.onContextClick, this)
27785         
27786         
27787     },
27788     onContextClick : function (ev,dom)
27789     {
27790         ev.preventDefault();
27791         var  cn = dom.className;
27792         Roo.log(cn);
27793         if (!cn.match(/x-ed-loc-/)) {
27794             return;
27795         }
27796         var n = cn.split('-').pop();
27797         var ans = this.footerEls;
27798         var sel = ans[n];
27799         
27800          // pick
27801         var range = this.editor.createRange();
27802         
27803         range.selectNodeContents(sel);
27804         //range.selectNode(sel);
27805         
27806         
27807         var selection = this.editor.getSelection();
27808         selection.removeAllRanges();
27809         selection.addRange(range);
27810         
27811         
27812         
27813         this.updateToolbar(null, null, sel);
27814         
27815         
27816     }
27817     
27818     
27819     
27820     
27821     
27822 });
27823
27824
27825
27826
27827
27828 /*
27829  * Based on:
27830  * Ext JS Library 1.1.1
27831  * Copyright(c) 2006-2007, Ext JS, LLC.
27832  *
27833  * Originally Released Under LGPL - original licence link has changed is not relivant.
27834  *
27835  * Fork - LGPL
27836  * <script type="text/javascript">
27837  */
27838  
27839 /**
27840  * @class Roo.form.BasicForm
27841  * @extends Roo.util.Observable
27842  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27843  * @constructor
27844  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27845  * @param {Object} config Configuration options
27846  */
27847 Roo.form.BasicForm = function(el, config){
27848     this.allItems = [];
27849     this.childForms = [];
27850     Roo.apply(this, config);
27851     /*
27852      * The Roo.form.Field items in this form.
27853      * @type MixedCollection
27854      */
27855      
27856      
27857     this.items = new Roo.util.MixedCollection(false, function(o){
27858         return o.id || (o.id = Roo.id());
27859     });
27860     this.addEvents({
27861         /**
27862          * @event beforeaction
27863          * Fires before any action is performed. Return false to cancel the action.
27864          * @param {Form} this
27865          * @param {Action} action The action to be performed
27866          */
27867         beforeaction: true,
27868         /**
27869          * @event actionfailed
27870          * Fires when an action fails.
27871          * @param {Form} this
27872          * @param {Action} action The action that failed
27873          */
27874         actionfailed : true,
27875         /**
27876          * @event actioncomplete
27877          * Fires when an action is completed.
27878          * @param {Form} this
27879          * @param {Action} action The action that completed
27880          */
27881         actioncomplete : true
27882     });
27883     if(el){
27884         this.initEl(el);
27885     }
27886     Roo.form.BasicForm.superclass.constructor.call(this);
27887 };
27888
27889 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27890     /**
27891      * @cfg {String} method
27892      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27893      */
27894     /**
27895      * @cfg {DataReader} reader
27896      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27897      * This is optional as there is built-in support for processing JSON.
27898      */
27899     /**
27900      * @cfg {DataReader} errorReader
27901      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27902      * This is completely optional as there is built-in support for processing JSON.
27903      */
27904     /**
27905      * @cfg {String} url
27906      * The URL to use for form actions if one isn't supplied in the action options.
27907      */
27908     /**
27909      * @cfg {Boolean} fileUpload
27910      * Set to true if this form is a file upload.
27911      */
27912      
27913     /**
27914      * @cfg {Object} baseParams
27915      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27916      */
27917      /**
27918      
27919     /**
27920      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27921      */
27922     timeout: 30,
27923
27924     // private
27925     activeAction : null,
27926
27927     /**
27928      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27929      * or setValues() data instead of when the form was first created.
27930      */
27931     trackResetOnLoad : false,
27932     
27933     
27934     /**
27935      * childForms - used for multi-tab forms
27936      * @type {Array}
27937      */
27938     childForms : false,
27939     
27940     /**
27941      * allItems - full list of fields.
27942      * @type {Array}
27943      */
27944     allItems : false,
27945     
27946     /**
27947      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27948      * element by passing it or its id or mask the form itself by passing in true.
27949      * @type Mixed
27950      */
27951     waitMsgTarget : false,
27952
27953     // private
27954     initEl : function(el){
27955         this.el = Roo.get(el);
27956         this.id = this.el.id || Roo.id();
27957         this.el.on('submit', this.onSubmit, this);
27958         this.el.addClass('x-form');
27959     },
27960
27961     // private
27962     onSubmit : function(e){
27963         e.stopEvent();
27964     },
27965
27966     /**
27967      * Returns true if client-side validation on the form is successful.
27968      * @return Boolean
27969      */
27970     isValid : function(){
27971         var valid = true;
27972         this.items.each(function(f){
27973            if(!f.validate()){
27974                valid = false;
27975            }
27976         });
27977         return valid;
27978     },
27979
27980     /**
27981      * Returns true if any fields in this form have changed since their original load.
27982      * @return Boolean
27983      */
27984     isDirty : function(){
27985         var dirty = false;
27986         this.items.each(function(f){
27987            if(f.isDirty()){
27988                dirty = true;
27989                return false;
27990            }
27991         });
27992         return dirty;
27993     },
27994
27995     /**
27996      * Performs a predefined action (submit or load) or custom actions you define on this form.
27997      * @param {String} actionName The name of the action type
27998      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27999      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28000      * accept other config options):
28001      * <pre>
28002 Property          Type             Description
28003 ----------------  ---------------  ----------------------------------------------------------------------------------
28004 url               String           The url for the action (defaults to the form's url)
28005 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28006 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28007 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28008                                    validate the form on the client (defaults to false)
28009      * </pre>
28010      * @return {BasicForm} this
28011      */
28012     doAction : function(action, options){
28013         if(typeof action == 'string'){
28014             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28015         }
28016         if(this.fireEvent('beforeaction', this, action) !== false){
28017             this.beforeAction(action);
28018             action.run.defer(100, action);
28019         }
28020         return this;
28021     },
28022
28023     /**
28024      * Shortcut to do a submit action.
28025      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28026      * @return {BasicForm} this
28027      */
28028     submit : function(options){
28029         this.doAction('submit', options);
28030         return this;
28031     },
28032
28033     /**
28034      * Shortcut to do a load action.
28035      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28036      * @return {BasicForm} this
28037      */
28038     load : function(options){
28039         this.doAction('load', options);
28040         return this;
28041     },
28042
28043     /**
28044      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28045      * @param {Record} record The record to edit
28046      * @return {BasicForm} this
28047      */
28048     updateRecord : function(record){
28049         record.beginEdit();
28050         var fs = record.fields;
28051         fs.each(function(f){
28052             var field = this.findField(f.name);
28053             if(field){
28054                 record.set(f.name, field.getValue());
28055             }
28056         }, this);
28057         record.endEdit();
28058         return this;
28059     },
28060
28061     /**
28062      * Loads an Roo.data.Record into this form.
28063      * @param {Record} record The record to load
28064      * @return {BasicForm} this
28065      */
28066     loadRecord : function(record){
28067         this.setValues(record.data);
28068         return this;
28069     },
28070
28071     // private
28072     beforeAction : function(action){
28073         var o = action.options;
28074         
28075        
28076         if(this.waitMsgTarget === true){
28077             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28078         }else if(this.waitMsgTarget){
28079             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28080             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28081         }else {
28082             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28083         }
28084          
28085     },
28086
28087     // private
28088     afterAction : function(action, success){
28089         this.activeAction = null;
28090         var o = action.options;
28091         
28092         if(this.waitMsgTarget === true){
28093             this.el.unmask();
28094         }else if(this.waitMsgTarget){
28095             this.waitMsgTarget.unmask();
28096         }else{
28097             Roo.MessageBox.updateProgress(1);
28098             Roo.MessageBox.hide();
28099         }
28100          
28101         if(success){
28102             if(o.reset){
28103                 this.reset();
28104             }
28105             Roo.callback(o.success, o.scope, [this, action]);
28106             this.fireEvent('actioncomplete', this, action);
28107             
28108         }else{
28109             
28110             // failure condition..
28111             // we have a scenario where updates need confirming.
28112             // eg. if a locking scenario exists..
28113             // we look for { errors : { needs_confirm : true }} in the response.
28114             if (
28115                 (typeof(action.result) != 'undefined')  &&
28116                 (typeof(action.result.errors) != 'undefined')  &&
28117                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28118            ){
28119                 var _t = this;
28120                 Roo.MessageBox.confirm(
28121                     "Change requires confirmation",
28122                     action.result.errorMsg,
28123                     function(r) {
28124                         if (r != 'yes') {
28125                             return;
28126                         }
28127                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28128                     }
28129                     
28130                 );
28131                 
28132                 
28133                 
28134                 return;
28135             }
28136             
28137             Roo.callback(o.failure, o.scope, [this, action]);
28138             // show an error message if no failed handler is set..
28139             if (!this.hasListener('actionfailed')) {
28140                 Roo.MessageBox.alert("Error",
28141                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28142                         action.result.errorMsg :
28143                         "Saving Failed, please check your entries or try again"
28144                 );
28145             }
28146             
28147             this.fireEvent('actionfailed', this, action);
28148         }
28149         
28150     },
28151
28152     /**
28153      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28154      * @param {String} id The value to search for
28155      * @return Field
28156      */
28157     findField : function(id){
28158         var field = this.items.get(id);
28159         if(!field){
28160             this.items.each(function(f){
28161                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28162                     field = f;
28163                     return false;
28164                 }
28165             });
28166         }
28167         return field || null;
28168     },
28169
28170     /**
28171      * Add a secondary form to this one, 
28172      * Used to provide tabbed forms. One form is primary, with hidden values 
28173      * which mirror the elements from the other forms.
28174      * 
28175      * @param {Roo.form.Form} form to add.
28176      * 
28177      */
28178     addForm : function(form)
28179     {
28180        
28181         if (this.childForms.indexOf(form) > -1) {
28182             // already added..
28183             return;
28184         }
28185         this.childForms.push(form);
28186         var n = '';
28187         Roo.each(form.allItems, function (fe) {
28188             
28189             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28190             if (this.findField(n)) { // already added..
28191                 return;
28192             }
28193             var add = new Roo.form.Hidden({
28194                 name : n
28195             });
28196             add.render(this.el);
28197             
28198             this.add( add );
28199         }, this);
28200         
28201     },
28202     /**
28203      * Mark fields in this form invalid in bulk.
28204      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28205      * @return {BasicForm} this
28206      */
28207     markInvalid : function(errors){
28208         if(errors instanceof Array){
28209             for(var i = 0, len = errors.length; i < len; i++){
28210                 var fieldError = errors[i];
28211                 var f = this.findField(fieldError.id);
28212                 if(f){
28213                     f.markInvalid(fieldError.msg);
28214                 }
28215             }
28216         }else{
28217             var field, id;
28218             for(id in errors){
28219                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28220                     field.markInvalid(errors[id]);
28221                 }
28222             }
28223         }
28224         Roo.each(this.childForms || [], function (f) {
28225             f.markInvalid(errors);
28226         });
28227         
28228         return this;
28229     },
28230
28231     /**
28232      * Set values for fields in this form in bulk.
28233      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28234      * @return {BasicForm} this
28235      */
28236     setValues : function(values){
28237         if(values instanceof Array){ // array of objects
28238             for(var i = 0, len = values.length; i < len; i++){
28239                 var v = values[i];
28240                 var f = this.findField(v.id);
28241                 if(f){
28242                     f.setValue(v.value);
28243                     if(this.trackResetOnLoad){
28244                         f.originalValue = f.getValue();
28245                     }
28246                 }
28247             }
28248         }else{ // object hash
28249             var field, id;
28250             for(id in values){
28251                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28252                     
28253                     if (field.setFromData && 
28254                         field.valueField && 
28255                         field.displayField &&
28256                         // combos' with local stores can 
28257                         // be queried via setValue()
28258                         // to set their value..
28259                         (field.store && !field.store.isLocal)
28260                         ) {
28261                         // it's a combo
28262                         var sd = { };
28263                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28264                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28265                         field.setFromData(sd);
28266                         
28267                     } else {
28268                         field.setValue(values[id]);
28269                     }
28270                     
28271                     
28272                     if(this.trackResetOnLoad){
28273                         field.originalValue = field.getValue();
28274                     }
28275                 }
28276             }
28277         }
28278          
28279         Roo.each(this.childForms || [], function (f) {
28280             f.setValues(values);
28281         });
28282                 
28283         return this;
28284     },
28285
28286     /**
28287      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28288      * they are returned as an array.
28289      * @param {Boolean} asString
28290      * @return {Object}
28291      */
28292     getValues : function(asString){
28293         if (this.childForms) {
28294             // copy values from the child forms
28295             Roo.each(this.childForms, function (f) {
28296                 this.setValues(f.getValues());
28297             }, this);
28298         }
28299         
28300         
28301         
28302         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28303         if(asString === true){
28304             return fs;
28305         }
28306         return Roo.urlDecode(fs);
28307     },
28308     
28309     /**
28310      * Returns the fields in this form as an object with key/value pairs. 
28311      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28312      * @return {Object}
28313      */
28314     getFieldValues : function(with_hidden)
28315     {
28316         if (this.childForms) {
28317             // copy values from the child forms
28318             // should this call getFieldValues - probably not as we do not currently copy
28319             // hidden fields when we generate..
28320             Roo.each(this.childForms, function (f) {
28321                 this.setValues(f.getValues());
28322             }, this);
28323         }
28324         
28325         var ret = {};
28326         this.items.each(function(f){
28327             if (!f.getName()) {
28328                 return;
28329             }
28330             var v = f.getValue();
28331             // not sure if this supported any more..
28332             if ((typeof(v) == 'object') && f.getRawValue) {
28333                 v = f.getRawValue() ; // dates..
28334             }
28335             // combo boxes where name != hiddenName...
28336             if (f.name != f.getName()) {
28337                 ret[f.name] = f.getRawValue();
28338             }
28339             ret[f.getName()] = v;
28340         });
28341         
28342         return ret;
28343     },
28344
28345     /**
28346      * Clears all invalid messages in this form.
28347      * @return {BasicForm} this
28348      */
28349     clearInvalid : function(){
28350         this.items.each(function(f){
28351            f.clearInvalid();
28352         });
28353         
28354         Roo.each(this.childForms || [], function (f) {
28355             f.clearInvalid();
28356         });
28357         
28358         
28359         return this;
28360     },
28361
28362     /**
28363      * Resets this form.
28364      * @return {BasicForm} this
28365      */
28366     reset : function(){
28367         this.items.each(function(f){
28368             f.reset();
28369         });
28370         
28371         Roo.each(this.childForms || [], function (f) {
28372             f.reset();
28373         });
28374        
28375         
28376         return this;
28377     },
28378
28379     /**
28380      * Add Roo.form components to this form.
28381      * @param {Field} field1
28382      * @param {Field} field2 (optional)
28383      * @param {Field} etc (optional)
28384      * @return {BasicForm} this
28385      */
28386     add : function(){
28387         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28388         return this;
28389     },
28390
28391
28392     /**
28393      * Removes a field from the items collection (does NOT remove its markup).
28394      * @param {Field} field
28395      * @return {BasicForm} this
28396      */
28397     remove : function(field){
28398         this.items.remove(field);
28399         return this;
28400     },
28401
28402     /**
28403      * Looks at the fields in this form, checks them for an id attribute,
28404      * and calls applyTo on the existing dom element with that id.
28405      * @return {BasicForm} this
28406      */
28407     render : function(){
28408         this.items.each(function(f){
28409             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28410                 f.applyTo(f.id);
28411             }
28412         });
28413         return this;
28414     },
28415
28416     /**
28417      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28418      * @param {Object} values
28419      * @return {BasicForm} this
28420      */
28421     applyToFields : function(o){
28422         this.items.each(function(f){
28423            Roo.apply(f, o);
28424         });
28425         return this;
28426     },
28427
28428     /**
28429      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28430      * @param {Object} values
28431      * @return {BasicForm} this
28432      */
28433     applyIfToFields : function(o){
28434         this.items.each(function(f){
28435            Roo.applyIf(f, o);
28436         });
28437         return this;
28438     }
28439 });
28440
28441 // back compat
28442 Roo.BasicForm = Roo.form.BasicForm;/*
28443  * Based on:
28444  * Ext JS Library 1.1.1
28445  * Copyright(c) 2006-2007, Ext JS, LLC.
28446  *
28447  * Originally Released Under LGPL - original licence link has changed is not relivant.
28448  *
28449  * Fork - LGPL
28450  * <script type="text/javascript">
28451  */
28452
28453 /**
28454  * @class Roo.form.Form
28455  * @extends Roo.form.BasicForm
28456  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28457  * @constructor
28458  * @param {Object} config Configuration options
28459  */
28460 Roo.form.Form = function(config){
28461     var xitems =  [];
28462     if (config.items) {
28463         xitems = config.items;
28464         delete config.items;
28465     }
28466    
28467     
28468     Roo.form.Form.superclass.constructor.call(this, null, config);
28469     this.url = this.url || this.action;
28470     if(!this.root){
28471         this.root = new Roo.form.Layout(Roo.applyIf({
28472             id: Roo.id()
28473         }, config));
28474     }
28475     this.active = this.root;
28476     /**
28477      * Array of all the buttons that have been added to this form via {@link addButton}
28478      * @type Array
28479      */
28480     this.buttons = [];
28481     this.allItems = [];
28482     this.addEvents({
28483         /**
28484          * @event clientvalidation
28485          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28486          * @param {Form} this
28487          * @param {Boolean} valid true if the form has passed client-side validation
28488          */
28489         clientvalidation: true,
28490         /**
28491          * @event rendered
28492          * Fires when the form is rendered
28493          * @param {Roo.form.Form} form
28494          */
28495         rendered : true
28496     });
28497     
28498     if (this.progressUrl) {
28499             // push a hidden field onto the list of fields..
28500             this.addxtype( {
28501                     xns: Roo.form, 
28502                     xtype : 'Hidden', 
28503                     name : 'UPLOAD_IDENTIFIER' 
28504             });
28505         }
28506         
28507     
28508     Roo.each(xitems, this.addxtype, this);
28509     
28510     
28511     
28512 };
28513
28514 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28515     /**
28516      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28517      */
28518     /**
28519      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28520      */
28521     /**
28522      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28523      */
28524     buttonAlign:'center',
28525
28526     /**
28527      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28528      */
28529     minButtonWidth:75,
28530
28531     /**
28532      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28533      * This property cascades to child containers if not set.
28534      */
28535     labelAlign:'left',
28536
28537     /**
28538      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28539      * fires a looping event with that state. This is required to bind buttons to the valid
28540      * state using the config value formBind:true on the button.
28541      */
28542     monitorValid : false,
28543
28544     /**
28545      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28546      */
28547     monitorPoll : 200,
28548     
28549     /**
28550      * @cfg {String} progressUrl - Url to return progress data 
28551      */
28552     
28553     progressUrl : false,
28554   
28555     /**
28556      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28557      * fields are added and the column is closed. If no fields are passed the column remains open
28558      * until end() is called.
28559      * @param {Object} config The config to pass to the column
28560      * @param {Field} field1 (optional)
28561      * @param {Field} field2 (optional)
28562      * @param {Field} etc (optional)
28563      * @return Column The column container object
28564      */
28565     column : function(c){
28566         var col = new Roo.form.Column(c);
28567         this.start(col);
28568         if(arguments.length > 1){ // duplicate code required because of Opera
28569             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28570             this.end();
28571         }
28572         return col;
28573     },
28574
28575     /**
28576      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28577      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28578      * until end() is called.
28579      * @param {Object} config The config to pass to the fieldset
28580      * @param {Field} field1 (optional)
28581      * @param {Field} field2 (optional)
28582      * @param {Field} etc (optional)
28583      * @return FieldSet The fieldset container object
28584      */
28585     fieldset : function(c){
28586         var fs = new Roo.form.FieldSet(c);
28587         this.start(fs);
28588         if(arguments.length > 1){ // duplicate code required because of Opera
28589             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28590             this.end();
28591         }
28592         return fs;
28593     },
28594
28595     /**
28596      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28597      * fields are added and the container is closed. If no fields are passed the container remains open
28598      * until end() is called.
28599      * @param {Object} config The config to pass to the Layout
28600      * @param {Field} field1 (optional)
28601      * @param {Field} field2 (optional)
28602      * @param {Field} etc (optional)
28603      * @return Layout The container object
28604      */
28605     container : function(c){
28606         var l = new Roo.form.Layout(c);
28607         this.start(l);
28608         if(arguments.length > 1){ // duplicate code required because of Opera
28609             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28610             this.end();
28611         }
28612         return l;
28613     },
28614
28615     /**
28616      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28617      * @param {Object} container A Roo.form.Layout or subclass of Layout
28618      * @return {Form} this
28619      */
28620     start : function(c){
28621         // cascade label info
28622         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28623         this.active.stack.push(c);
28624         c.ownerCt = this.active;
28625         this.active = c;
28626         return this;
28627     },
28628
28629     /**
28630      * Closes the current open container
28631      * @return {Form} this
28632      */
28633     end : function(){
28634         if(this.active == this.root){
28635             return this;
28636         }
28637         this.active = this.active.ownerCt;
28638         return this;
28639     },
28640
28641     /**
28642      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28643      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28644      * as the label of the field.
28645      * @param {Field} field1
28646      * @param {Field} field2 (optional)
28647      * @param {Field} etc. (optional)
28648      * @return {Form} this
28649      */
28650     add : function(){
28651         this.active.stack.push.apply(this.active.stack, arguments);
28652         this.allItems.push.apply(this.allItems,arguments);
28653         var r = [];
28654         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28655             if(a[i].isFormField){
28656                 r.push(a[i]);
28657             }
28658         }
28659         if(r.length > 0){
28660             Roo.form.Form.superclass.add.apply(this, r);
28661         }
28662         return this;
28663     },
28664     
28665
28666     
28667     
28668     
28669      /**
28670      * Find any element that has been added to a form, using it's ID or name
28671      * This can include framesets, columns etc. along with regular fields..
28672      * @param {String} id - id or name to find.
28673      
28674      * @return {Element} e - or false if nothing found.
28675      */
28676     findbyId : function(id)
28677     {
28678         var ret = false;
28679         if (!id) {
28680             return ret;
28681         }
28682         Roo.each(this.allItems, function(f){
28683             if (f.id == id || f.name == id ){
28684                 ret = f;
28685                 return false;
28686             }
28687         });
28688         return ret;
28689     },
28690
28691     
28692     
28693     /**
28694      * Render this form into the passed container. This should only be called once!
28695      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28696      * @return {Form} this
28697      */
28698     render : function(ct)
28699     {
28700         
28701         
28702         
28703         ct = Roo.get(ct);
28704         var o = this.autoCreate || {
28705             tag: 'form',
28706             method : this.method || 'POST',
28707             id : this.id || Roo.id()
28708         };
28709         this.initEl(ct.createChild(o));
28710
28711         this.root.render(this.el);
28712         
28713        
28714              
28715         this.items.each(function(f){
28716             f.render('x-form-el-'+f.id);
28717         });
28718
28719         if(this.buttons.length > 0){
28720             // tables are required to maintain order and for correct IE layout
28721             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28722                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28723                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28724             }}, null, true);
28725             var tr = tb.getElementsByTagName('tr')[0];
28726             for(var i = 0, len = this.buttons.length; i < len; i++) {
28727                 var b = this.buttons[i];
28728                 var td = document.createElement('td');
28729                 td.className = 'x-form-btn-td';
28730                 b.render(tr.appendChild(td));
28731             }
28732         }
28733         if(this.monitorValid){ // initialize after render
28734             this.startMonitoring();
28735         }
28736         this.fireEvent('rendered', this);
28737         return this;
28738     },
28739
28740     /**
28741      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28742      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28743      * object or a valid Roo.DomHelper element config
28744      * @param {Function} handler The function called when the button is clicked
28745      * @param {Object} scope (optional) The scope of the handler function
28746      * @return {Roo.Button}
28747      */
28748     addButton : function(config, handler, scope){
28749         var bc = {
28750             handler: handler,
28751             scope: scope,
28752             minWidth: this.minButtonWidth,
28753             hideParent:true
28754         };
28755         if(typeof config == "string"){
28756             bc.text = config;
28757         }else{
28758             Roo.apply(bc, config);
28759         }
28760         var btn = new Roo.Button(null, bc);
28761         this.buttons.push(btn);
28762         return btn;
28763     },
28764
28765      /**
28766      * Adds a series of form elements (using the xtype property as the factory method.
28767      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28768      * @param {Object} config 
28769      */
28770     
28771     addxtype : function()
28772     {
28773         var ar = Array.prototype.slice.call(arguments, 0);
28774         var ret = false;
28775         for(var i = 0; i < ar.length; i++) {
28776             if (!ar[i]) {
28777                 continue; // skip -- if this happends something invalid got sent, we 
28778                 // should ignore it, as basically that interface element will not show up
28779                 // and that should be pretty obvious!!
28780             }
28781             
28782             if (Roo.form[ar[i].xtype]) {
28783                 ar[i].form = this;
28784                 var fe = Roo.factory(ar[i], Roo.form);
28785                 if (!ret) {
28786                     ret = fe;
28787                 }
28788                 fe.form = this;
28789                 if (fe.store) {
28790                     fe.store.form = this;
28791                 }
28792                 if (fe.isLayout) {  
28793                          
28794                     this.start(fe);
28795                     this.allItems.push(fe);
28796                     if (fe.items && fe.addxtype) {
28797                         fe.addxtype.apply(fe, fe.items);
28798                         delete fe.items;
28799                     }
28800                      this.end();
28801                     continue;
28802                 }
28803                 
28804                 
28805                  
28806                 this.add(fe);
28807               //  console.log('adding ' + ar[i].xtype);
28808             }
28809             if (ar[i].xtype == 'Button') {  
28810                 //console.log('adding button');
28811                 //console.log(ar[i]);
28812                 this.addButton(ar[i]);
28813                 this.allItems.push(fe);
28814                 continue;
28815             }
28816             
28817             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28818                 alert('end is not supported on xtype any more, use items');
28819             //    this.end();
28820             //    //console.log('adding end');
28821             }
28822             
28823         }
28824         return ret;
28825     },
28826     
28827     /**
28828      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28829      * option "monitorValid"
28830      */
28831     startMonitoring : function(){
28832         if(!this.bound){
28833             this.bound = true;
28834             Roo.TaskMgr.start({
28835                 run : this.bindHandler,
28836                 interval : this.monitorPoll || 200,
28837                 scope: this
28838             });
28839         }
28840     },
28841
28842     /**
28843      * Stops monitoring of the valid state of this form
28844      */
28845     stopMonitoring : function(){
28846         this.bound = false;
28847     },
28848
28849     // private
28850     bindHandler : function(){
28851         if(!this.bound){
28852             return false; // stops binding
28853         }
28854         var valid = true;
28855         this.items.each(function(f){
28856             if(!f.isValid(true)){
28857                 valid = false;
28858                 return false;
28859             }
28860         });
28861         for(var i = 0, len = this.buttons.length; i < len; i++){
28862             var btn = this.buttons[i];
28863             if(btn.formBind === true && btn.disabled === valid){
28864                 btn.setDisabled(!valid);
28865             }
28866         }
28867         this.fireEvent('clientvalidation', this, valid);
28868     }
28869     
28870     
28871     
28872     
28873     
28874     
28875     
28876     
28877 });
28878
28879
28880 // back compat
28881 Roo.Form = Roo.form.Form;
28882 /*
28883  * Based on:
28884  * Ext JS Library 1.1.1
28885  * Copyright(c) 2006-2007, Ext JS, LLC.
28886  *
28887  * Originally Released Under LGPL - original licence link has changed is not relivant.
28888  *
28889  * Fork - LGPL
28890  * <script type="text/javascript">
28891  */
28892  
28893  /**
28894  * @class Roo.form.Action
28895  * Internal Class used to handle form actions
28896  * @constructor
28897  * @param {Roo.form.BasicForm} el The form element or its id
28898  * @param {Object} config Configuration options
28899  */
28900  
28901  
28902 // define the action interface
28903 Roo.form.Action = function(form, options){
28904     this.form = form;
28905     this.options = options || {};
28906 };
28907 /**
28908  * Client Validation Failed
28909  * @const 
28910  */
28911 Roo.form.Action.CLIENT_INVALID = 'client';
28912 /**
28913  * Server Validation Failed
28914  * @const 
28915  */
28916  Roo.form.Action.SERVER_INVALID = 'server';
28917  /**
28918  * Connect to Server Failed
28919  * @const 
28920  */
28921 Roo.form.Action.CONNECT_FAILURE = 'connect';
28922 /**
28923  * Reading Data from Server Failed
28924  * @const 
28925  */
28926 Roo.form.Action.LOAD_FAILURE = 'load';
28927
28928 Roo.form.Action.prototype = {
28929     type : 'default',
28930     failureType : undefined,
28931     response : undefined,
28932     result : undefined,
28933
28934     // interface method
28935     run : function(options){
28936
28937     },
28938
28939     // interface method
28940     success : function(response){
28941
28942     },
28943
28944     // interface method
28945     handleResponse : function(response){
28946
28947     },
28948
28949     // default connection failure
28950     failure : function(response){
28951         
28952         this.response = response;
28953         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28954         this.form.afterAction(this, false);
28955     },
28956
28957     processResponse : function(response){
28958         this.response = response;
28959         if(!response.responseText){
28960             return true;
28961         }
28962         this.result = this.handleResponse(response);
28963         return this.result;
28964     },
28965
28966     // utility functions used internally
28967     getUrl : function(appendParams){
28968         var url = this.options.url || this.form.url || this.form.el.dom.action;
28969         if(appendParams){
28970             var p = this.getParams();
28971             if(p){
28972                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28973             }
28974         }
28975         return url;
28976     },
28977
28978     getMethod : function(){
28979         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28980     },
28981
28982     getParams : function(){
28983         var bp = this.form.baseParams;
28984         var p = this.options.params;
28985         if(p){
28986             if(typeof p == "object"){
28987                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28988             }else if(typeof p == 'string' && bp){
28989                 p += '&' + Roo.urlEncode(bp);
28990             }
28991         }else if(bp){
28992             p = Roo.urlEncode(bp);
28993         }
28994         return p;
28995     },
28996
28997     createCallback : function(){
28998         return {
28999             success: this.success,
29000             failure: this.failure,
29001             scope: this,
29002             timeout: (this.form.timeout*1000),
29003             upload: this.form.fileUpload ? this.success : undefined
29004         };
29005     }
29006 };
29007
29008 Roo.form.Action.Submit = function(form, options){
29009     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29010 };
29011
29012 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29013     type : 'submit',
29014
29015     haveProgress : false,
29016     uploadComplete : false,
29017     
29018     // uploadProgress indicator.
29019     uploadProgress : function()
29020     {
29021         if (!this.form.progressUrl) {
29022             return;
29023         }
29024         
29025         if (!this.haveProgress) {
29026             Roo.MessageBox.progress("Uploading", "Uploading");
29027         }
29028         if (this.uploadComplete) {
29029            Roo.MessageBox.hide();
29030            return;
29031         }
29032         
29033         this.haveProgress = true;
29034    
29035         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29036         
29037         var c = new Roo.data.Connection();
29038         c.request({
29039             url : this.form.progressUrl,
29040             params: {
29041                 id : uid
29042             },
29043             method: 'GET',
29044             success : function(req){
29045                //console.log(data);
29046                 var rdata = false;
29047                 var edata;
29048                 try  {
29049                    rdata = Roo.decode(req.responseText)
29050                 } catch (e) {
29051                     Roo.log("Invalid data from server..");
29052                     Roo.log(edata);
29053                     return;
29054                 }
29055                 if (!rdata || !rdata.success) {
29056                     Roo.log(rdata);
29057                     Roo.MessageBox.alert(Roo.encode(rdata));
29058                     return;
29059                 }
29060                 var data = rdata.data;
29061                 
29062                 if (this.uploadComplete) {
29063                    Roo.MessageBox.hide();
29064                    return;
29065                 }
29066                    
29067                 if (data){
29068                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29069                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29070                     );
29071                 }
29072                 this.uploadProgress.defer(2000,this);
29073             },
29074        
29075             failure: function(data) {
29076                 Roo.log('progress url failed ');
29077                 Roo.log(data);
29078             },
29079             scope : this
29080         });
29081            
29082     },
29083     
29084     
29085     run : function()
29086     {
29087         // run get Values on the form, so it syncs any secondary forms.
29088         this.form.getValues();
29089         
29090         var o = this.options;
29091         var method = this.getMethod();
29092         var isPost = method == 'POST';
29093         if(o.clientValidation === false || this.form.isValid()){
29094             
29095             if (this.form.progressUrl) {
29096                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29097                     (new Date() * 1) + '' + Math.random());
29098                     
29099             } 
29100             
29101             
29102             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29103                 form:this.form.el.dom,
29104                 url:this.getUrl(!isPost),
29105                 method: method,
29106                 params:isPost ? this.getParams() : null,
29107                 isUpload: this.form.fileUpload
29108             }));
29109             
29110             this.uploadProgress();
29111
29112         }else if (o.clientValidation !== false){ // client validation failed
29113             this.failureType = Roo.form.Action.CLIENT_INVALID;
29114             this.form.afterAction(this, false);
29115         }
29116     },
29117
29118     success : function(response)
29119     {
29120         this.uploadComplete= true;
29121         if (this.haveProgress) {
29122             Roo.MessageBox.hide();
29123         }
29124         
29125         
29126         var result = this.processResponse(response);
29127         if(result === true || result.success){
29128             this.form.afterAction(this, true);
29129             return;
29130         }
29131         if(result.errors){
29132             this.form.markInvalid(result.errors);
29133             this.failureType = Roo.form.Action.SERVER_INVALID;
29134         }
29135         this.form.afterAction(this, false);
29136     },
29137     failure : function(response)
29138     {
29139         this.uploadComplete= true;
29140         if (this.haveProgress) {
29141             Roo.MessageBox.hide();
29142         }
29143         
29144         this.response = response;
29145         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29146         this.form.afterAction(this, false);
29147     },
29148     
29149     handleResponse : function(response){
29150         if(this.form.errorReader){
29151             var rs = this.form.errorReader.read(response);
29152             var errors = [];
29153             if(rs.records){
29154                 for(var i = 0, len = rs.records.length; i < len; i++) {
29155                     var r = rs.records[i];
29156                     errors[i] = r.data;
29157                 }
29158             }
29159             if(errors.length < 1){
29160                 errors = null;
29161             }
29162             return {
29163                 success : rs.success,
29164                 errors : errors
29165             };
29166         }
29167         var ret = false;
29168         try {
29169             ret = Roo.decode(response.responseText);
29170         } catch (e) {
29171             ret = {
29172                 success: false,
29173                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29174                 errors : []
29175             };
29176         }
29177         return ret;
29178         
29179     }
29180 });
29181
29182
29183 Roo.form.Action.Load = function(form, options){
29184     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29185     this.reader = this.form.reader;
29186 };
29187
29188 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29189     type : 'load',
29190
29191     run : function(){
29192         
29193         Roo.Ajax.request(Roo.apply(
29194                 this.createCallback(), {
29195                     method:this.getMethod(),
29196                     url:this.getUrl(false),
29197                     params:this.getParams()
29198         }));
29199     },
29200
29201     success : function(response){
29202         
29203         var result = this.processResponse(response);
29204         if(result === true || !result.success || !result.data){
29205             this.failureType = Roo.form.Action.LOAD_FAILURE;
29206             this.form.afterAction(this, false);
29207             return;
29208         }
29209         this.form.clearInvalid();
29210         this.form.setValues(result.data);
29211         this.form.afterAction(this, true);
29212     },
29213
29214     handleResponse : function(response){
29215         if(this.form.reader){
29216             var rs = this.form.reader.read(response);
29217             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29218             return {
29219                 success : rs.success,
29220                 data : data
29221             };
29222         }
29223         return Roo.decode(response.responseText);
29224     }
29225 });
29226
29227 Roo.form.Action.ACTION_TYPES = {
29228     'load' : Roo.form.Action.Load,
29229     'submit' : Roo.form.Action.Submit
29230 };/*
29231  * Based on:
29232  * Ext JS Library 1.1.1
29233  * Copyright(c) 2006-2007, Ext JS, LLC.
29234  *
29235  * Originally Released Under LGPL - original licence link has changed is not relivant.
29236  *
29237  * Fork - LGPL
29238  * <script type="text/javascript">
29239  */
29240  
29241 /**
29242  * @class Roo.form.Layout
29243  * @extends Roo.Component
29244  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29245  * @constructor
29246  * @param {Object} config Configuration options
29247  */
29248 Roo.form.Layout = function(config){
29249     var xitems = [];
29250     if (config.items) {
29251         xitems = config.items;
29252         delete config.items;
29253     }
29254     Roo.form.Layout.superclass.constructor.call(this, config);
29255     this.stack = [];
29256     Roo.each(xitems, this.addxtype, this);
29257      
29258 };
29259
29260 Roo.extend(Roo.form.Layout, Roo.Component, {
29261     /**
29262      * @cfg {String/Object} autoCreate
29263      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29264      */
29265     /**
29266      * @cfg {String/Object/Function} style
29267      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29268      * a function which returns such a specification.
29269      */
29270     /**
29271      * @cfg {String} labelAlign
29272      * Valid values are "left," "top" and "right" (defaults to "left")
29273      */
29274     /**
29275      * @cfg {Number} labelWidth
29276      * Fixed width in pixels of all field labels (defaults to undefined)
29277      */
29278     /**
29279      * @cfg {Boolean} clear
29280      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29281      */
29282     clear : true,
29283     /**
29284      * @cfg {String} labelSeparator
29285      * The separator to use after field labels (defaults to ':')
29286      */
29287     labelSeparator : ':',
29288     /**
29289      * @cfg {Boolean} hideLabels
29290      * True to suppress the display of field labels in this layout (defaults to false)
29291      */
29292     hideLabels : false,
29293
29294     // private
29295     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29296     
29297     isLayout : true,
29298     
29299     // private
29300     onRender : function(ct, position){
29301         if(this.el){ // from markup
29302             this.el = Roo.get(this.el);
29303         }else {  // generate
29304             var cfg = this.getAutoCreate();
29305             this.el = ct.createChild(cfg, position);
29306         }
29307         if(this.style){
29308             this.el.applyStyles(this.style);
29309         }
29310         if(this.labelAlign){
29311             this.el.addClass('x-form-label-'+this.labelAlign);
29312         }
29313         if(this.hideLabels){
29314             this.labelStyle = "display:none";
29315             this.elementStyle = "padding-left:0;";
29316         }else{
29317             if(typeof this.labelWidth == 'number'){
29318                 this.labelStyle = "width:"+this.labelWidth+"px;";
29319                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29320             }
29321             if(this.labelAlign == 'top'){
29322                 this.labelStyle = "width:auto;";
29323                 this.elementStyle = "padding-left:0;";
29324             }
29325         }
29326         var stack = this.stack;
29327         var slen = stack.length;
29328         if(slen > 0){
29329             if(!this.fieldTpl){
29330                 var t = new Roo.Template(
29331                     '<div class="x-form-item {5}">',
29332                         '<label for="{0}" style="{2}">{1}{4}</label>',
29333                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29334                         '</div>',
29335                     '</div><div class="x-form-clear-left"></div>'
29336                 );
29337                 t.disableFormats = true;
29338                 t.compile();
29339                 Roo.form.Layout.prototype.fieldTpl = t;
29340             }
29341             for(var i = 0; i < slen; i++) {
29342                 if(stack[i].isFormField){
29343                     this.renderField(stack[i]);
29344                 }else{
29345                     this.renderComponent(stack[i]);
29346                 }
29347             }
29348         }
29349         if(this.clear){
29350             this.el.createChild({cls:'x-form-clear'});
29351         }
29352     },
29353
29354     // private
29355     renderField : function(f){
29356         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29357                f.id, //0
29358                f.fieldLabel, //1
29359                f.labelStyle||this.labelStyle||'', //2
29360                this.elementStyle||'', //3
29361                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29362                f.itemCls||this.itemCls||''  //5
29363        ], true).getPrevSibling());
29364     },
29365
29366     // private
29367     renderComponent : function(c){
29368         c.render(c.isLayout ? this.el : this.el.createChild());    
29369     },
29370     /**
29371      * Adds a object form elements (using the xtype property as the factory method.)
29372      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29373      * @param {Object} config 
29374      */
29375     addxtype : function(o)
29376     {
29377         // create the lement.
29378         o.form = this.form;
29379         var fe = Roo.factory(o, Roo.form);
29380         this.form.allItems.push(fe);
29381         this.stack.push(fe);
29382         
29383         if (fe.isFormField) {
29384             this.form.items.add(fe);
29385         }
29386          
29387         return fe;
29388     }
29389 });
29390
29391 /**
29392  * @class Roo.form.Column
29393  * @extends Roo.form.Layout
29394  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29395  * @constructor
29396  * @param {Object} config Configuration options
29397  */
29398 Roo.form.Column = function(config){
29399     Roo.form.Column.superclass.constructor.call(this, config);
29400 };
29401
29402 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29403     /**
29404      * @cfg {Number/String} width
29405      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29406      */
29407     /**
29408      * @cfg {String/Object} autoCreate
29409      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29410      */
29411
29412     // private
29413     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29414
29415     // private
29416     onRender : function(ct, position){
29417         Roo.form.Column.superclass.onRender.call(this, ct, position);
29418         if(this.width){
29419             this.el.setWidth(this.width);
29420         }
29421     }
29422 });
29423
29424
29425 /**
29426  * @class Roo.form.Row
29427  * @extends Roo.form.Layout
29428  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29429  * @constructor
29430  * @param {Object} config Configuration options
29431  */
29432
29433  
29434 Roo.form.Row = function(config){
29435     Roo.form.Row.superclass.constructor.call(this, config);
29436 };
29437  
29438 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29439       /**
29440      * @cfg {Number/String} width
29441      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29442      */
29443     /**
29444      * @cfg {Number/String} height
29445      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29446      */
29447     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29448     
29449     padWidth : 20,
29450     // private
29451     onRender : function(ct, position){
29452         //console.log('row render');
29453         if(!this.rowTpl){
29454             var t = new Roo.Template(
29455                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29456                     '<label for="{0}" style="{2}">{1}{4}</label>',
29457                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29458                     '</div>',
29459                 '</div>'
29460             );
29461             t.disableFormats = true;
29462             t.compile();
29463             Roo.form.Layout.prototype.rowTpl = t;
29464         }
29465         this.fieldTpl = this.rowTpl;
29466         
29467         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29468         var labelWidth = 100;
29469         
29470         if ((this.labelAlign != 'top')) {
29471             if (typeof this.labelWidth == 'number') {
29472                 labelWidth = this.labelWidth
29473             }
29474             this.padWidth =  20 + labelWidth;
29475             
29476         }
29477         
29478         Roo.form.Column.superclass.onRender.call(this, ct, position);
29479         if(this.width){
29480             this.el.setWidth(this.width);
29481         }
29482         if(this.height){
29483             this.el.setHeight(this.height);
29484         }
29485     },
29486     
29487     // private
29488     renderField : function(f){
29489         f.fieldEl = this.fieldTpl.append(this.el, [
29490                f.id, f.fieldLabel,
29491                f.labelStyle||this.labelStyle||'',
29492                this.elementStyle||'',
29493                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29494                f.itemCls||this.itemCls||'',
29495                f.width ? f.width + this.padWidth : 160 + this.padWidth
29496        ],true);
29497     }
29498 });
29499  
29500
29501 /**
29502  * @class Roo.form.FieldSet
29503  * @extends Roo.form.Layout
29504  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29505  * @constructor
29506  * @param {Object} config Configuration options
29507  */
29508 Roo.form.FieldSet = function(config){
29509     Roo.form.FieldSet.superclass.constructor.call(this, config);
29510 };
29511
29512 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29513     /**
29514      * @cfg {String} legend
29515      * The text to display as the legend for the FieldSet (defaults to '')
29516      */
29517     /**
29518      * @cfg {String/Object} autoCreate
29519      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29520      */
29521
29522     // private
29523     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29524
29525     // private
29526     onRender : function(ct, position){
29527         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29528         if(this.legend){
29529             this.setLegend(this.legend);
29530         }
29531     },
29532
29533     // private
29534     setLegend : function(text){
29535         if(this.rendered){
29536             this.el.child('legend').update(text);
29537         }
29538     }
29539 });/*
29540  * Based on:
29541  * Ext JS Library 1.1.1
29542  * Copyright(c) 2006-2007, Ext JS, LLC.
29543  *
29544  * Originally Released Under LGPL - original licence link has changed is not relivant.
29545  *
29546  * Fork - LGPL
29547  * <script type="text/javascript">
29548  */
29549 /**
29550  * @class Roo.form.VTypes
29551  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29552  * @singleton
29553  */
29554 Roo.form.VTypes = function(){
29555     // closure these in so they are only created once.
29556     var alpha = /^[a-zA-Z_]+$/;
29557     var alphanum = /^[a-zA-Z0-9_]+$/;
29558     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29559     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29560
29561     // All these messages and functions are configurable
29562     return {
29563         /**
29564          * The function used to validate email addresses
29565          * @param {String} value The email address
29566          */
29567         'email' : function(v){
29568             return email.test(v);
29569         },
29570         /**
29571          * The error text to display when the email validation function returns false
29572          * @type String
29573          */
29574         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29575         /**
29576          * The keystroke filter mask to be applied on email input
29577          * @type RegExp
29578          */
29579         'emailMask' : /[a-z0-9_\.\-@]/i,
29580
29581         /**
29582          * The function used to validate URLs
29583          * @param {String} value The URL
29584          */
29585         'url' : function(v){
29586             return url.test(v);
29587         },
29588         /**
29589          * The error text to display when the url validation function returns false
29590          * @type String
29591          */
29592         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29593         
29594         /**
29595          * The function used to validate alpha values
29596          * @param {String} value The value
29597          */
29598         'alpha' : function(v){
29599             return alpha.test(v);
29600         },
29601         /**
29602          * The error text to display when the alpha validation function returns false
29603          * @type String
29604          */
29605         'alphaText' : 'This field should only contain letters and _',
29606         /**
29607          * The keystroke filter mask to be applied on alpha input
29608          * @type RegExp
29609          */
29610         'alphaMask' : /[a-z_]/i,
29611
29612         /**
29613          * The function used to validate alphanumeric values
29614          * @param {String} value The value
29615          */
29616         'alphanum' : function(v){
29617             return alphanum.test(v);
29618         },
29619         /**
29620          * The error text to display when the alphanumeric validation function returns false
29621          * @type String
29622          */
29623         'alphanumText' : 'This field should only contain letters, numbers and _',
29624         /**
29625          * The keystroke filter mask to be applied on alphanumeric input
29626          * @type RegExp
29627          */
29628         'alphanumMask' : /[a-z0-9_]/i
29629     };
29630 }();//<script type="text/javascript">
29631
29632 /**
29633  * @class Roo.form.FCKeditor
29634  * @extends Roo.form.TextArea
29635  * Wrapper around the FCKEditor http://www.fckeditor.net
29636  * @constructor
29637  * Creates a new FCKeditor
29638  * @param {Object} config Configuration options
29639  */
29640 Roo.form.FCKeditor = function(config){
29641     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29642     this.addEvents({
29643          /**
29644          * @event editorinit
29645          * Fired when the editor is initialized - you can add extra handlers here..
29646          * @param {FCKeditor} this
29647          * @param {Object} the FCK object.
29648          */
29649         editorinit : true
29650     });
29651     
29652     
29653 };
29654 Roo.form.FCKeditor.editors = { };
29655 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29656 {
29657     //defaultAutoCreate : {
29658     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29659     //},
29660     // private
29661     /**
29662      * @cfg {Object} fck options - see fck manual for details.
29663      */
29664     fckconfig : false,
29665     
29666     /**
29667      * @cfg {Object} fck toolbar set (Basic or Default)
29668      */
29669     toolbarSet : 'Basic',
29670     /**
29671      * @cfg {Object} fck BasePath
29672      */ 
29673     basePath : '/fckeditor/',
29674     
29675     
29676     frame : false,
29677     
29678     value : '',
29679     
29680    
29681     onRender : function(ct, position)
29682     {
29683         if(!this.el){
29684             this.defaultAutoCreate = {
29685                 tag: "textarea",
29686                 style:"width:300px;height:60px;",
29687                 autocomplete: "off"
29688             };
29689         }
29690         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29691         /*
29692         if(this.grow){
29693             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29694             if(this.preventScrollbars){
29695                 this.el.setStyle("overflow", "hidden");
29696             }
29697             this.el.setHeight(this.growMin);
29698         }
29699         */
29700         //console.log('onrender' + this.getId() );
29701         Roo.form.FCKeditor.editors[this.getId()] = this;
29702          
29703
29704         this.replaceTextarea() ;
29705         
29706     },
29707     
29708     getEditor : function() {
29709         return this.fckEditor;
29710     },
29711     /**
29712      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29713      * @param {Mixed} value The value to set
29714      */
29715     
29716     
29717     setValue : function(value)
29718     {
29719         //console.log('setValue: ' + value);
29720         
29721         if(typeof(value) == 'undefined') { // not sure why this is happending...
29722             return;
29723         }
29724         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29725         
29726         //if(!this.el || !this.getEditor()) {
29727         //    this.value = value;
29728             //this.setValue.defer(100,this,[value]);    
29729         //    return;
29730         //} 
29731         
29732         if(!this.getEditor()) {
29733             return;
29734         }
29735         
29736         this.getEditor().SetData(value);
29737         
29738         //
29739
29740     },
29741
29742     /**
29743      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29744      * @return {Mixed} value The field value
29745      */
29746     getValue : function()
29747     {
29748         
29749         if (this.frame && this.frame.dom.style.display == 'none') {
29750             return Roo.form.FCKeditor.superclass.getValue.call(this);
29751         }
29752         
29753         if(!this.el || !this.getEditor()) {
29754            
29755            // this.getValue.defer(100,this); 
29756             return this.value;
29757         }
29758        
29759         
29760         var value=this.getEditor().GetData();
29761         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29762         return Roo.form.FCKeditor.superclass.getValue.call(this);
29763         
29764
29765     },
29766
29767     /**
29768      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29769      * @return {Mixed} value The field value
29770      */
29771     getRawValue : function()
29772     {
29773         if (this.frame && this.frame.dom.style.display == 'none') {
29774             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29775         }
29776         
29777         if(!this.el || !this.getEditor()) {
29778             //this.getRawValue.defer(100,this); 
29779             return this.value;
29780             return;
29781         }
29782         
29783         
29784         
29785         var value=this.getEditor().GetData();
29786         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29787         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29788          
29789     },
29790     
29791     setSize : function(w,h) {
29792         
29793         
29794         
29795         //if (this.frame && this.frame.dom.style.display == 'none') {
29796         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29797         //    return;
29798         //}
29799         //if(!this.el || !this.getEditor()) {
29800         //    this.setSize.defer(100,this, [w,h]); 
29801         //    return;
29802         //}
29803         
29804         
29805         
29806         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29807         
29808         this.frame.dom.setAttribute('width', w);
29809         this.frame.dom.setAttribute('height', h);
29810         this.frame.setSize(w,h);
29811         
29812     },
29813     
29814     toggleSourceEdit : function(value) {
29815         
29816       
29817          
29818         this.el.dom.style.display = value ? '' : 'none';
29819         this.frame.dom.style.display = value ?  'none' : '';
29820         
29821     },
29822     
29823     
29824     focus: function(tag)
29825     {
29826         if (this.frame.dom.style.display == 'none') {
29827             return Roo.form.FCKeditor.superclass.focus.call(this);
29828         }
29829         if(!this.el || !this.getEditor()) {
29830             this.focus.defer(100,this, [tag]); 
29831             return;
29832         }
29833         
29834         
29835         
29836         
29837         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29838         this.getEditor().Focus();
29839         if (tgs.length) {
29840             if (!this.getEditor().Selection.GetSelection()) {
29841                 this.focus.defer(100,this, [tag]); 
29842                 return;
29843             }
29844             
29845             
29846             var r = this.getEditor().EditorDocument.createRange();
29847             r.setStart(tgs[0],0);
29848             r.setEnd(tgs[0],0);
29849             this.getEditor().Selection.GetSelection().removeAllRanges();
29850             this.getEditor().Selection.GetSelection().addRange(r);
29851             this.getEditor().Focus();
29852         }
29853         
29854     },
29855     
29856     
29857     
29858     replaceTextarea : function()
29859     {
29860         if ( document.getElementById( this.getId() + '___Frame' ) )
29861             return ;
29862         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29863         //{
29864             // We must check the elements firstly using the Id and then the name.
29865         var oTextarea = document.getElementById( this.getId() );
29866         
29867         var colElementsByName = document.getElementsByName( this.getId() ) ;
29868          
29869         oTextarea.style.display = 'none' ;
29870
29871         if ( oTextarea.tabIndex ) {            
29872             this.TabIndex = oTextarea.tabIndex ;
29873         }
29874         
29875         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29876         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29877         this.frame = Roo.get(this.getId() + '___Frame')
29878     },
29879     
29880     _getConfigHtml : function()
29881     {
29882         var sConfig = '' ;
29883
29884         for ( var o in this.fckconfig ) {
29885             sConfig += sConfig.length > 0  ? '&amp;' : '';
29886             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29887         }
29888
29889         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29890     },
29891     
29892     
29893     _getIFrameHtml : function()
29894     {
29895         var sFile = 'fckeditor.html' ;
29896         /* no idea what this is about..
29897         try
29898         {
29899             if ( (/fcksource=true/i).test( window.top.location.search ) )
29900                 sFile = 'fckeditor.original.html' ;
29901         }
29902         catch (e) { 
29903         */
29904
29905         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29906         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29907         
29908         
29909         var html = '<iframe id="' + this.getId() +
29910             '___Frame" src="' + sLink +
29911             '" width="' + this.width +
29912             '" height="' + this.height + '"' +
29913             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29914             ' frameborder="0" scrolling="no"></iframe>' ;
29915
29916         return html ;
29917     },
29918     
29919     _insertHtmlBefore : function( html, element )
29920     {
29921         if ( element.insertAdjacentHTML )       {
29922             // IE
29923             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29924         } else { // Gecko
29925             var oRange = document.createRange() ;
29926             oRange.setStartBefore( element ) ;
29927             var oFragment = oRange.createContextualFragment( html );
29928             element.parentNode.insertBefore( oFragment, element ) ;
29929         }
29930     }
29931     
29932     
29933   
29934     
29935     
29936     
29937     
29938
29939 });
29940
29941 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29942
29943 function FCKeditor_OnComplete(editorInstance){
29944     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29945     f.fckEditor = editorInstance;
29946     //console.log("loaded");
29947     f.fireEvent('editorinit', f, editorInstance);
29948
29949   
29950
29951  
29952
29953
29954
29955
29956
29957
29958
29959
29960
29961
29962
29963
29964
29965
29966
29967 //<script type="text/javascript">
29968 /**
29969  * @class Roo.form.GridField
29970  * @extends Roo.form.Field
29971  * Embed a grid (or editable grid into a form)
29972  * STATUS ALPHA
29973  * 
29974  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29975  * it needs 
29976  * xgrid.store = Roo.data.Store
29977  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29978  * xgrid.store.reader = Roo.data.JsonReader 
29979  * 
29980  * 
29981  * @constructor
29982  * Creates a new GridField
29983  * @param {Object} config Configuration options
29984  */
29985 Roo.form.GridField = function(config){
29986     Roo.form.GridField.superclass.constructor.call(this, config);
29987      
29988 };
29989
29990 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29991     /**
29992      * @cfg {Number} width  - used to restrict width of grid..
29993      */
29994     width : 100,
29995     /**
29996      * @cfg {Number} height - used to restrict height of grid..
29997      */
29998     height : 50,
29999      /**
30000      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30001          * 
30002          *}
30003      */
30004     xgrid : false, 
30005     /**
30006      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30007      * {tag: "input", type: "checkbox", autocomplete: "off"})
30008      */
30009    // defaultAutoCreate : { tag: 'div' },
30010     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30011     /**
30012      * @cfg {String} addTitle Text to include for adding a title.
30013      */
30014     addTitle : false,
30015     //
30016     onResize : function(){
30017         Roo.form.Field.superclass.onResize.apply(this, arguments);
30018     },
30019
30020     initEvents : function(){
30021         // Roo.form.Checkbox.superclass.initEvents.call(this);
30022         // has no events...
30023        
30024     },
30025
30026
30027     getResizeEl : function(){
30028         return this.wrap;
30029     },
30030
30031     getPositionEl : function(){
30032         return this.wrap;
30033     },
30034
30035     // private
30036     onRender : function(ct, position){
30037         
30038         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30039         var style = this.style;
30040         delete this.style;
30041         
30042         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30043         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30044         this.viewEl = this.wrap.createChild({ tag: 'div' });
30045         if (style) {
30046             this.viewEl.applyStyles(style);
30047         }
30048         if (this.width) {
30049             this.viewEl.setWidth(this.width);
30050         }
30051         if (this.height) {
30052             this.viewEl.setHeight(this.height);
30053         }
30054         //if(this.inputValue !== undefined){
30055         //this.setValue(this.value);
30056         
30057         
30058         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30059         
30060         
30061         this.grid.render();
30062         this.grid.getDataSource().on('remove', this.refreshValue, this);
30063         this.grid.getDataSource().on('update', this.refreshValue, this);
30064         this.grid.on('afteredit', this.refreshValue, this);
30065  
30066     },
30067      
30068     
30069     /**
30070      * Sets the value of the item. 
30071      * @param {String} either an object  or a string..
30072      */
30073     setValue : function(v){
30074         //this.value = v;
30075         v = v || []; // empty set..
30076         // this does not seem smart - it really only affects memoryproxy grids..
30077         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30078             var ds = this.grid.getDataSource();
30079             // assumes a json reader..
30080             var data = {}
30081             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30082             ds.loadData( data);
30083         }
30084         // clear selection so it does not get stale.
30085         if (this.grid.sm) { 
30086             this.grid.sm.clearSelections();
30087         }
30088         
30089         Roo.form.GridField.superclass.setValue.call(this, v);
30090         this.refreshValue();
30091         // should load data in the grid really....
30092     },
30093     
30094     // private
30095     refreshValue: function() {
30096          var val = [];
30097         this.grid.getDataSource().each(function(r) {
30098             val.push(r.data);
30099         });
30100         this.el.dom.value = Roo.encode(val);
30101     }
30102     
30103      
30104     
30105     
30106 });/*
30107  * Based on:
30108  * Ext JS Library 1.1.1
30109  * Copyright(c) 2006-2007, Ext JS, LLC.
30110  *
30111  * Originally Released Under LGPL - original licence link has changed is not relivant.
30112  *
30113  * Fork - LGPL
30114  * <script type="text/javascript">
30115  */
30116 /**
30117  * @class Roo.form.DisplayField
30118  * @extends Roo.form.Field
30119  * A generic Field to display non-editable data.
30120  * @constructor
30121  * Creates a new Display Field item.
30122  * @param {Object} config Configuration options
30123  */
30124 Roo.form.DisplayField = function(config){
30125     Roo.form.DisplayField.superclass.constructor.call(this, config);
30126     
30127 };
30128
30129 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30130     inputType:      'hidden',
30131     allowBlank:     true,
30132     readOnly:         true,
30133     
30134  
30135     /**
30136      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30137      */
30138     focusClass : undefined,
30139     /**
30140      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30141      */
30142     fieldClass: 'x-form-field',
30143     
30144      /**
30145      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30146      */
30147     valueRenderer: undefined,
30148     
30149     width: 100,
30150     /**
30151      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30152      * {tag: "input", type: "checkbox", autocomplete: "off"})
30153      */
30154      
30155  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30156
30157     onResize : function(){
30158         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30159         
30160     },
30161
30162     initEvents : function(){
30163         // Roo.form.Checkbox.superclass.initEvents.call(this);
30164         // has no events...
30165        
30166     },
30167
30168
30169     getResizeEl : function(){
30170         return this.wrap;
30171     },
30172
30173     getPositionEl : function(){
30174         return this.wrap;
30175     },
30176
30177     // private
30178     onRender : function(ct, position){
30179         
30180         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30181         //if(this.inputValue !== undefined){
30182         this.wrap = this.el.wrap();
30183         
30184         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30185         
30186         if (this.bodyStyle) {
30187             this.viewEl.applyStyles(this.bodyStyle);
30188         }
30189         //this.viewEl.setStyle('padding', '2px');
30190         
30191         this.setValue(this.value);
30192         
30193     },
30194 /*
30195     // private
30196     initValue : Roo.emptyFn,
30197
30198   */
30199
30200         // private
30201     onClick : function(){
30202         
30203     },
30204
30205     /**
30206      * Sets the checked state of the checkbox.
30207      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30208      */
30209     setValue : function(v){
30210         this.value = v;
30211         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30212         // this might be called before we have a dom element..
30213         if (!this.viewEl) {
30214             return;
30215         }
30216         this.viewEl.dom.innerHTML = html;
30217         Roo.form.DisplayField.superclass.setValue.call(this, v);
30218
30219     }
30220 });/*
30221  * 
30222  * Licence- LGPL
30223  * 
30224  */
30225
30226 /**
30227  * @class Roo.form.DayPicker
30228  * @extends Roo.form.Field
30229  * A Day picker show [M] [T] [W] ....
30230  * @constructor
30231  * Creates a new Day Picker
30232  * @param {Object} config Configuration options
30233  */
30234 Roo.form.DayPicker= function(config){
30235     Roo.form.DayPicker.superclass.constructor.call(this, config);
30236      
30237 };
30238
30239 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30240     /**
30241      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30242      */
30243     focusClass : undefined,
30244     /**
30245      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30246      */
30247     fieldClass: "x-form-field",
30248    
30249     /**
30250      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30251      * {tag: "input", type: "checkbox", autocomplete: "off"})
30252      */
30253     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30254     
30255    
30256     actionMode : 'viewEl', 
30257     //
30258     // private
30259  
30260     inputType : 'hidden',
30261     
30262      
30263     inputElement: false, // real input element?
30264     basedOn: false, // ????
30265     
30266     isFormField: true, // not sure where this is needed!!!!
30267
30268     onResize : function(){
30269         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30270         if(!this.boxLabel){
30271             this.el.alignTo(this.wrap, 'c-c');
30272         }
30273     },
30274
30275     initEvents : function(){
30276         Roo.form.Checkbox.superclass.initEvents.call(this);
30277         this.el.on("click", this.onClick,  this);
30278         this.el.on("change", this.onClick,  this);
30279     },
30280
30281
30282     getResizeEl : function(){
30283         return this.wrap;
30284     },
30285
30286     getPositionEl : function(){
30287         return this.wrap;
30288     },
30289
30290     
30291     // private
30292     onRender : function(ct, position){
30293         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30294        
30295         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30296         
30297         var r1 = '<table><tr>';
30298         var r2 = '<tr class="x-form-daypick-icons">';
30299         for (var i=0; i < 7; i++) {
30300             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30301             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30302         }
30303         
30304         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30305         viewEl.select('img').on('click', this.onClick, this);
30306         this.viewEl = viewEl;   
30307         
30308         
30309         // this will not work on Chrome!!!
30310         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30311         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30312         
30313         
30314           
30315
30316     },
30317
30318     // private
30319     initValue : Roo.emptyFn,
30320
30321     /**
30322      * Returns the checked state of the checkbox.
30323      * @return {Boolean} True if checked, else false
30324      */
30325     getValue : function(){
30326         return this.el.dom.value;
30327         
30328     },
30329
30330         // private
30331     onClick : function(e){ 
30332         //this.setChecked(!this.checked);
30333         Roo.get(e.target).toggleClass('x-menu-item-checked');
30334         this.refreshValue();
30335         //if(this.el.dom.checked != this.checked){
30336         //    this.setValue(this.el.dom.checked);
30337        // }
30338     },
30339     
30340     // private
30341     refreshValue : function()
30342     {
30343         var val = '';
30344         this.viewEl.select('img',true).each(function(e,i,n)  {
30345             val += e.is(".x-menu-item-checked") ? String(n) : '';
30346         });
30347         this.setValue(val, true);
30348     },
30349
30350     /**
30351      * Sets the checked state of the checkbox.
30352      * On is always based on a string comparison between inputValue and the param.
30353      * @param {Boolean/String} value - the value to set 
30354      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30355      */
30356     setValue : function(v,suppressEvent){
30357         if (!this.el.dom) {
30358             return;
30359         }
30360         var old = this.el.dom.value ;
30361         this.el.dom.value = v;
30362         if (suppressEvent) {
30363             return ;
30364         }
30365          
30366         // update display..
30367         this.viewEl.select('img',true).each(function(e,i,n)  {
30368             
30369             var on = e.is(".x-menu-item-checked");
30370             var newv = v.indexOf(String(n)) > -1;
30371             if (on != newv) {
30372                 e.toggleClass('x-menu-item-checked');
30373             }
30374             
30375         });
30376         
30377         
30378         this.fireEvent('change', this, v, old);
30379         
30380         
30381     },
30382    
30383     // handle setting of hidden value by some other method!!?!?
30384     setFromHidden: function()
30385     {
30386         if(!this.el){
30387             return;
30388         }
30389         //console.log("SET FROM HIDDEN");
30390         //alert('setFrom hidden');
30391         this.setValue(this.el.dom.value);
30392     },
30393     
30394     onDestroy : function()
30395     {
30396         if(this.viewEl){
30397             Roo.get(this.viewEl).remove();
30398         }
30399          
30400         Roo.form.DayPicker.superclass.onDestroy.call(this);
30401     }
30402
30403 });/*
30404  * RooJS Library 1.1.1
30405  * Copyright(c) 2008-2011  Alan Knowles
30406  *
30407  * License - LGPL
30408  */
30409  
30410
30411 /**
30412  * @class Roo.form.ComboCheck
30413  * @extends Roo.form.ComboBox
30414  * A combobox for multiple select items.
30415  *
30416  * FIXME - could do with a reset button..
30417  * 
30418  * @constructor
30419  * Create a new ComboCheck
30420  * @param {Object} config Configuration options
30421  */
30422 Roo.form.ComboCheck = function(config){
30423     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30424     // should verify some data...
30425     // like
30426     // hiddenName = required..
30427     // displayField = required
30428     // valudField == required
30429     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30430     var _t = this;
30431     Roo.each(req, function(e) {
30432         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30433             throw "Roo.form.ComboCheck : missing value for: " + e;
30434         }
30435     });
30436     
30437     
30438 };
30439
30440 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30441      
30442      
30443     editable : false,
30444      
30445     selectedClass: 'x-menu-item-checked', 
30446     
30447     // private
30448     onRender : function(ct, position){
30449         var _t = this;
30450         
30451         
30452         
30453         if(!this.tpl){
30454             var cls = 'x-combo-list';
30455
30456             
30457             this.tpl =  new Roo.Template({
30458                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30459                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30460                    '<span>{' + this.displayField + '}</span>' +
30461                     '</div>' 
30462                 
30463             });
30464         }
30465  
30466         
30467         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30468         this.view.singleSelect = false;
30469         this.view.multiSelect = true;
30470         this.view.toggleSelect = true;
30471         this.pageTb.add(new Roo.Toolbar.Fill(), {
30472             
30473             text: 'Done',
30474             handler: function()
30475             {
30476                 _t.collapse();
30477             }
30478         });
30479     },
30480     
30481     onViewOver : function(e, t){
30482         // do nothing...
30483         return;
30484         
30485     },
30486     
30487     onViewClick : function(doFocus,index){
30488         return;
30489         
30490     },
30491     select: function () {
30492         //Roo.log("SELECT CALLED");
30493     },
30494      
30495     selectByValue : function(xv, scrollIntoView){
30496         var ar = this.getValueArray();
30497         var sels = [];
30498         
30499         Roo.each(ar, function(v) {
30500             if(v === undefined || v === null){
30501                 return;
30502             }
30503             var r = this.findRecord(this.valueField, v);
30504             if(r){
30505                 sels.push(this.store.indexOf(r))
30506                 
30507             }
30508         },this);
30509         this.view.select(sels);
30510         return false;
30511     },
30512     
30513     
30514     
30515     onSelect : function(record, index){
30516        // Roo.log("onselect Called");
30517        // this is only called by the clear button now..
30518         this.view.clearSelections();
30519         this.setValue('[]');
30520         if (this.value != this.valueBefore) {
30521             this.fireEvent('change', this, this.value, this.valueBefore);
30522         }
30523     },
30524     getValueArray : function()
30525     {
30526         var ar = [] ;
30527         
30528         try {
30529             //Roo.log(this.value);
30530             if (typeof(this.value) == 'undefined') {
30531                 return [];
30532             }
30533             var ar = Roo.decode(this.value);
30534             return  ar instanceof Array ? ar : []; //?? valid?
30535             
30536         } catch(e) {
30537             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30538             return [];
30539         }
30540          
30541     },
30542     expand : function ()
30543     {
30544         Roo.form.ComboCheck.superclass.expand.call(this);
30545         this.valueBefore = this.value;
30546         
30547
30548     },
30549     
30550     collapse : function(){
30551         Roo.form.ComboCheck.superclass.collapse.call(this);
30552         var sl = this.view.getSelectedIndexes();
30553         var st = this.store;
30554         var nv = [];
30555         var tv = [];
30556         var r;
30557         Roo.each(sl, function(i) {
30558             r = st.getAt(i);
30559             nv.push(r.get(this.valueField));
30560         },this);
30561         this.setValue(Roo.encode(nv));
30562         if (this.value != this.valueBefore) {
30563
30564             this.fireEvent('change', this, this.value, this.valueBefore);
30565         }
30566         
30567     },
30568     
30569     setValue : function(v){
30570         // Roo.log(v);
30571         this.value = v;
30572         
30573         var vals = this.getValueArray();
30574         var tv = [];
30575         Roo.each(vals, function(k) {
30576             var r = this.findRecord(this.valueField, k);
30577             if(r){
30578                 tv.push(r.data[this.displayField]);
30579             }else if(this.valueNotFoundText !== undefined){
30580                 tv.push( this.valueNotFoundText );
30581             }
30582         },this);
30583        // Roo.log(tv);
30584         
30585         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30586         this.hiddenField.value = v;
30587         this.value = v;
30588     }
30589     
30590 });//<script type="text/javasscript">
30591  
30592
30593 /**
30594  * @class Roo.DDView
30595  * A DnD enabled version of Roo.View.
30596  * @param {Element/String} container The Element in which to create the View.
30597  * @param {String} tpl The template string used to create the markup for each element of the View
30598  * @param {Object} config The configuration properties. These include all the config options of
30599  * {@link Roo.View} plus some specific to this class.<br>
30600  * <p>
30601  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30602  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30603  * <p>
30604  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30605 .x-view-drag-insert-above {
30606         border-top:1px dotted #3366cc;
30607 }
30608 .x-view-drag-insert-below {
30609         border-bottom:1px dotted #3366cc;
30610 }
30611 </code></pre>
30612  * 
30613  */
30614  
30615 Roo.DDView = function(container, tpl, config) {
30616     Roo.DDView.superclass.constructor.apply(this, arguments);
30617     this.getEl().setStyle("outline", "0px none");
30618     this.getEl().unselectable();
30619     if (this.dragGroup) {
30620                 this.setDraggable(this.dragGroup.split(","));
30621     }
30622     if (this.dropGroup) {
30623                 this.setDroppable(this.dropGroup.split(","));
30624     }
30625     if (this.deletable) {
30626         this.setDeletable();
30627     }
30628     this.isDirtyFlag = false;
30629         this.addEvents({
30630                 "drop" : true
30631         });
30632 };
30633
30634 Roo.extend(Roo.DDView, Roo.View, {
30635 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30636 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30637 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30638 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30639
30640         isFormField: true,
30641
30642         reset: Roo.emptyFn,
30643         
30644         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30645
30646         validate: function() {
30647                 return true;
30648         },
30649         
30650         destroy: function() {
30651                 this.purgeListeners();
30652                 this.getEl.removeAllListeners();
30653                 this.getEl().remove();
30654                 if (this.dragZone) {
30655                         if (this.dragZone.destroy) {
30656                                 this.dragZone.destroy();
30657                         }
30658                 }
30659                 if (this.dropZone) {
30660                         if (this.dropZone.destroy) {
30661                                 this.dropZone.destroy();
30662                         }
30663                 }
30664         },
30665
30666 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30667         getName: function() {
30668                 return this.name;
30669         },
30670
30671 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30672         setValue: function(v) {
30673                 if (!this.store) {
30674                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30675                 }
30676                 var data = {};
30677                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30678                 this.store.proxy = new Roo.data.MemoryProxy(data);
30679                 this.store.load();
30680         },
30681
30682 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30683         getValue: function() {
30684                 var result = '(';
30685                 this.store.each(function(rec) {
30686                         result += rec.id + ',';
30687                 });
30688                 return result.substr(0, result.length - 1) + ')';
30689         },
30690         
30691         getIds: function() {
30692                 var i = 0, result = new Array(this.store.getCount());
30693                 this.store.each(function(rec) {
30694                         result[i++] = rec.id;
30695                 });
30696                 return result;
30697         },
30698         
30699         isDirty: function() {
30700                 return this.isDirtyFlag;
30701         },
30702
30703 /**
30704  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30705  *      whole Element becomes the target, and this causes the drop gesture to append.
30706  */
30707     getTargetFromEvent : function(e) {
30708                 var target = e.getTarget();
30709                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30710                 target = target.parentNode;
30711                 }
30712                 if (!target) {
30713                         target = this.el.dom.lastChild || this.el.dom;
30714                 }
30715                 return target;
30716     },
30717
30718 /**
30719  *      Create the drag data which consists of an object which has the property "ddel" as
30720  *      the drag proxy element. 
30721  */
30722     getDragData : function(e) {
30723         var target = this.findItemFromChild(e.getTarget());
30724                 if(target) {
30725                         this.handleSelection(e);
30726                         var selNodes = this.getSelectedNodes();
30727             var dragData = {
30728                 source: this,
30729                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30730                 nodes: selNodes,
30731                 records: []
30732                         };
30733                         var selectedIndices = this.getSelectedIndexes();
30734                         for (var i = 0; i < selectedIndices.length; i++) {
30735                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30736                         }
30737                         if (selNodes.length == 1) {
30738                                 dragData.ddel = target.cloneNode(true); // the div element
30739                         } else {
30740                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30741                                 div.className = 'multi-proxy';
30742                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30743                                         div.appendChild(selNodes[i].cloneNode(true));
30744                                 }
30745                                 dragData.ddel = div;
30746                         }
30747             //console.log(dragData)
30748             //console.log(dragData.ddel.innerHTML)
30749                         return dragData;
30750                 }
30751         //console.log('nodragData')
30752                 return false;
30753     },
30754     
30755 /**     Specify to which ddGroup items in this DDView may be dragged. */
30756     setDraggable: function(ddGroup) {
30757         if (ddGroup instanceof Array) {
30758                 Roo.each(ddGroup, this.setDraggable, this);
30759                 return;
30760         }
30761         if (this.dragZone) {
30762                 this.dragZone.addToGroup(ddGroup);
30763         } else {
30764                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30765                                 containerScroll: true,
30766                                 ddGroup: ddGroup 
30767
30768                         });
30769 //                      Draggability implies selection. DragZone's mousedown selects the element.
30770                         if (!this.multiSelect) { this.singleSelect = true; }
30771
30772 //                      Wire the DragZone's handlers up to methods in *this*
30773                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30774                 }
30775     },
30776
30777 /**     Specify from which ddGroup this DDView accepts drops. */
30778     setDroppable: function(ddGroup) {
30779         if (ddGroup instanceof Array) {
30780                 Roo.each(ddGroup, this.setDroppable, this);
30781                 return;
30782         }
30783         if (this.dropZone) {
30784                 this.dropZone.addToGroup(ddGroup);
30785         } else {
30786                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30787                                 containerScroll: true,
30788                                 ddGroup: ddGroup
30789                         });
30790
30791 //                      Wire the DropZone's handlers up to methods in *this*
30792                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30793                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30794                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30795                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30796                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30797                 }
30798     },
30799
30800 /**     Decide whether to drop above or below a View node. */
30801     getDropPoint : function(e, n, dd){
30802         if (n == this.el.dom) { return "above"; }
30803                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30804                 var c = t + (b - t) / 2;
30805                 var y = Roo.lib.Event.getPageY(e);
30806                 if(y <= c) {
30807                         return "above";
30808                 }else{
30809                         return "below";
30810                 }
30811     },
30812
30813     onNodeEnter : function(n, dd, e, data){
30814                 return false;
30815     },
30816     
30817     onNodeOver : function(n, dd, e, data){
30818                 var pt = this.getDropPoint(e, n, dd);
30819                 // set the insert point style on the target node
30820                 var dragElClass = this.dropNotAllowed;
30821                 if (pt) {
30822                         var targetElClass;
30823                         if (pt == "above"){
30824                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30825                                 targetElClass = "x-view-drag-insert-above";
30826                         } else {
30827                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30828                                 targetElClass = "x-view-drag-insert-below";
30829                         }
30830                         if (this.lastInsertClass != targetElClass){
30831                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30832                                 this.lastInsertClass = targetElClass;
30833                         }
30834                 }
30835                 return dragElClass;
30836         },
30837
30838     onNodeOut : function(n, dd, e, data){
30839                 this.removeDropIndicators(n);
30840     },
30841
30842     onNodeDrop : function(n, dd, e, data){
30843         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30844                 return false;
30845         }
30846         var pt = this.getDropPoint(e, n, dd);
30847                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30848                 if (pt == "below") { insertAt++; }
30849                 for (var i = 0; i < data.records.length; i++) {
30850                         var r = data.records[i];
30851                         var dup = this.store.getById(r.id);
30852                         if (dup && (dd != this.dragZone)) {
30853                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30854                         } else {
30855                                 if (data.copy) {
30856                                         this.store.insert(insertAt++, r.copy());
30857                                 } else {
30858                                         data.source.isDirtyFlag = true;
30859                                         r.store.remove(r);
30860                                         this.store.insert(insertAt++, r);
30861                                 }
30862                                 this.isDirtyFlag = true;
30863                         }
30864                 }
30865                 this.dragZone.cachedTarget = null;
30866                 return true;
30867     },
30868
30869     removeDropIndicators : function(n){
30870                 if(n){
30871                         Roo.fly(n).removeClass([
30872                                 "x-view-drag-insert-above",
30873                                 "x-view-drag-insert-below"]);
30874                         this.lastInsertClass = "_noclass";
30875                 }
30876     },
30877
30878 /**
30879  *      Utility method. Add a delete option to the DDView's context menu.
30880  *      @param {String} imageUrl The URL of the "delete" icon image.
30881  */
30882         setDeletable: function(imageUrl) {
30883                 if (!this.singleSelect && !this.multiSelect) {
30884                         this.singleSelect = true;
30885                 }
30886                 var c = this.getContextMenu();
30887                 this.contextMenu.on("itemclick", function(item) {
30888                         switch (item.id) {
30889                                 case "delete":
30890                                         this.remove(this.getSelectedIndexes());
30891                                         break;
30892                         }
30893                 }, this);
30894                 this.contextMenu.add({
30895                         icon: imageUrl,
30896                         id: "delete",
30897                         text: 'Delete'
30898                 });
30899         },
30900         
30901 /**     Return the context menu for this DDView. */
30902         getContextMenu: function() {
30903                 if (!this.contextMenu) {
30904 //                      Create the View's context menu
30905                         this.contextMenu = new Roo.menu.Menu({
30906                                 id: this.id + "-contextmenu"
30907                         });
30908                         this.el.on("contextmenu", this.showContextMenu, this);
30909                 }
30910                 return this.contextMenu;
30911         },
30912         
30913         disableContextMenu: function() {
30914                 if (this.contextMenu) {
30915                         this.el.un("contextmenu", this.showContextMenu, this);
30916                 }
30917         },
30918
30919         showContextMenu: function(e, item) {
30920         item = this.findItemFromChild(e.getTarget());
30921                 if (item) {
30922                         e.stopEvent();
30923                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30924                         this.contextMenu.showAt(e.getXY());
30925             }
30926     },
30927
30928 /**
30929  *      Remove {@link Roo.data.Record}s at the specified indices.
30930  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30931  */
30932     remove: function(selectedIndices) {
30933                 selectedIndices = [].concat(selectedIndices);
30934                 for (var i = 0; i < selectedIndices.length; i++) {
30935                         var rec = this.store.getAt(selectedIndices[i]);
30936                         this.store.remove(rec);
30937                 }
30938     },
30939
30940 /**
30941  *      Double click fires the event, but also, if this is draggable, and there is only one other
30942  *      related DropZone, it transfers the selected node.
30943  */
30944     onDblClick : function(e){
30945         var item = this.findItemFromChild(e.getTarget());
30946         if(item){
30947             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30948                 return false;
30949             }
30950             if (this.dragGroup) {
30951                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30952                     while (targets.indexOf(this.dropZone) > -1) {
30953                             targets.remove(this.dropZone);
30954                                 }
30955                     if (targets.length == 1) {
30956                                         this.dragZone.cachedTarget = null;
30957                         var el = Roo.get(targets[0].getEl());
30958                         var box = el.getBox(true);
30959                         targets[0].onNodeDrop(el.dom, {
30960                                 target: el.dom,
30961                                 xy: [box.x, box.y + box.height - 1]
30962                         }, null, this.getDragData(e));
30963                     }
30964                 }
30965         }
30966     },
30967     
30968     handleSelection: function(e) {
30969                 this.dragZone.cachedTarget = null;
30970         var item = this.findItemFromChild(e.getTarget());
30971         if (!item) {
30972                 this.clearSelections(true);
30973                 return;
30974         }
30975                 if (item && (this.multiSelect || this.singleSelect)){
30976                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30977                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30978                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30979                                 this.unselect(item);
30980                         } else {
30981                                 this.select(item, this.multiSelect && e.ctrlKey);
30982                                 this.lastSelection = item;
30983                         }
30984                 }
30985     },
30986
30987     onItemClick : function(item, index, e){
30988                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30989                         return false;
30990                 }
30991                 return true;
30992     },
30993
30994     unselect : function(nodeInfo, suppressEvent){
30995                 var node = this.getNode(nodeInfo);
30996                 if(node && this.isSelected(node)){
30997                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30998                                 Roo.fly(node).removeClass(this.selectedClass);
30999                                 this.selections.remove(node);
31000                                 if(!suppressEvent){
31001                                         this.fireEvent("selectionchange", this, this.selections);
31002                                 }
31003                         }
31004                 }
31005     }
31006 });
31007 /*
31008  * Based on:
31009  * Ext JS Library 1.1.1
31010  * Copyright(c) 2006-2007, Ext JS, LLC.
31011  *
31012  * Originally Released Under LGPL - original licence link has changed is not relivant.
31013  *
31014  * Fork - LGPL
31015  * <script type="text/javascript">
31016  */
31017  
31018 /**
31019  * @class Roo.LayoutManager
31020  * @extends Roo.util.Observable
31021  * Base class for layout managers.
31022  */
31023 Roo.LayoutManager = function(container, config){
31024     Roo.LayoutManager.superclass.constructor.call(this);
31025     this.el = Roo.get(container);
31026     // ie scrollbar fix
31027     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31028         document.body.scroll = "no";
31029     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31030         this.el.position('relative');
31031     }
31032     this.id = this.el.id;
31033     this.el.addClass("x-layout-container");
31034     /** false to disable window resize monitoring @type Boolean */
31035     this.monitorWindowResize = true;
31036     this.regions = {};
31037     this.addEvents({
31038         /**
31039          * @event layout
31040          * Fires when a layout is performed. 
31041          * @param {Roo.LayoutManager} this
31042          */
31043         "layout" : true,
31044         /**
31045          * @event regionresized
31046          * Fires when the user resizes a region. 
31047          * @param {Roo.LayoutRegion} region The resized region
31048          * @param {Number} newSize The new size (width for east/west, height for north/south)
31049          */
31050         "regionresized" : true,
31051         /**
31052          * @event regioncollapsed
31053          * Fires when a region is collapsed. 
31054          * @param {Roo.LayoutRegion} region The collapsed region
31055          */
31056         "regioncollapsed" : true,
31057         /**
31058          * @event regionexpanded
31059          * Fires when a region is expanded.  
31060          * @param {Roo.LayoutRegion} region The expanded region
31061          */
31062         "regionexpanded" : true
31063     });
31064     this.updating = false;
31065     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31066 };
31067
31068 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
31069     /**
31070      * Returns true if this layout is currently being updated
31071      * @return {Boolean}
31072      */
31073     isUpdating : function(){
31074         return this.updating; 
31075     },
31076     
31077     /**
31078      * Suspend the LayoutManager from doing auto-layouts while
31079      * making multiple add or remove calls
31080      */
31081     beginUpdate : function(){
31082         this.updating = true;    
31083     },
31084     
31085     /**
31086      * Restore auto-layouts and optionally disable the manager from performing a layout
31087      * @param {Boolean} noLayout true to disable a layout update 
31088      */
31089     endUpdate : function(noLayout){
31090         this.updating = false;
31091         if(!noLayout){
31092             this.layout();
31093         }    
31094     },
31095     
31096     layout: function(){
31097         
31098     },
31099     
31100     onRegionResized : function(region, newSize){
31101         this.fireEvent("regionresized", region, newSize);
31102         this.layout();
31103     },
31104     
31105     onRegionCollapsed : function(region){
31106         this.fireEvent("regioncollapsed", region);
31107     },
31108     
31109     onRegionExpanded : function(region){
31110         this.fireEvent("regionexpanded", region);
31111     },
31112         
31113     /**
31114      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31115      * performs box-model adjustments.
31116      * @return {Object} The size as an object {width: (the width), height: (the height)}
31117      */
31118     getViewSize : function(){
31119         var size;
31120         if(this.el.dom != document.body){
31121             size = this.el.getSize();
31122         }else{
31123             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31124         }
31125         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31126         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31127         return size;
31128     },
31129     
31130     /**
31131      * Returns the Element this layout is bound to.
31132      * @return {Roo.Element}
31133      */
31134     getEl : function(){
31135         return this.el;
31136     },
31137     
31138     /**
31139      * Returns the specified region.
31140      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31141      * @return {Roo.LayoutRegion}
31142      */
31143     getRegion : function(target){
31144         return this.regions[target.toLowerCase()];
31145     },
31146     
31147     onWindowResize : function(){
31148         if(this.monitorWindowResize){
31149             this.layout();
31150         }
31151     }
31152 });/*
31153  * Based on:
31154  * Ext JS Library 1.1.1
31155  * Copyright(c) 2006-2007, Ext JS, LLC.
31156  *
31157  * Originally Released Under LGPL - original licence link has changed is not relivant.
31158  *
31159  * Fork - LGPL
31160  * <script type="text/javascript">
31161  */
31162 /**
31163  * @class Roo.BorderLayout
31164  * @extends Roo.LayoutManager
31165  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31166  * please see: <br><br>
31167  * <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>
31168  * <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>
31169  * Example:
31170  <pre><code>
31171  var layout = new Roo.BorderLayout(document.body, {
31172     north: {
31173         initialSize: 25,
31174         titlebar: false
31175     },
31176     west: {
31177         split:true,
31178         initialSize: 200,
31179         minSize: 175,
31180         maxSize: 400,
31181         titlebar: true,
31182         collapsible: true
31183     },
31184     east: {
31185         split:true,
31186         initialSize: 202,
31187         minSize: 175,
31188         maxSize: 400,
31189         titlebar: true,
31190         collapsible: true
31191     },
31192     south: {
31193         split:true,
31194         initialSize: 100,
31195         minSize: 100,
31196         maxSize: 200,
31197         titlebar: true,
31198         collapsible: true
31199     },
31200     center: {
31201         titlebar: true,
31202         autoScroll:true,
31203         resizeTabs: true,
31204         minTabWidth: 50,
31205         preferredTabWidth: 150
31206     }
31207 });
31208
31209 // shorthand
31210 var CP = Roo.ContentPanel;
31211
31212 layout.beginUpdate();
31213 layout.add("north", new CP("north", "North"));
31214 layout.add("south", new CP("south", {title: "South", closable: true}));
31215 layout.add("west", new CP("west", {title: "West"}));
31216 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31217 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31218 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31219 layout.getRegion("center").showPanel("center1");
31220 layout.endUpdate();
31221 </code></pre>
31222
31223 <b>The container the layout is rendered into can be either the body element or any other element.
31224 If it is not the body element, the container needs to either be an absolute positioned element,
31225 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31226 the container size if it is not the body element.</b>
31227
31228 * @constructor
31229 * Create a new BorderLayout
31230 * @param {String/HTMLElement/Element} container The container this layout is bound to
31231 * @param {Object} config Configuration options
31232  */
31233 Roo.BorderLayout = function(container, config){
31234     config = config || {};
31235     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31236     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31237     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31238         var target = this.factory.validRegions[i];
31239         if(config[target]){
31240             this.addRegion(target, config[target]);
31241         }
31242     }
31243 };
31244
31245 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31246     /**
31247      * Creates and adds a new region if it doesn't already exist.
31248      * @param {String} target The target region key (north, south, east, west or center).
31249      * @param {Object} config The regions config object
31250      * @return {BorderLayoutRegion} The new region
31251      */
31252     addRegion : function(target, config){
31253         if(!this.regions[target]){
31254             var r = this.factory.create(target, this, config);
31255             this.bindRegion(target, r);
31256         }
31257         return this.regions[target];
31258     },
31259
31260     // private (kinda)
31261     bindRegion : function(name, r){
31262         this.regions[name] = r;
31263         r.on("visibilitychange", this.layout, this);
31264         r.on("paneladded", this.layout, this);
31265         r.on("panelremoved", this.layout, this);
31266         r.on("invalidated", this.layout, this);
31267         r.on("resized", this.onRegionResized, this);
31268         r.on("collapsed", this.onRegionCollapsed, this);
31269         r.on("expanded", this.onRegionExpanded, this);
31270     },
31271
31272     /**
31273      * Performs a layout update.
31274      */
31275     layout : function(){
31276         if(this.updating) return;
31277         var size = this.getViewSize();
31278         var w = size.width;
31279         var h = size.height;
31280         var centerW = w;
31281         var centerH = h;
31282         var centerY = 0;
31283         var centerX = 0;
31284         //var x = 0, y = 0;
31285
31286         var rs = this.regions;
31287         var north = rs["north"];
31288         var south = rs["south"]; 
31289         var west = rs["west"];
31290         var east = rs["east"];
31291         var center = rs["center"];
31292         //if(this.hideOnLayout){ // not supported anymore
31293             //c.el.setStyle("display", "none");
31294         //}
31295         if(north && north.isVisible()){
31296             var b = north.getBox();
31297             var m = north.getMargins();
31298             b.width = w - (m.left+m.right);
31299             b.x = m.left;
31300             b.y = m.top;
31301             centerY = b.height + b.y + m.bottom;
31302             centerH -= centerY;
31303             north.updateBox(this.safeBox(b));
31304         }
31305         if(south && south.isVisible()){
31306             var b = south.getBox();
31307             var m = south.getMargins();
31308             b.width = w - (m.left+m.right);
31309             b.x = m.left;
31310             var totalHeight = (b.height + m.top + m.bottom);
31311             b.y = h - totalHeight + m.top;
31312             centerH -= totalHeight;
31313             south.updateBox(this.safeBox(b));
31314         }
31315         if(west && west.isVisible()){
31316             var b = west.getBox();
31317             var m = west.getMargins();
31318             b.height = centerH - (m.top+m.bottom);
31319             b.x = m.left;
31320             b.y = centerY + m.top;
31321             var totalWidth = (b.width + m.left + m.right);
31322             centerX += totalWidth;
31323             centerW -= totalWidth;
31324             west.updateBox(this.safeBox(b));
31325         }
31326         if(east && east.isVisible()){
31327             var b = east.getBox();
31328             var m = east.getMargins();
31329             b.height = centerH - (m.top+m.bottom);
31330             var totalWidth = (b.width + m.left + m.right);
31331             b.x = w - totalWidth + m.left;
31332             b.y = centerY + m.top;
31333             centerW -= totalWidth;
31334             east.updateBox(this.safeBox(b));
31335         }
31336         if(center){
31337             var m = center.getMargins();
31338             var centerBox = {
31339                 x: centerX + m.left,
31340                 y: centerY + m.top,
31341                 width: centerW - (m.left+m.right),
31342                 height: centerH - (m.top+m.bottom)
31343             };
31344             //if(this.hideOnLayout){
31345                 //center.el.setStyle("display", "block");
31346             //}
31347             center.updateBox(this.safeBox(centerBox));
31348         }
31349         this.el.repaint();
31350         this.fireEvent("layout", this);
31351     },
31352
31353     // private
31354     safeBox : function(box){
31355         box.width = Math.max(0, box.width);
31356         box.height = Math.max(0, box.height);
31357         return box;
31358     },
31359
31360     /**
31361      * Adds a ContentPanel (or subclass) to this layout.
31362      * @param {String} target The target region key (north, south, east, west or center).
31363      * @param {Roo.ContentPanel} panel The panel to add
31364      * @return {Roo.ContentPanel} The added panel
31365      */
31366     add : function(target, panel){
31367          
31368         target = target.toLowerCase();
31369         return this.regions[target].add(panel);
31370     },
31371
31372     /**
31373      * Remove a ContentPanel (or subclass) to this layout.
31374      * @param {String} target The target region key (north, south, east, west or center).
31375      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31376      * @return {Roo.ContentPanel} The removed panel
31377      */
31378     remove : function(target, panel){
31379         target = target.toLowerCase();
31380         return this.regions[target].remove(panel);
31381     },
31382
31383     /**
31384      * Searches all regions for a panel with the specified id
31385      * @param {String} panelId
31386      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31387      */
31388     findPanel : function(panelId){
31389         var rs = this.regions;
31390         for(var target in rs){
31391             if(typeof rs[target] != "function"){
31392                 var p = rs[target].getPanel(panelId);
31393                 if(p){
31394                     return p;
31395                 }
31396             }
31397         }
31398         return null;
31399     },
31400
31401     /**
31402      * Searches all regions for a panel with the specified id and activates (shows) it.
31403      * @param {String/ContentPanel} panelId The panels id or the panel itself
31404      * @return {Roo.ContentPanel} The shown panel or null
31405      */
31406     showPanel : function(panelId) {
31407       var rs = this.regions;
31408       for(var target in rs){
31409          var r = rs[target];
31410          if(typeof r != "function"){
31411             if(r.hasPanel(panelId)){
31412                return r.showPanel(panelId);
31413             }
31414          }
31415       }
31416       return null;
31417    },
31418
31419    /**
31420      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31421      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31422      */
31423     restoreState : function(provider){
31424         if(!provider){
31425             provider = Roo.state.Manager;
31426         }
31427         var sm = new Roo.LayoutStateManager();
31428         sm.init(this, provider);
31429     },
31430
31431     /**
31432      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31433      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31434      * a valid ContentPanel config object.  Example:
31435      * <pre><code>
31436 // Create the main layout
31437 var layout = new Roo.BorderLayout('main-ct', {
31438     west: {
31439         split:true,
31440         minSize: 175,
31441         titlebar: true
31442     },
31443     center: {
31444         title:'Components'
31445     }
31446 }, 'main-ct');
31447
31448 // Create and add multiple ContentPanels at once via configs
31449 layout.batchAdd({
31450    west: {
31451        id: 'source-files',
31452        autoCreate:true,
31453        title:'Ext Source Files',
31454        autoScroll:true,
31455        fitToFrame:true
31456    },
31457    center : {
31458        el: cview,
31459        autoScroll:true,
31460        fitToFrame:true,
31461        toolbar: tb,
31462        resizeEl:'cbody'
31463    }
31464 });
31465 </code></pre>
31466      * @param {Object} regions An object containing ContentPanel configs by region name
31467      */
31468     batchAdd : function(regions){
31469         this.beginUpdate();
31470         for(var rname in regions){
31471             var lr = this.regions[rname];
31472             if(lr){
31473                 this.addTypedPanels(lr, regions[rname]);
31474             }
31475         }
31476         this.endUpdate();
31477     },
31478
31479     // private
31480     addTypedPanels : function(lr, ps){
31481         if(typeof ps == 'string'){
31482             lr.add(new Roo.ContentPanel(ps));
31483         }
31484         else if(ps instanceof Array){
31485             for(var i =0, len = ps.length; i < len; i++){
31486                 this.addTypedPanels(lr, ps[i]);
31487             }
31488         }
31489         else if(!ps.events){ // raw config?
31490             var el = ps.el;
31491             delete ps.el; // prevent conflict
31492             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31493         }
31494         else {  // panel object assumed!
31495             lr.add(ps);
31496         }
31497     },
31498     /**
31499      * Adds a xtype elements to the layout.
31500      * <pre><code>
31501
31502 layout.addxtype({
31503        xtype : 'ContentPanel',
31504        region: 'west',
31505        items: [ .... ]
31506    }
31507 );
31508
31509 layout.addxtype({
31510         xtype : 'NestedLayoutPanel',
31511         region: 'west',
31512         layout: {
31513            center: { },
31514            west: { }   
31515         },
31516         items : [ ... list of content panels or nested layout panels.. ]
31517    }
31518 );
31519 </code></pre>
31520      * @param {Object} cfg Xtype definition of item to add.
31521      */
31522     addxtype : function(cfg)
31523     {
31524         // basically accepts a pannel...
31525         // can accept a layout region..!?!?
31526         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31527         
31528         if (!cfg.xtype.match(/Panel$/)) {
31529             return false;
31530         }
31531         var ret = false;
31532         
31533         if (typeof(cfg.region) == 'undefined') {
31534             Roo.log("Failed to add Panel, region was not set");
31535             Roo.log(cfg);
31536             return false;
31537         }
31538         var region = cfg.region;
31539         delete cfg.region;
31540         
31541           
31542         var xitems = [];
31543         if (cfg.items) {
31544             xitems = cfg.items;
31545             delete cfg.items;
31546         }
31547         var nb = false;
31548         
31549         switch(cfg.xtype) 
31550         {
31551             case 'ContentPanel':  // ContentPanel (el, cfg)
31552             case 'ScrollPanel':  // ContentPanel (el, cfg)
31553                 if(cfg.autoCreate) {
31554                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31555                 } else {
31556                     var el = this.el.createChild();
31557                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31558                 }
31559                 
31560                 this.add(region, ret);
31561                 break;
31562             
31563             
31564             case 'TreePanel': // our new panel!
31565                 cfg.el = this.el.createChild();
31566                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31567                 this.add(region, ret);
31568                 break;
31569             
31570             case 'NestedLayoutPanel': 
31571                 // create a new Layout (which is  a Border Layout...
31572                 var el = this.el.createChild();
31573                 var clayout = cfg.layout;
31574                 delete cfg.layout;
31575                 clayout.items   = clayout.items  || [];
31576                 // replace this exitems with the clayout ones..
31577                 xitems = clayout.items;
31578                  
31579                 
31580                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31581                     cfg.background = false;
31582                 }
31583                 var layout = new Roo.BorderLayout(el, clayout);
31584                 
31585                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31586                 //console.log('adding nested layout panel '  + cfg.toSource());
31587                 this.add(region, ret);
31588                 nb = {}; /// find first...
31589                 break;
31590                 
31591             case 'GridPanel': 
31592             
31593                 // needs grid and region
31594                 
31595                 //var el = this.getRegion(region).el.createChild();
31596                 var el = this.el.createChild();
31597                 // create the grid first...
31598                 
31599                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31600                 delete cfg.grid;
31601                 if (region == 'center' && this.active ) {
31602                     cfg.background = false;
31603                 }
31604                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31605                 
31606                 this.add(region, ret);
31607                 if (cfg.background) {
31608                     ret.on('activate', function(gp) {
31609                         if (!gp.grid.rendered) {
31610                             gp.grid.render();
31611                         }
31612                     });
31613                 } else {
31614                     grid.render();
31615                 }
31616                 break;
31617            
31618                
31619                 
31620                 
31621             default: 
31622                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31623                 return null;
31624              // GridPanel (grid, cfg)
31625             
31626         }
31627         this.beginUpdate();
31628         // add children..
31629         var region = '';
31630         var abn = {};
31631         Roo.each(xitems, function(i)  {
31632             region = nb && i.region ? i.region : false;
31633             
31634             var add = ret.addxtype(i);
31635            
31636             if (region) {
31637                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31638                 if (!i.background) {
31639                     abn[region] = nb[region] ;
31640                 }
31641             }
31642             
31643         });
31644         this.endUpdate();
31645
31646         // make the last non-background panel active..
31647         //if (nb) { Roo.log(abn); }
31648         if (nb) {
31649             
31650             for(var r in abn) {
31651                 region = this.getRegion(r);
31652                 if (region) {
31653                     // tried using nb[r], but it does not work..
31654                      
31655                     region.showPanel(abn[r]);
31656                    
31657                 }
31658             }
31659         }
31660         return ret;
31661         
31662     }
31663 });
31664
31665 /**
31666  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31667  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31668  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31669  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31670  * <pre><code>
31671 // shorthand
31672 var CP = Roo.ContentPanel;
31673
31674 var layout = Roo.BorderLayout.create({
31675     north: {
31676         initialSize: 25,
31677         titlebar: false,
31678         panels: [new CP("north", "North")]
31679     },
31680     west: {
31681         split:true,
31682         initialSize: 200,
31683         minSize: 175,
31684         maxSize: 400,
31685         titlebar: true,
31686         collapsible: true,
31687         panels: [new CP("west", {title: "West"})]
31688     },
31689     east: {
31690         split:true,
31691         initialSize: 202,
31692         minSize: 175,
31693         maxSize: 400,
31694         titlebar: true,
31695         collapsible: true,
31696         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31697     },
31698     south: {
31699         split:true,
31700         initialSize: 100,
31701         minSize: 100,
31702         maxSize: 200,
31703         titlebar: true,
31704         collapsible: true,
31705         panels: [new CP("south", {title: "South", closable: true})]
31706     },
31707     center: {
31708         titlebar: true,
31709         autoScroll:true,
31710         resizeTabs: true,
31711         minTabWidth: 50,
31712         preferredTabWidth: 150,
31713         panels: [
31714             new CP("center1", {title: "Close Me", closable: true}),
31715             new CP("center2", {title: "Center Panel", closable: false})
31716         ]
31717     }
31718 }, document.body);
31719
31720 layout.getRegion("center").showPanel("center1");
31721 </code></pre>
31722  * @param config
31723  * @param targetEl
31724  */
31725 Roo.BorderLayout.create = function(config, targetEl){
31726     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31727     layout.beginUpdate();
31728     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31729     for(var j = 0, jlen = regions.length; j < jlen; j++){
31730         var lr = regions[j];
31731         if(layout.regions[lr] && config[lr].panels){
31732             var r = layout.regions[lr];
31733             var ps = config[lr].panels;
31734             layout.addTypedPanels(r, ps);
31735         }
31736     }
31737     layout.endUpdate();
31738     return layout;
31739 };
31740
31741 // private
31742 Roo.BorderLayout.RegionFactory = {
31743     // private
31744     validRegions : ["north","south","east","west","center"],
31745
31746     // private
31747     create : function(target, mgr, config){
31748         target = target.toLowerCase();
31749         if(config.lightweight || config.basic){
31750             return new Roo.BasicLayoutRegion(mgr, config, target);
31751         }
31752         switch(target){
31753             case "north":
31754                 return new Roo.NorthLayoutRegion(mgr, config);
31755             case "south":
31756                 return new Roo.SouthLayoutRegion(mgr, config);
31757             case "east":
31758                 return new Roo.EastLayoutRegion(mgr, config);
31759             case "west":
31760                 return new Roo.WestLayoutRegion(mgr, config);
31761             case "center":
31762                 return new Roo.CenterLayoutRegion(mgr, config);
31763         }
31764         throw 'Layout region "'+target+'" not supported.';
31765     }
31766 };/*
31767  * Based on:
31768  * Ext JS Library 1.1.1
31769  * Copyright(c) 2006-2007, Ext JS, LLC.
31770  *
31771  * Originally Released Under LGPL - original licence link has changed is not relivant.
31772  *
31773  * Fork - LGPL
31774  * <script type="text/javascript">
31775  */
31776  
31777 /**
31778  * @class Roo.BasicLayoutRegion
31779  * @extends Roo.util.Observable
31780  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31781  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31782  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31783  */
31784 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31785     this.mgr = mgr;
31786     this.position  = pos;
31787     this.events = {
31788         /**
31789          * @scope Roo.BasicLayoutRegion
31790          */
31791         
31792         /**
31793          * @event beforeremove
31794          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31795          * @param {Roo.LayoutRegion} this
31796          * @param {Roo.ContentPanel} panel The panel
31797          * @param {Object} e The cancel event object
31798          */
31799         "beforeremove" : true,
31800         /**
31801          * @event invalidated
31802          * Fires when the layout for this region is changed.
31803          * @param {Roo.LayoutRegion} this
31804          */
31805         "invalidated" : true,
31806         /**
31807          * @event visibilitychange
31808          * Fires when this region is shown or hidden 
31809          * @param {Roo.LayoutRegion} this
31810          * @param {Boolean} visibility true or false
31811          */
31812         "visibilitychange" : true,
31813         /**
31814          * @event paneladded
31815          * Fires when a panel is added. 
31816          * @param {Roo.LayoutRegion} this
31817          * @param {Roo.ContentPanel} panel The panel
31818          */
31819         "paneladded" : true,
31820         /**
31821          * @event panelremoved
31822          * Fires when a panel is removed. 
31823          * @param {Roo.LayoutRegion} this
31824          * @param {Roo.ContentPanel} panel The panel
31825          */
31826         "panelremoved" : true,
31827         /**
31828          * @event collapsed
31829          * Fires when this region is collapsed.
31830          * @param {Roo.LayoutRegion} this
31831          */
31832         "collapsed" : true,
31833         /**
31834          * @event expanded
31835          * Fires when this region is expanded.
31836          * @param {Roo.LayoutRegion} this
31837          */
31838         "expanded" : true,
31839         /**
31840          * @event slideshow
31841          * Fires when this region is slid into view.
31842          * @param {Roo.LayoutRegion} this
31843          */
31844         "slideshow" : true,
31845         /**
31846          * @event slidehide
31847          * Fires when this region slides out of view. 
31848          * @param {Roo.LayoutRegion} this
31849          */
31850         "slidehide" : true,
31851         /**
31852          * @event panelactivated
31853          * Fires when a panel is activated. 
31854          * @param {Roo.LayoutRegion} this
31855          * @param {Roo.ContentPanel} panel The activated panel
31856          */
31857         "panelactivated" : true,
31858         /**
31859          * @event resized
31860          * Fires when the user resizes this region. 
31861          * @param {Roo.LayoutRegion} this
31862          * @param {Number} newSize The new size (width for east/west, height for north/south)
31863          */
31864         "resized" : true
31865     };
31866     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31867     this.panels = new Roo.util.MixedCollection();
31868     this.panels.getKey = this.getPanelId.createDelegate(this);
31869     this.box = null;
31870     this.activePanel = null;
31871     // ensure listeners are added...
31872     
31873     if (config.listeners || config.events) {
31874         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31875             listeners : config.listeners || {},
31876             events : config.events || {}
31877         });
31878     }
31879     
31880     if(skipConfig !== true){
31881         this.applyConfig(config);
31882     }
31883 };
31884
31885 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31886     getPanelId : function(p){
31887         return p.getId();
31888     },
31889     
31890     applyConfig : function(config){
31891         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31892         this.config = config;
31893         
31894     },
31895     
31896     /**
31897      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31898      * the width, for horizontal (north, south) the height.
31899      * @param {Number} newSize The new width or height
31900      */
31901     resizeTo : function(newSize){
31902         var el = this.el ? this.el :
31903                  (this.activePanel ? this.activePanel.getEl() : null);
31904         if(el){
31905             switch(this.position){
31906                 case "east":
31907                 case "west":
31908                     el.setWidth(newSize);
31909                     this.fireEvent("resized", this, newSize);
31910                 break;
31911                 case "north":
31912                 case "south":
31913                     el.setHeight(newSize);
31914                     this.fireEvent("resized", this, newSize);
31915                 break;                
31916             }
31917         }
31918     },
31919     
31920     getBox : function(){
31921         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31922     },
31923     
31924     getMargins : function(){
31925         return this.margins;
31926     },
31927     
31928     updateBox : function(box){
31929         this.box = box;
31930         var el = this.activePanel.getEl();
31931         el.dom.style.left = box.x + "px";
31932         el.dom.style.top = box.y + "px";
31933         this.activePanel.setSize(box.width, box.height);
31934     },
31935     
31936     /**
31937      * Returns the container element for this region.
31938      * @return {Roo.Element}
31939      */
31940     getEl : function(){
31941         return this.activePanel;
31942     },
31943     
31944     /**
31945      * Returns true if this region is currently visible.
31946      * @return {Boolean}
31947      */
31948     isVisible : function(){
31949         return this.activePanel ? true : false;
31950     },
31951     
31952     setActivePanel : function(panel){
31953         panel = this.getPanel(panel);
31954         if(this.activePanel && this.activePanel != panel){
31955             this.activePanel.setActiveState(false);
31956             this.activePanel.getEl().setLeftTop(-10000,-10000);
31957         }
31958         this.activePanel = panel;
31959         panel.setActiveState(true);
31960         if(this.box){
31961             panel.setSize(this.box.width, this.box.height);
31962         }
31963         this.fireEvent("panelactivated", this, panel);
31964         this.fireEvent("invalidated");
31965     },
31966     
31967     /**
31968      * Show the specified panel.
31969      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31970      * @return {Roo.ContentPanel} The shown panel or null
31971      */
31972     showPanel : function(panel){
31973         if(panel = this.getPanel(panel)){
31974             this.setActivePanel(panel);
31975         }
31976         return panel;
31977     },
31978     
31979     /**
31980      * Get the active panel for this region.
31981      * @return {Roo.ContentPanel} The active panel or null
31982      */
31983     getActivePanel : function(){
31984         return this.activePanel;
31985     },
31986     
31987     /**
31988      * Add the passed ContentPanel(s)
31989      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31990      * @return {Roo.ContentPanel} The panel added (if only one was added)
31991      */
31992     add : function(panel){
31993         if(arguments.length > 1){
31994             for(var i = 0, len = arguments.length; i < len; i++) {
31995                 this.add(arguments[i]);
31996             }
31997             return null;
31998         }
31999         if(this.hasPanel(panel)){
32000             this.showPanel(panel);
32001             return panel;
32002         }
32003         var el = panel.getEl();
32004         if(el.dom.parentNode != this.mgr.el.dom){
32005             this.mgr.el.dom.appendChild(el.dom);
32006         }
32007         if(panel.setRegion){
32008             panel.setRegion(this);
32009         }
32010         this.panels.add(panel);
32011         el.setStyle("position", "absolute");
32012         if(!panel.background){
32013             this.setActivePanel(panel);
32014             if(this.config.initialSize && this.panels.getCount()==1){
32015                 this.resizeTo(this.config.initialSize);
32016             }
32017         }
32018         this.fireEvent("paneladded", this, panel);
32019         return panel;
32020     },
32021     
32022     /**
32023      * Returns true if the panel is in this region.
32024      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32025      * @return {Boolean}
32026      */
32027     hasPanel : function(panel){
32028         if(typeof panel == "object"){ // must be panel obj
32029             panel = panel.getId();
32030         }
32031         return this.getPanel(panel) ? true : false;
32032     },
32033     
32034     /**
32035      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32036      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32037      * @param {Boolean} preservePanel Overrides the config preservePanel option
32038      * @return {Roo.ContentPanel} The panel that was removed
32039      */
32040     remove : function(panel, preservePanel){
32041         panel = this.getPanel(panel);
32042         if(!panel){
32043             return null;
32044         }
32045         var e = {};
32046         this.fireEvent("beforeremove", this, panel, e);
32047         if(e.cancel === true){
32048             return null;
32049         }
32050         var panelId = panel.getId();
32051         this.panels.removeKey(panelId);
32052         return panel;
32053     },
32054     
32055     /**
32056      * Returns the panel specified or null if it's not in this region.
32057      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32058      * @return {Roo.ContentPanel}
32059      */
32060     getPanel : function(id){
32061         if(typeof id == "object"){ // must be panel obj
32062             return id;
32063         }
32064         return this.panels.get(id);
32065     },
32066     
32067     /**
32068      * Returns this regions position (north/south/east/west/center).
32069      * @return {String} 
32070      */
32071     getPosition: function(){
32072         return this.position;    
32073     }
32074 });/*
32075  * Based on:
32076  * Ext JS Library 1.1.1
32077  * Copyright(c) 2006-2007, Ext JS, LLC.
32078  *
32079  * Originally Released Under LGPL - original licence link has changed is not relivant.
32080  *
32081  * Fork - LGPL
32082  * <script type="text/javascript">
32083  */
32084  
32085 /**
32086  * @class Roo.LayoutRegion
32087  * @extends Roo.BasicLayoutRegion
32088  * This class represents a region in a layout manager.
32089  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
32090  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
32091  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
32092  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32093  * @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})
32094  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
32095  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
32096  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32097  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32098  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32099  * @cfg {String}    title           The title for the region (overrides panel titles)
32100  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32101  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32102  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32103  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32104  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32105  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32106  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32107  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32108  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32109  * @cfg {Boolean}   showPin         True to show a pin button
32110  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32111  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32112  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32113  * @cfg {Number}    width           For East/West panels
32114  * @cfg {Number}    height          For North/South panels
32115  * @cfg {Boolean}   split           To show the splitter
32116  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32117  */
32118 Roo.LayoutRegion = function(mgr, config, pos){
32119     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
32120     var dh = Roo.DomHelper;
32121     /** This region's container element 
32122     * @type Roo.Element */
32123     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
32124     /** This region's title element 
32125     * @type Roo.Element */
32126
32127     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
32128         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32129         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
32130     ]}, true);
32131     this.titleEl.enableDisplayMode();
32132     /** This region's title text element 
32133     * @type HTMLElement */
32134     this.titleTextEl = this.titleEl.dom.firstChild;
32135     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32136     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
32137     this.closeBtn.enableDisplayMode();
32138     this.closeBtn.on("click", this.closeClicked, this);
32139     this.closeBtn.hide();
32140
32141     this.createBody(config);
32142     this.visible = true;
32143     this.collapsed = false;
32144
32145     if(config.hideWhenEmpty){
32146         this.hide();
32147         this.on("paneladded", this.validateVisibility, this);
32148         this.on("panelremoved", this.validateVisibility, this);
32149     }
32150     this.applyConfig(config);
32151 };
32152
32153 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
32154
32155     createBody : function(){
32156         /** This region's body element 
32157         * @type Roo.Element */
32158         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
32159     },
32160
32161     applyConfig : function(c){
32162         if(c.collapsible && this.position != "center" && !this.collapsedEl){
32163             var dh = Roo.DomHelper;
32164             if(c.titlebar !== false){
32165                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
32166                 this.collapseBtn.on("click", this.collapse, this);
32167                 this.collapseBtn.enableDisplayMode();
32168
32169                 if(c.showPin === true || this.showPin){
32170                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
32171                     this.stickBtn.enableDisplayMode();
32172                     this.stickBtn.on("click", this.expand, this);
32173                     this.stickBtn.hide();
32174                 }
32175             }
32176             /** This region's collapsed element
32177             * @type Roo.Element */
32178             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32179                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32180             ]}, true);
32181             if(c.floatable !== false){
32182                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32183                this.collapsedEl.on("click", this.collapseClick, this);
32184             }
32185
32186             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32187                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32188                    id: "message", unselectable: "on", style:{"float":"left"}});
32189                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32190              }
32191             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32192             this.expandBtn.on("click", this.expand, this);
32193         }
32194         if(this.collapseBtn){
32195             this.collapseBtn.setVisible(c.collapsible == true);
32196         }
32197         this.cmargins = c.cmargins || this.cmargins ||
32198                          (this.position == "west" || this.position == "east" ?
32199                              {top: 0, left: 2, right:2, bottom: 0} :
32200                              {top: 2, left: 0, right:0, bottom: 2});
32201         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32202         this.bottomTabs = c.tabPosition != "top";
32203         this.autoScroll = c.autoScroll || false;
32204         if(this.autoScroll){
32205             this.bodyEl.setStyle("overflow", "auto");
32206         }else{
32207             this.bodyEl.setStyle("overflow", "hidden");
32208         }
32209         //if(c.titlebar !== false){
32210             if((!c.titlebar && !c.title) || c.titlebar === false){
32211                 this.titleEl.hide();
32212             }else{
32213                 this.titleEl.show();
32214                 if(c.title){
32215                     this.titleTextEl.innerHTML = c.title;
32216                 }
32217             }
32218         //}
32219         this.duration = c.duration || .30;
32220         this.slideDuration = c.slideDuration || .45;
32221         this.config = c;
32222         if(c.collapsed){
32223             this.collapse(true);
32224         }
32225         if(c.hidden){
32226             this.hide();
32227         }
32228     },
32229     /**
32230      * Returns true if this region is currently visible.
32231      * @return {Boolean}
32232      */
32233     isVisible : function(){
32234         return this.visible;
32235     },
32236
32237     /**
32238      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32239      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32240      */
32241     setCollapsedTitle : function(title){
32242         title = title || "&#160;";
32243         if(this.collapsedTitleTextEl){
32244             this.collapsedTitleTextEl.innerHTML = title;
32245         }
32246     },
32247
32248     getBox : function(){
32249         var b;
32250         if(!this.collapsed){
32251             b = this.el.getBox(false, true);
32252         }else{
32253             b = this.collapsedEl.getBox(false, true);
32254         }
32255         return b;
32256     },
32257
32258     getMargins : function(){
32259         return this.collapsed ? this.cmargins : this.margins;
32260     },
32261
32262     highlight : function(){
32263         this.el.addClass("x-layout-panel-dragover");
32264     },
32265
32266     unhighlight : function(){
32267         this.el.removeClass("x-layout-panel-dragover");
32268     },
32269
32270     updateBox : function(box){
32271         this.box = box;
32272         if(!this.collapsed){
32273             this.el.dom.style.left = box.x + "px";
32274             this.el.dom.style.top = box.y + "px";
32275             this.updateBody(box.width, box.height);
32276         }else{
32277             this.collapsedEl.dom.style.left = box.x + "px";
32278             this.collapsedEl.dom.style.top = box.y + "px";
32279             this.collapsedEl.setSize(box.width, box.height);
32280         }
32281         if(this.tabs){
32282             this.tabs.autoSizeTabs();
32283         }
32284     },
32285
32286     updateBody : function(w, h){
32287         if(w !== null){
32288             this.el.setWidth(w);
32289             w -= this.el.getBorderWidth("rl");
32290             if(this.config.adjustments){
32291                 w += this.config.adjustments[0];
32292             }
32293         }
32294         if(h !== null){
32295             this.el.setHeight(h);
32296             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32297             h -= this.el.getBorderWidth("tb");
32298             if(this.config.adjustments){
32299                 h += this.config.adjustments[1];
32300             }
32301             this.bodyEl.setHeight(h);
32302             if(this.tabs){
32303                 h = this.tabs.syncHeight(h);
32304             }
32305         }
32306         if(this.panelSize){
32307             w = w !== null ? w : this.panelSize.width;
32308             h = h !== null ? h : this.panelSize.height;
32309         }
32310         if(this.activePanel){
32311             var el = this.activePanel.getEl();
32312             w = w !== null ? w : el.getWidth();
32313             h = h !== null ? h : el.getHeight();
32314             this.panelSize = {width: w, height: h};
32315             this.activePanel.setSize(w, h);
32316         }
32317         if(Roo.isIE && this.tabs){
32318             this.tabs.el.repaint();
32319         }
32320     },
32321
32322     /**
32323      * Returns the container element for this region.
32324      * @return {Roo.Element}
32325      */
32326     getEl : function(){
32327         return this.el;
32328     },
32329
32330     /**
32331      * Hides this region.
32332      */
32333     hide : function(){
32334         if(!this.collapsed){
32335             this.el.dom.style.left = "-2000px";
32336             this.el.hide();
32337         }else{
32338             this.collapsedEl.dom.style.left = "-2000px";
32339             this.collapsedEl.hide();
32340         }
32341         this.visible = false;
32342         this.fireEvent("visibilitychange", this, false);
32343     },
32344
32345     /**
32346      * Shows this region if it was previously hidden.
32347      */
32348     show : function(){
32349         if(!this.collapsed){
32350             this.el.show();
32351         }else{
32352             this.collapsedEl.show();
32353         }
32354         this.visible = true;
32355         this.fireEvent("visibilitychange", this, true);
32356     },
32357
32358     closeClicked : function(){
32359         if(this.activePanel){
32360             this.remove(this.activePanel);
32361         }
32362     },
32363
32364     collapseClick : function(e){
32365         if(this.isSlid){
32366            e.stopPropagation();
32367            this.slideIn();
32368         }else{
32369            e.stopPropagation();
32370            this.slideOut();
32371         }
32372     },
32373
32374     /**
32375      * Collapses this region.
32376      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32377      */
32378     collapse : function(skipAnim){
32379         if(this.collapsed) return;
32380         this.collapsed = true;
32381         if(this.split){
32382             this.split.el.hide();
32383         }
32384         if(this.config.animate && skipAnim !== true){
32385             this.fireEvent("invalidated", this);
32386             this.animateCollapse();
32387         }else{
32388             this.el.setLocation(-20000,-20000);
32389             this.el.hide();
32390             this.collapsedEl.show();
32391             this.fireEvent("collapsed", this);
32392             this.fireEvent("invalidated", this);
32393         }
32394     },
32395
32396     animateCollapse : function(){
32397         // overridden
32398     },
32399
32400     /**
32401      * Expands this region if it was previously collapsed.
32402      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32403      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32404      */
32405     expand : function(e, skipAnim){
32406         if(e) e.stopPropagation();
32407         if(!this.collapsed || this.el.hasActiveFx()) return;
32408         if(this.isSlid){
32409             this.afterSlideIn();
32410             skipAnim = true;
32411         }
32412         this.collapsed = false;
32413         if(this.config.animate && skipAnim !== true){
32414             this.animateExpand();
32415         }else{
32416             this.el.show();
32417             if(this.split){
32418                 this.split.el.show();
32419             }
32420             this.collapsedEl.setLocation(-2000,-2000);
32421             this.collapsedEl.hide();
32422             this.fireEvent("invalidated", this);
32423             this.fireEvent("expanded", this);
32424         }
32425     },
32426
32427     animateExpand : function(){
32428         // overridden
32429     },
32430
32431     initTabs : function()
32432     {
32433         this.bodyEl.setStyle("overflow", "hidden");
32434         var ts = new Roo.TabPanel(
32435                 this.bodyEl.dom,
32436                 {
32437                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32438                     disableTooltips: this.config.disableTabTips,
32439                     toolbar : this.config.toolbar
32440                 }
32441         );
32442         if(this.config.hideTabs){
32443             ts.stripWrap.setDisplayed(false);
32444         }
32445         this.tabs = ts;
32446         ts.resizeTabs = this.config.resizeTabs === true;
32447         ts.minTabWidth = this.config.minTabWidth || 40;
32448         ts.maxTabWidth = this.config.maxTabWidth || 250;
32449         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32450         ts.monitorResize = false;
32451         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32452         ts.bodyEl.addClass('x-layout-tabs-body');
32453         this.panels.each(this.initPanelAsTab, this);
32454     },
32455
32456     initPanelAsTab : function(panel){
32457         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32458                     this.config.closeOnTab && panel.isClosable());
32459         if(panel.tabTip !== undefined){
32460             ti.setTooltip(panel.tabTip);
32461         }
32462         ti.on("activate", function(){
32463               this.setActivePanel(panel);
32464         }, this);
32465         if(this.config.closeOnTab){
32466             ti.on("beforeclose", function(t, e){
32467                 e.cancel = true;
32468                 this.remove(panel);
32469             }, this);
32470         }
32471         return ti;
32472     },
32473
32474     updatePanelTitle : function(panel, title){
32475         if(this.activePanel == panel){
32476             this.updateTitle(title);
32477         }
32478         if(this.tabs){
32479             var ti = this.tabs.getTab(panel.getEl().id);
32480             ti.setText(title);
32481             if(panel.tabTip !== undefined){
32482                 ti.setTooltip(panel.tabTip);
32483             }
32484         }
32485     },
32486
32487     updateTitle : function(title){
32488         if(this.titleTextEl && !this.config.title){
32489             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32490         }
32491     },
32492
32493     setActivePanel : function(panel){
32494         panel = this.getPanel(panel);
32495         if(this.activePanel && this.activePanel != panel){
32496             this.activePanel.setActiveState(false);
32497         }
32498         this.activePanel = panel;
32499         panel.setActiveState(true);
32500         if(this.panelSize){
32501             panel.setSize(this.panelSize.width, this.panelSize.height);
32502         }
32503         if(this.closeBtn){
32504             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32505         }
32506         this.updateTitle(panel.getTitle());
32507         if(this.tabs){
32508             this.fireEvent("invalidated", this);
32509         }
32510         this.fireEvent("panelactivated", this, panel);
32511     },
32512
32513     /**
32514      * Shows the specified panel.
32515      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32516      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32517      */
32518     showPanel : function(panel){
32519         if(panel = this.getPanel(panel)){
32520             if(this.tabs){
32521                 var tab = this.tabs.getTab(panel.getEl().id);
32522                 if(tab.isHidden()){
32523                     this.tabs.unhideTab(tab.id);
32524                 }
32525                 tab.activate();
32526             }else{
32527                 this.setActivePanel(panel);
32528             }
32529         }
32530         return panel;
32531     },
32532
32533     /**
32534      * Get the active panel for this region.
32535      * @return {Roo.ContentPanel} The active panel or null
32536      */
32537     getActivePanel : function(){
32538         return this.activePanel;
32539     },
32540
32541     validateVisibility : function(){
32542         if(this.panels.getCount() < 1){
32543             this.updateTitle("&#160;");
32544             this.closeBtn.hide();
32545             this.hide();
32546         }else{
32547             if(!this.isVisible()){
32548                 this.show();
32549             }
32550         }
32551     },
32552
32553     /**
32554      * Adds the passed ContentPanel(s) to this region.
32555      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32556      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32557      */
32558     add : function(panel){
32559         if(arguments.length > 1){
32560             for(var i = 0, len = arguments.length; i < len; i++) {
32561                 this.add(arguments[i]);
32562             }
32563             return null;
32564         }
32565         if(this.hasPanel(panel)){
32566             this.showPanel(panel);
32567             return panel;
32568         }
32569         panel.setRegion(this);
32570         this.panels.add(panel);
32571         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32572             this.bodyEl.dom.appendChild(panel.getEl().dom);
32573             if(panel.background !== true){
32574                 this.setActivePanel(panel);
32575             }
32576             this.fireEvent("paneladded", this, panel);
32577             return panel;
32578         }
32579         if(!this.tabs){
32580             this.initTabs();
32581         }else{
32582             this.initPanelAsTab(panel);
32583         }
32584         if(panel.background !== true){
32585             this.tabs.activate(panel.getEl().id);
32586         }
32587         this.fireEvent("paneladded", this, panel);
32588         return panel;
32589     },
32590
32591     /**
32592      * Hides the tab for the specified panel.
32593      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32594      */
32595     hidePanel : function(panel){
32596         if(this.tabs && (panel = this.getPanel(panel))){
32597             this.tabs.hideTab(panel.getEl().id);
32598         }
32599     },
32600
32601     /**
32602      * Unhides the tab for a previously hidden panel.
32603      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32604      */
32605     unhidePanel : function(panel){
32606         if(this.tabs && (panel = this.getPanel(panel))){
32607             this.tabs.unhideTab(panel.getEl().id);
32608         }
32609     },
32610
32611     clearPanels : function(){
32612         while(this.panels.getCount() > 0){
32613              this.remove(this.panels.first());
32614         }
32615     },
32616
32617     /**
32618      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32619      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32620      * @param {Boolean} preservePanel Overrides the config preservePanel option
32621      * @return {Roo.ContentPanel} The panel that was removed
32622      */
32623     remove : function(panel, preservePanel){
32624         panel = this.getPanel(panel);
32625         if(!panel){
32626             return null;
32627         }
32628         var e = {};
32629         this.fireEvent("beforeremove", this, panel, e);
32630         if(e.cancel === true){
32631             return null;
32632         }
32633         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32634         var panelId = panel.getId();
32635         this.panels.removeKey(panelId);
32636         if(preservePanel){
32637             document.body.appendChild(panel.getEl().dom);
32638         }
32639         if(this.tabs){
32640             this.tabs.removeTab(panel.getEl().id);
32641         }else if (!preservePanel){
32642             this.bodyEl.dom.removeChild(panel.getEl().dom);
32643         }
32644         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32645             var p = this.panels.first();
32646             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32647             tempEl.appendChild(p.getEl().dom);
32648             this.bodyEl.update("");
32649             this.bodyEl.dom.appendChild(p.getEl().dom);
32650             tempEl = null;
32651             this.updateTitle(p.getTitle());
32652             this.tabs = null;
32653             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32654             this.setActivePanel(p);
32655         }
32656         panel.setRegion(null);
32657         if(this.activePanel == panel){
32658             this.activePanel = null;
32659         }
32660         if(this.config.autoDestroy !== false && preservePanel !== true){
32661             try{panel.destroy();}catch(e){}
32662         }
32663         this.fireEvent("panelremoved", this, panel);
32664         return panel;
32665     },
32666
32667     /**
32668      * Returns the TabPanel component used by this region
32669      * @return {Roo.TabPanel}
32670      */
32671     getTabs : function(){
32672         return this.tabs;
32673     },
32674
32675     createTool : function(parentEl, className){
32676         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32677             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32678         btn.addClassOnOver("x-layout-tools-button-over");
32679         return btn;
32680     }
32681 });/*
32682  * Based on:
32683  * Ext JS Library 1.1.1
32684  * Copyright(c) 2006-2007, Ext JS, LLC.
32685  *
32686  * Originally Released Under LGPL - original licence link has changed is not relivant.
32687  *
32688  * Fork - LGPL
32689  * <script type="text/javascript">
32690  */
32691  
32692
32693
32694 /**
32695  * @class Roo.SplitLayoutRegion
32696  * @extends Roo.LayoutRegion
32697  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32698  */
32699 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32700     this.cursor = cursor;
32701     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32702 };
32703
32704 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32705     splitTip : "Drag to resize.",
32706     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32707     useSplitTips : false,
32708
32709     applyConfig : function(config){
32710         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32711         if(config.split){
32712             if(!this.split){
32713                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32714                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32715                 /** The SplitBar for this region 
32716                 * @type Roo.SplitBar */
32717                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32718                 this.split.on("moved", this.onSplitMove, this);
32719                 this.split.useShim = config.useShim === true;
32720                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32721                 if(this.useSplitTips){
32722                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32723                 }
32724                 if(config.collapsible){
32725                     this.split.el.on("dblclick", this.collapse,  this);
32726                 }
32727             }
32728             if(typeof config.minSize != "undefined"){
32729                 this.split.minSize = config.minSize;
32730             }
32731             if(typeof config.maxSize != "undefined"){
32732                 this.split.maxSize = config.maxSize;
32733             }
32734             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32735                 this.hideSplitter();
32736             }
32737         }
32738     },
32739
32740     getHMaxSize : function(){
32741          var cmax = this.config.maxSize || 10000;
32742          var center = this.mgr.getRegion("center");
32743          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32744     },
32745
32746     getVMaxSize : function(){
32747          var cmax = this.config.maxSize || 10000;
32748          var center = this.mgr.getRegion("center");
32749          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32750     },
32751
32752     onSplitMove : function(split, newSize){
32753         this.fireEvent("resized", this, newSize);
32754     },
32755     
32756     /** 
32757      * Returns the {@link Roo.SplitBar} for this region.
32758      * @return {Roo.SplitBar}
32759      */
32760     getSplitBar : function(){
32761         return this.split;
32762     },
32763     
32764     hide : function(){
32765         this.hideSplitter();
32766         Roo.SplitLayoutRegion.superclass.hide.call(this);
32767     },
32768
32769     hideSplitter : function(){
32770         if(this.split){
32771             this.split.el.setLocation(-2000,-2000);
32772             this.split.el.hide();
32773         }
32774     },
32775
32776     show : function(){
32777         if(this.split){
32778             this.split.el.show();
32779         }
32780         Roo.SplitLayoutRegion.superclass.show.call(this);
32781     },
32782     
32783     beforeSlide: function(){
32784         if(Roo.isGecko){// firefox overflow auto bug workaround
32785             this.bodyEl.clip();
32786             if(this.tabs) this.tabs.bodyEl.clip();
32787             if(this.activePanel){
32788                 this.activePanel.getEl().clip();
32789                 
32790                 if(this.activePanel.beforeSlide){
32791                     this.activePanel.beforeSlide();
32792                 }
32793             }
32794         }
32795     },
32796     
32797     afterSlide : function(){
32798         if(Roo.isGecko){// firefox overflow auto bug workaround
32799             this.bodyEl.unclip();
32800             if(this.tabs) this.tabs.bodyEl.unclip();
32801             if(this.activePanel){
32802                 this.activePanel.getEl().unclip();
32803                 if(this.activePanel.afterSlide){
32804                     this.activePanel.afterSlide();
32805                 }
32806             }
32807         }
32808     },
32809
32810     initAutoHide : function(){
32811         if(this.autoHide !== false){
32812             if(!this.autoHideHd){
32813                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32814                 this.autoHideHd = {
32815                     "mouseout": function(e){
32816                         if(!e.within(this.el, true)){
32817                             st.delay(500);
32818                         }
32819                     },
32820                     "mouseover" : function(e){
32821                         st.cancel();
32822                     },
32823                     scope : this
32824                 };
32825             }
32826             this.el.on(this.autoHideHd);
32827         }
32828     },
32829
32830     clearAutoHide : function(){
32831         if(this.autoHide !== false){
32832             this.el.un("mouseout", this.autoHideHd.mouseout);
32833             this.el.un("mouseover", this.autoHideHd.mouseover);
32834         }
32835     },
32836
32837     clearMonitor : function(){
32838         Roo.get(document).un("click", this.slideInIf, this);
32839     },
32840
32841     // these names are backwards but not changed for compat
32842     slideOut : function(){
32843         if(this.isSlid || this.el.hasActiveFx()){
32844             return;
32845         }
32846         this.isSlid = true;
32847         if(this.collapseBtn){
32848             this.collapseBtn.hide();
32849         }
32850         this.closeBtnState = this.closeBtn.getStyle('display');
32851         this.closeBtn.hide();
32852         if(this.stickBtn){
32853             this.stickBtn.show();
32854         }
32855         this.el.show();
32856         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32857         this.beforeSlide();
32858         this.el.setStyle("z-index", 10001);
32859         this.el.slideIn(this.getSlideAnchor(), {
32860             callback: function(){
32861                 this.afterSlide();
32862                 this.initAutoHide();
32863                 Roo.get(document).on("click", this.slideInIf, this);
32864                 this.fireEvent("slideshow", this);
32865             },
32866             scope: this,
32867             block: true
32868         });
32869     },
32870
32871     afterSlideIn : function(){
32872         this.clearAutoHide();
32873         this.isSlid = false;
32874         this.clearMonitor();
32875         this.el.setStyle("z-index", "");
32876         if(this.collapseBtn){
32877             this.collapseBtn.show();
32878         }
32879         this.closeBtn.setStyle('display', this.closeBtnState);
32880         if(this.stickBtn){
32881             this.stickBtn.hide();
32882         }
32883         this.fireEvent("slidehide", this);
32884     },
32885
32886     slideIn : function(cb){
32887         if(!this.isSlid || this.el.hasActiveFx()){
32888             Roo.callback(cb);
32889             return;
32890         }
32891         this.isSlid = false;
32892         this.beforeSlide();
32893         this.el.slideOut(this.getSlideAnchor(), {
32894             callback: function(){
32895                 this.el.setLeftTop(-10000, -10000);
32896                 this.afterSlide();
32897                 this.afterSlideIn();
32898                 Roo.callback(cb);
32899             },
32900             scope: this,
32901             block: true
32902         });
32903     },
32904     
32905     slideInIf : function(e){
32906         if(!e.within(this.el)){
32907             this.slideIn();
32908         }
32909     },
32910
32911     animateCollapse : function(){
32912         this.beforeSlide();
32913         this.el.setStyle("z-index", 20000);
32914         var anchor = this.getSlideAnchor();
32915         this.el.slideOut(anchor, {
32916             callback : function(){
32917                 this.el.setStyle("z-index", "");
32918                 this.collapsedEl.slideIn(anchor, {duration:.3});
32919                 this.afterSlide();
32920                 this.el.setLocation(-10000,-10000);
32921                 this.el.hide();
32922                 this.fireEvent("collapsed", this);
32923             },
32924             scope: this,
32925             block: true
32926         });
32927     },
32928
32929     animateExpand : function(){
32930         this.beforeSlide();
32931         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32932         this.el.setStyle("z-index", 20000);
32933         this.collapsedEl.hide({
32934             duration:.1
32935         });
32936         this.el.slideIn(this.getSlideAnchor(), {
32937             callback : function(){
32938                 this.el.setStyle("z-index", "");
32939                 this.afterSlide();
32940                 if(this.split){
32941                     this.split.el.show();
32942                 }
32943                 this.fireEvent("invalidated", this);
32944                 this.fireEvent("expanded", this);
32945             },
32946             scope: this,
32947             block: true
32948         });
32949     },
32950
32951     anchors : {
32952         "west" : "left",
32953         "east" : "right",
32954         "north" : "top",
32955         "south" : "bottom"
32956     },
32957
32958     sanchors : {
32959         "west" : "l",
32960         "east" : "r",
32961         "north" : "t",
32962         "south" : "b"
32963     },
32964
32965     canchors : {
32966         "west" : "tl-tr",
32967         "east" : "tr-tl",
32968         "north" : "tl-bl",
32969         "south" : "bl-tl"
32970     },
32971
32972     getAnchor : function(){
32973         return this.anchors[this.position];
32974     },
32975
32976     getCollapseAnchor : function(){
32977         return this.canchors[this.position];
32978     },
32979
32980     getSlideAnchor : function(){
32981         return this.sanchors[this.position];
32982     },
32983
32984     getAlignAdj : function(){
32985         var cm = this.cmargins;
32986         switch(this.position){
32987             case "west":
32988                 return [0, 0];
32989             break;
32990             case "east":
32991                 return [0, 0];
32992             break;
32993             case "north":
32994                 return [0, 0];
32995             break;
32996             case "south":
32997                 return [0, 0];
32998             break;
32999         }
33000     },
33001
33002     getExpandAdj : function(){
33003         var c = this.collapsedEl, cm = this.cmargins;
33004         switch(this.position){
33005             case "west":
33006                 return [-(cm.right+c.getWidth()+cm.left), 0];
33007             break;
33008             case "east":
33009                 return [cm.right+c.getWidth()+cm.left, 0];
33010             break;
33011             case "north":
33012                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33013             break;
33014             case "south":
33015                 return [0, cm.top+cm.bottom+c.getHeight()];
33016             break;
33017         }
33018     }
33019 });/*
33020  * Based on:
33021  * Ext JS Library 1.1.1
33022  * Copyright(c) 2006-2007, Ext JS, LLC.
33023  *
33024  * Originally Released Under LGPL - original licence link has changed is not relivant.
33025  *
33026  * Fork - LGPL
33027  * <script type="text/javascript">
33028  */
33029 /*
33030  * These classes are private internal classes
33031  */
33032 Roo.CenterLayoutRegion = function(mgr, config){
33033     Roo.LayoutRegion.call(this, mgr, config, "center");
33034     this.visible = true;
33035     this.minWidth = config.minWidth || 20;
33036     this.minHeight = config.minHeight || 20;
33037 };
33038
33039 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
33040     hide : function(){
33041         // center panel can't be hidden
33042     },
33043     
33044     show : function(){
33045         // center panel can't be hidden
33046     },
33047     
33048     getMinWidth: function(){
33049         return this.minWidth;
33050     },
33051     
33052     getMinHeight: function(){
33053         return this.minHeight;
33054     }
33055 });
33056
33057
33058 Roo.NorthLayoutRegion = function(mgr, config){
33059     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
33060     if(this.split){
33061         this.split.placement = Roo.SplitBar.TOP;
33062         this.split.orientation = Roo.SplitBar.VERTICAL;
33063         this.split.el.addClass("x-layout-split-v");
33064     }
33065     var size = config.initialSize || config.height;
33066     if(typeof size != "undefined"){
33067         this.el.setHeight(size);
33068     }
33069 };
33070 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
33071     orientation: Roo.SplitBar.VERTICAL,
33072     getBox : function(){
33073         if(this.collapsed){
33074             return this.collapsedEl.getBox();
33075         }
33076         var box = this.el.getBox();
33077         if(this.split){
33078             box.height += this.split.el.getHeight();
33079         }
33080         return box;
33081     },
33082     
33083     updateBox : function(box){
33084         if(this.split && !this.collapsed){
33085             box.height -= this.split.el.getHeight();
33086             this.split.el.setLeft(box.x);
33087             this.split.el.setTop(box.y+box.height);
33088             this.split.el.setWidth(box.width);
33089         }
33090         if(this.collapsed){
33091             this.updateBody(box.width, null);
33092         }
33093         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33094     }
33095 });
33096
33097 Roo.SouthLayoutRegion = function(mgr, config){
33098     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
33099     if(this.split){
33100         this.split.placement = Roo.SplitBar.BOTTOM;
33101         this.split.orientation = Roo.SplitBar.VERTICAL;
33102         this.split.el.addClass("x-layout-split-v");
33103     }
33104     var size = config.initialSize || config.height;
33105     if(typeof size != "undefined"){
33106         this.el.setHeight(size);
33107     }
33108 };
33109 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
33110     orientation: Roo.SplitBar.VERTICAL,
33111     getBox : function(){
33112         if(this.collapsed){
33113             return this.collapsedEl.getBox();
33114         }
33115         var box = this.el.getBox();
33116         if(this.split){
33117             var sh = this.split.el.getHeight();
33118             box.height += sh;
33119             box.y -= sh;
33120         }
33121         return box;
33122     },
33123     
33124     updateBox : function(box){
33125         if(this.split && !this.collapsed){
33126             var sh = this.split.el.getHeight();
33127             box.height -= sh;
33128             box.y += sh;
33129             this.split.el.setLeft(box.x);
33130             this.split.el.setTop(box.y-sh);
33131             this.split.el.setWidth(box.width);
33132         }
33133         if(this.collapsed){
33134             this.updateBody(box.width, null);
33135         }
33136         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33137     }
33138 });
33139
33140 Roo.EastLayoutRegion = function(mgr, config){
33141     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
33142     if(this.split){
33143         this.split.placement = Roo.SplitBar.RIGHT;
33144         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33145         this.split.el.addClass("x-layout-split-h");
33146     }
33147     var size = config.initialSize || config.width;
33148     if(typeof size != "undefined"){
33149         this.el.setWidth(size);
33150     }
33151 };
33152 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
33153     orientation: Roo.SplitBar.HORIZONTAL,
33154     getBox : function(){
33155         if(this.collapsed){
33156             return this.collapsedEl.getBox();
33157         }
33158         var box = this.el.getBox();
33159         if(this.split){
33160             var sw = this.split.el.getWidth();
33161             box.width += sw;
33162             box.x -= sw;
33163         }
33164         return box;
33165     },
33166
33167     updateBox : function(box){
33168         if(this.split && !this.collapsed){
33169             var sw = this.split.el.getWidth();
33170             box.width -= sw;
33171             this.split.el.setLeft(box.x);
33172             this.split.el.setTop(box.y);
33173             this.split.el.setHeight(box.height);
33174             box.x += sw;
33175         }
33176         if(this.collapsed){
33177             this.updateBody(null, box.height);
33178         }
33179         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33180     }
33181 });
33182
33183 Roo.WestLayoutRegion = function(mgr, config){
33184     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
33185     if(this.split){
33186         this.split.placement = Roo.SplitBar.LEFT;
33187         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33188         this.split.el.addClass("x-layout-split-h");
33189     }
33190     var size = config.initialSize || config.width;
33191     if(typeof size != "undefined"){
33192         this.el.setWidth(size);
33193     }
33194 };
33195 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
33196     orientation: Roo.SplitBar.HORIZONTAL,
33197     getBox : function(){
33198         if(this.collapsed){
33199             return this.collapsedEl.getBox();
33200         }
33201         var box = this.el.getBox();
33202         if(this.split){
33203             box.width += this.split.el.getWidth();
33204         }
33205         return box;
33206     },
33207     
33208     updateBox : function(box){
33209         if(this.split && !this.collapsed){
33210             var sw = this.split.el.getWidth();
33211             box.width -= sw;
33212             this.split.el.setLeft(box.x+box.width);
33213             this.split.el.setTop(box.y);
33214             this.split.el.setHeight(box.height);
33215         }
33216         if(this.collapsed){
33217             this.updateBody(null, box.height);
33218         }
33219         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33220     }
33221 });
33222 /*
33223  * Based on:
33224  * Ext JS Library 1.1.1
33225  * Copyright(c) 2006-2007, Ext JS, LLC.
33226  *
33227  * Originally Released Under LGPL - original licence link has changed is not relivant.
33228  *
33229  * Fork - LGPL
33230  * <script type="text/javascript">
33231  */
33232  
33233  
33234 /*
33235  * Private internal class for reading and applying state
33236  */
33237 Roo.LayoutStateManager = function(layout){
33238      // default empty state
33239      this.state = {
33240         north: {},
33241         south: {},
33242         east: {},
33243         west: {}       
33244     };
33245 };
33246
33247 Roo.LayoutStateManager.prototype = {
33248     init : function(layout, provider){
33249         this.provider = provider;
33250         var state = provider.get(layout.id+"-layout-state");
33251         if(state){
33252             var wasUpdating = layout.isUpdating();
33253             if(!wasUpdating){
33254                 layout.beginUpdate();
33255             }
33256             for(var key in state){
33257                 if(typeof state[key] != "function"){
33258                     var rstate = state[key];
33259                     var r = layout.getRegion(key);
33260                     if(r && rstate){
33261                         if(rstate.size){
33262                             r.resizeTo(rstate.size);
33263                         }
33264                         if(rstate.collapsed == true){
33265                             r.collapse(true);
33266                         }else{
33267                             r.expand(null, true);
33268                         }
33269                     }
33270                 }
33271             }
33272             if(!wasUpdating){
33273                 layout.endUpdate();
33274             }
33275             this.state = state; 
33276         }
33277         this.layout = layout;
33278         layout.on("regionresized", this.onRegionResized, this);
33279         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33280         layout.on("regionexpanded", this.onRegionExpanded, this);
33281     },
33282     
33283     storeState : function(){
33284         this.provider.set(this.layout.id+"-layout-state", this.state);
33285     },
33286     
33287     onRegionResized : function(region, newSize){
33288         this.state[region.getPosition()].size = newSize;
33289         this.storeState();
33290     },
33291     
33292     onRegionCollapsed : function(region){
33293         this.state[region.getPosition()].collapsed = true;
33294         this.storeState();
33295     },
33296     
33297     onRegionExpanded : function(region){
33298         this.state[region.getPosition()].collapsed = false;
33299         this.storeState();
33300     }
33301 };/*
33302  * Based on:
33303  * Ext JS Library 1.1.1
33304  * Copyright(c) 2006-2007, Ext JS, LLC.
33305  *
33306  * Originally Released Under LGPL - original licence link has changed is not relivant.
33307  *
33308  * Fork - LGPL
33309  * <script type="text/javascript">
33310  */
33311 /**
33312  * @class Roo.ContentPanel
33313  * @extends Roo.util.Observable
33314  * A basic ContentPanel element.
33315  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33316  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33317  * @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
33318  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33319  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33320  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33321  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33322  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33323  * @cfg {String} title          The title for this panel
33324  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33325  * @cfg {String} url            Calls {@link #setUrl} with this value
33326  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33327  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33328  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33329  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33330
33331  * @constructor
33332  * Create a new ContentPanel.
33333  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33334  * @param {String/Object} config A string to set only the title or a config object
33335  * @param {String} content (optional) Set the HTML content for this panel
33336  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33337  */
33338 Roo.ContentPanel = function(el, config, content){
33339     
33340      
33341     /*
33342     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33343         config = el;
33344         el = Roo.id();
33345     }
33346     if (config && config.parentLayout) { 
33347         el = config.parentLayout.el.createChild(); 
33348     }
33349     */
33350     if(el.autoCreate){ // xtype is available if this is called from factory
33351         config = el;
33352         el = Roo.id();
33353     }
33354     this.el = Roo.get(el);
33355     if(!this.el && config && config.autoCreate){
33356         if(typeof config.autoCreate == "object"){
33357             if(!config.autoCreate.id){
33358                 config.autoCreate.id = config.id||el;
33359             }
33360             this.el = Roo.DomHelper.append(document.body,
33361                         config.autoCreate, true);
33362         }else{
33363             this.el = Roo.DomHelper.append(document.body,
33364                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33365         }
33366     }
33367     this.closable = false;
33368     this.loaded = false;
33369     this.active = false;
33370     if(typeof config == "string"){
33371         this.title = config;
33372     }else{
33373         Roo.apply(this, config);
33374     }
33375     
33376     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33377         this.wrapEl = this.el.wrap();
33378         this.toolbar.container = this.el.insertSibling(false, 'before');
33379         this.toolbar = new Roo.Toolbar(this.toolbar);
33380     }
33381     
33382     // xtype created footer. - not sure if will work as we normally have to render first..
33383     if (this.footer && !this.footer.el && this.footer.xtype) {
33384         if (!this.wrapEl) {
33385             this.wrapEl = this.el.wrap();
33386         }
33387     
33388         this.footer.container = this.wrapEl.createChild();
33389          
33390         this.footer = Roo.factory(this.footer, Roo);
33391         
33392     }
33393     
33394     if(this.resizeEl){
33395         this.resizeEl = Roo.get(this.resizeEl, true);
33396     }else{
33397         this.resizeEl = this.el;
33398     }
33399     // handle view.xtype
33400     
33401     if (this.view && typeof(this.view.xtype) != 'undefined') {
33402         this.view.el = this.el.appendChild(document.createElement("div"));
33403         this.view = Roo.factory(this.view);
33404         this.view.render && this.view.render(false, ''); // render blank..
33405     }
33406     
33407     
33408     
33409     this.addEvents({
33410         /**
33411          * @event activate
33412          * Fires when this panel is activated. 
33413          * @param {Roo.ContentPanel} this
33414          */
33415         "activate" : true,
33416         /**
33417          * @event deactivate
33418          * Fires when this panel is activated. 
33419          * @param {Roo.ContentPanel} this
33420          */
33421         "deactivate" : true,
33422
33423         /**
33424          * @event resize
33425          * Fires when this panel is resized if fitToFrame is true.
33426          * @param {Roo.ContentPanel} this
33427          * @param {Number} width The width after any component adjustments
33428          * @param {Number} height The height after any component adjustments
33429          */
33430         "resize" : true,
33431         
33432          /**
33433          * @event render
33434          * Fires when this tab is created
33435          * @param {Roo.ContentPanel} this
33436          */
33437         "render" : true
33438         
33439         
33440         
33441     });
33442     if(this.autoScroll){
33443         this.resizeEl.setStyle("overflow", "auto");
33444     } else {
33445         // fix randome scrolling
33446         this.el.on('scroll', function() {
33447             Roo.log('fix random scolling');
33448             this.scrollTo('top',0); 
33449         });
33450     }
33451     content = content || this.content;
33452     if(content){
33453         this.setContent(content);
33454     }
33455     if(config && config.url){
33456         this.setUrl(this.url, this.params, this.loadOnce);
33457     }
33458     
33459     
33460     
33461     Roo.ContentPanel.superclass.constructor.call(this);
33462     
33463     this.fireEvent('render', this);
33464 };
33465
33466 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33467     tabTip:'',
33468     setRegion : function(region){
33469         this.region = region;
33470         if(region){
33471            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33472         }else{
33473            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33474         } 
33475     },
33476     
33477     /**
33478      * Returns the toolbar for this Panel if one was configured. 
33479      * @return {Roo.Toolbar} 
33480      */
33481     getToolbar : function(){
33482         return this.toolbar;
33483     },
33484     
33485     setActiveState : function(active){
33486         this.active = active;
33487         if(!active){
33488             this.fireEvent("deactivate", this);
33489         }else{
33490             this.fireEvent("activate", this);
33491         }
33492     },
33493     /**
33494      * Updates this panel's element
33495      * @param {String} content The new content
33496      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33497     */
33498     setContent : function(content, loadScripts){
33499         this.el.update(content, loadScripts);
33500     },
33501
33502     ignoreResize : function(w, h){
33503         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33504             return true;
33505         }else{
33506             this.lastSize = {width: w, height: h};
33507             return false;
33508         }
33509     },
33510     /**
33511      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33512      * @return {Roo.UpdateManager} The UpdateManager
33513      */
33514     getUpdateManager : function(){
33515         return this.el.getUpdateManager();
33516     },
33517      /**
33518      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33519      * @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:
33520 <pre><code>
33521 panel.load({
33522     url: "your-url.php",
33523     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33524     callback: yourFunction,
33525     scope: yourObject, //(optional scope)
33526     discardUrl: false,
33527     nocache: false,
33528     text: "Loading...",
33529     timeout: 30,
33530     scripts: false
33531 });
33532 </code></pre>
33533      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33534      * 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.
33535      * @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}
33536      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33537      * @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.
33538      * @return {Roo.ContentPanel} this
33539      */
33540     load : function(){
33541         var um = this.el.getUpdateManager();
33542         um.update.apply(um, arguments);
33543         return this;
33544     },
33545
33546
33547     /**
33548      * 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.
33549      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33550      * @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)
33551      * @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)
33552      * @return {Roo.UpdateManager} The UpdateManager
33553      */
33554     setUrl : function(url, params, loadOnce){
33555         if(this.refreshDelegate){
33556             this.removeListener("activate", this.refreshDelegate);
33557         }
33558         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33559         this.on("activate", this.refreshDelegate);
33560         return this.el.getUpdateManager();
33561     },
33562     
33563     _handleRefresh : function(url, params, loadOnce){
33564         if(!loadOnce || !this.loaded){
33565             var updater = this.el.getUpdateManager();
33566             updater.update(url, params, this._setLoaded.createDelegate(this));
33567         }
33568     },
33569     
33570     _setLoaded : function(){
33571         this.loaded = true;
33572     }, 
33573     
33574     /**
33575      * Returns this panel's id
33576      * @return {String} 
33577      */
33578     getId : function(){
33579         return this.el.id;
33580     },
33581     
33582     /** 
33583      * Returns this panel's element - used by regiosn to add.
33584      * @return {Roo.Element} 
33585      */
33586     getEl : function(){
33587         return this.wrapEl || this.el;
33588     },
33589     
33590     adjustForComponents : function(width, height)
33591     {
33592         //Roo.log('adjustForComponents ');
33593         if(this.resizeEl != this.el){
33594             width -= this.el.getFrameWidth('lr');
33595             height -= this.el.getFrameWidth('tb');
33596         }
33597         if(this.toolbar){
33598             var te = this.toolbar.getEl();
33599             height -= te.getHeight();
33600             te.setWidth(width);
33601         }
33602         if(this.footer){
33603             var te = this.footer.getEl();
33604             Roo.log("footer:" + te.getHeight());
33605             
33606             height -= te.getHeight();
33607             te.setWidth(width);
33608         }
33609         
33610         
33611         if(this.adjustments){
33612             width += this.adjustments[0];
33613             height += this.adjustments[1];
33614         }
33615         return {"width": width, "height": height};
33616     },
33617     
33618     setSize : function(width, height){
33619         if(this.fitToFrame && !this.ignoreResize(width, height)){
33620             if(this.fitContainer && this.resizeEl != this.el){
33621                 this.el.setSize(width, height);
33622             }
33623             var size = this.adjustForComponents(width, height);
33624             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33625             this.fireEvent('resize', this, size.width, size.height);
33626         }
33627     },
33628     
33629     /**
33630      * Returns this panel's title
33631      * @return {String} 
33632      */
33633     getTitle : function(){
33634         return this.title;
33635     },
33636     
33637     /**
33638      * Set this panel's title
33639      * @param {String} title
33640      */
33641     setTitle : function(title){
33642         this.title = title;
33643         if(this.region){
33644             this.region.updatePanelTitle(this, title);
33645         }
33646     },
33647     
33648     /**
33649      * Returns true is this panel was configured to be closable
33650      * @return {Boolean} 
33651      */
33652     isClosable : function(){
33653         return this.closable;
33654     },
33655     
33656     beforeSlide : function(){
33657         this.el.clip();
33658         this.resizeEl.clip();
33659     },
33660     
33661     afterSlide : function(){
33662         this.el.unclip();
33663         this.resizeEl.unclip();
33664     },
33665     
33666     /**
33667      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33668      *   Will fail silently if the {@link #setUrl} method has not been called.
33669      *   This does not activate the panel, just updates its content.
33670      */
33671     refresh : function(){
33672         if(this.refreshDelegate){
33673            this.loaded = false;
33674            this.refreshDelegate();
33675         }
33676     },
33677     
33678     /**
33679      * Destroys this panel
33680      */
33681     destroy : function(){
33682         this.el.removeAllListeners();
33683         var tempEl = document.createElement("span");
33684         tempEl.appendChild(this.el.dom);
33685         tempEl.innerHTML = "";
33686         this.el.remove();
33687         this.el = null;
33688     },
33689     
33690     /**
33691      * form - if the content panel contains a form - this is a reference to it.
33692      * @type {Roo.form.Form}
33693      */
33694     form : false,
33695     /**
33696      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33697      *    This contains a reference to it.
33698      * @type {Roo.View}
33699      */
33700     view : false,
33701     
33702       /**
33703      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33704      * <pre><code>
33705
33706 layout.addxtype({
33707        xtype : 'Form',
33708        items: [ .... ]
33709    }
33710 );
33711
33712 </code></pre>
33713      * @param {Object} cfg Xtype definition of item to add.
33714      */
33715     
33716     addxtype : function(cfg) {
33717         // add form..
33718         if (cfg.xtype.match(/^Form$/)) {
33719             
33720             var el;
33721             //if (this.footer) {
33722             //    el = this.footer.container.insertSibling(false, 'before');
33723             //} else {
33724                 el = this.el.createChild();
33725             //}
33726
33727             this.form = new  Roo.form.Form(cfg);
33728             
33729             
33730             if ( this.form.allItems.length) this.form.render(el.dom);
33731             return this.form;
33732         }
33733         // should only have one of theses..
33734         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33735             // views..
33736             cfg.el = this.el.appendChild(document.createElement("div"));
33737             // factory?
33738             
33739             var ret = new Roo.factory(cfg);
33740             ret.render && ret.render(false, ''); // render blank..
33741             this.view = ret;
33742             return ret;
33743         }
33744         return false;
33745     }
33746 });
33747
33748 /**
33749  * @class Roo.GridPanel
33750  * @extends Roo.ContentPanel
33751  * @constructor
33752  * Create a new GridPanel.
33753  * @param {Roo.grid.Grid} grid The grid for this panel
33754  * @param {String/Object} config A string to set only the panel's title, or a config object
33755  */
33756 Roo.GridPanel = function(grid, config){
33757     
33758   
33759     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33760         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33761         
33762     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33763     
33764     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33765     
33766     if(this.toolbar){
33767         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33768     }
33769     // xtype created footer. - not sure if will work as we normally have to render first..
33770     if (this.footer && !this.footer.el && this.footer.xtype) {
33771         
33772         this.footer.container = this.grid.getView().getFooterPanel(true);
33773         this.footer.dataSource = this.grid.dataSource;
33774         this.footer = Roo.factory(this.footer, Roo);
33775         
33776     }
33777     
33778     grid.monitorWindowResize = false; // turn off autosizing
33779     grid.autoHeight = false;
33780     grid.autoWidth = false;
33781     this.grid = grid;
33782     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33783 };
33784
33785 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33786     getId : function(){
33787         return this.grid.id;
33788     },
33789     
33790     /**
33791      * Returns the grid for this panel
33792      * @return {Roo.grid.Grid} 
33793      */
33794     getGrid : function(){
33795         return this.grid;    
33796     },
33797     
33798     setSize : function(width, height){
33799         if(!this.ignoreResize(width, height)){
33800             var grid = this.grid;
33801             var size = this.adjustForComponents(width, height);
33802             grid.getGridEl().setSize(size.width, size.height);
33803             grid.autoSize();
33804         }
33805     },
33806     
33807     beforeSlide : function(){
33808         this.grid.getView().scroller.clip();
33809     },
33810     
33811     afterSlide : function(){
33812         this.grid.getView().scroller.unclip();
33813     },
33814     
33815     destroy : function(){
33816         this.grid.destroy();
33817         delete this.grid;
33818         Roo.GridPanel.superclass.destroy.call(this); 
33819     }
33820 });
33821
33822
33823 /**
33824  * @class Roo.NestedLayoutPanel
33825  * @extends Roo.ContentPanel
33826  * @constructor
33827  * Create a new NestedLayoutPanel.
33828  * 
33829  * 
33830  * @param {Roo.BorderLayout} layout The layout for this panel
33831  * @param {String/Object} config A string to set only the title or a config object
33832  */
33833 Roo.NestedLayoutPanel = function(layout, config)
33834 {
33835     // construct with only one argument..
33836     /* FIXME - implement nicer consturctors
33837     if (layout.layout) {
33838         config = layout;
33839         layout = config.layout;
33840         delete config.layout;
33841     }
33842     if (layout.xtype && !layout.getEl) {
33843         // then layout needs constructing..
33844         layout = Roo.factory(layout, Roo);
33845     }
33846     */
33847     
33848     
33849     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33850     
33851     layout.monitorWindowResize = false; // turn off autosizing
33852     this.layout = layout;
33853     this.layout.getEl().addClass("x-layout-nested-layout");
33854     
33855     
33856     
33857     
33858 };
33859
33860 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33861
33862     setSize : function(width, height){
33863         if(!this.ignoreResize(width, height)){
33864             var size = this.adjustForComponents(width, height);
33865             var el = this.layout.getEl();
33866             el.setSize(size.width, size.height);
33867             var touch = el.dom.offsetWidth;
33868             this.layout.layout();
33869             // ie requires a double layout on the first pass
33870             if(Roo.isIE && !this.initialized){
33871                 this.initialized = true;
33872                 this.layout.layout();
33873             }
33874         }
33875     },
33876     
33877     // activate all subpanels if not currently active..
33878     
33879     setActiveState : function(active){
33880         this.active = active;
33881         if(!active){
33882             this.fireEvent("deactivate", this);
33883             return;
33884         }
33885         
33886         this.fireEvent("activate", this);
33887         // not sure if this should happen before or after..
33888         if (!this.layout) {
33889             return; // should not happen..
33890         }
33891         var reg = false;
33892         for (var r in this.layout.regions) {
33893             reg = this.layout.getRegion(r);
33894             if (reg.getActivePanel()) {
33895                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33896                 reg.setActivePanel(reg.getActivePanel());
33897                 continue;
33898             }
33899             if (!reg.panels.length) {
33900                 continue;
33901             }
33902             reg.showPanel(reg.getPanel(0));
33903         }
33904         
33905         
33906         
33907         
33908     },
33909     
33910     /**
33911      * Returns the nested BorderLayout for this panel
33912      * @return {Roo.BorderLayout} 
33913      */
33914     getLayout : function(){
33915         return this.layout;
33916     },
33917     
33918      /**
33919      * Adds a xtype elements to the layout of the nested panel
33920      * <pre><code>
33921
33922 panel.addxtype({
33923        xtype : 'ContentPanel',
33924        region: 'west',
33925        items: [ .... ]
33926    }
33927 );
33928
33929 panel.addxtype({
33930         xtype : 'NestedLayoutPanel',
33931         region: 'west',
33932         layout: {
33933            center: { },
33934            west: { }   
33935         },
33936         items : [ ... list of content panels or nested layout panels.. ]
33937    }
33938 );
33939 </code></pre>
33940      * @param {Object} cfg Xtype definition of item to add.
33941      */
33942     addxtype : function(cfg) {
33943         return this.layout.addxtype(cfg);
33944     
33945     }
33946 });
33947
33948 Roo.ScrollPanel = function(el, config, content){
33949     config = config || {};
33950     config.fitToFrame = true;
33951     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33952     
33953     this.el.dom.style.overflow = "hidden";
33954     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33955     this.el.removeClass("x-layout-inactive-content");
33956     this.el.on("mousewheel", this.onWheel, this);
33957
33958     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33959     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33960     up.unselectable(); down.unselectable();
33961     up.on("click", this.scrollUp, this);
33962     down.on("click", this.scrollDown, this);
33963     up.addClassOnOver("x-scroller-btn-over");
33964     down.addClassOnOver("x-scroller-btn-over");
33965     up.addClassOnClick("x-scroller-btn-click");
33966     down.addClassOnClick("x-scroller-btn-click");
33967     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33968
33969     this.resizeEl = this.el;
33970     this.el = wrap; this.up = up; this.down = down;
33971 };
33972
33973 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33974     increment : 100,
33975     wheelIncrement : 5,
33976     scrollUp : function(){
33977         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33978     },
33979
33980     scrollDown : function(){
33981         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33982     },
33983
33984     afterScroll : function(){
33985         var el = this.resizeEl;
33986         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33987         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33988         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33989     },
33990
33991     setSize : function(){
33992         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33993         this.afterScroll();
33994     },
33995
33996     onWheel : function(e){
33997         var d = e.getWheelDelta();
33998         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33999         this.afterScroll();
34000         e.stopEvent();
34001     },
34002
34003     setContent : function(content, loadScripts){
34004         this.resizeEl.update(content, loadScripts);
34005     }
34006
34007 });
34008
34009
34010
34011
34012
34013
34014
34015
34016
34017 /**
34018  * @class Roo.TreePanel
34019  * @extends Roo.ContentPanel
34020  * @constructor
34021  * Create a new TreePanel. - defaults to fit/scoll contents.
34022  * @param {String/Object} config A string to set only the panel's title, or a config object
34023  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
34024  */
34025 Roo.TreePanel = function(config){
34026     var el = config.el;
34027     var tree = config.tree;
34028     delete config.tree; 
34029     delete config.el; // hopefull!
34030     
34031     // wrapper for IE7 strict & safari scroll issue
34032     
34033     var treeEl = el.createChild();
34034     config.resizeEl = treeEl;
34035     
34036     
34037     
34038     Roo.TreePanel.superclass.constructor.call(this, el, config);
34039  
34040  
34041     this.tree = new Roo.tree.TreePanel(treeEl , tree);
34042     //console.log(tree);
34043     this.on('activate', function()
34044     {
34045         if (this.tree.rendered) {
34046             return;
34047         }
34048         //console.log('render tree');
34049         this.tree.render();
34050     });
34051     // this should not be needed.. - it's actually the 'el' that resizes?
34052     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
34053     
34054     //this.on('resize',  function (cp, w, h) {
34055     //        this.tree.innerCt.setWidth(w);
34056     //        this.tree.innerCt.setHeight(h);
34057     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
34058     //});
34059
34060         
34061     
34062 };
34063
34064 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
34065     fitToFrame : true,
34066     autoScroll : true
34067 });
34068
34069
34070
34071
34072
34073
34074
34075
34076
34077
34078
34079 /*
34080  * Based on:
34081  * Ext JS Library 1.1.1
34082  * Copyright(c) 2006-2007, Ext JS, LLC.
34083  *
34084  * Originally Released Under LGPL - original licence link has changed is not relivant.
34085  *
34086  * Fork - LGPL
34087  * <script type="text/javascript">
34088  */
34089  
34090
34091 /**
34092  * @class Roo.ReaderLayout
34093  * @extends Roo.BorderLayout
34094  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
34095  * center region containing two nested regions (a top one for a list view and one for item preview below),
34096  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
34097  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
34098  * expedites the setup of the overall layout and regions for this common application style.
34099  * Example:
34100  <pre><code>
34101 var reader = new Roo.ReaderLayout();
34102 var CP = Roo.ContentPanel;  // shortcut for adding
34103
34104 reader.beginUpdate();
34105 reader.add("north", new CP("north", "North"));
34106 reader.add("west", new CP("west", {title: "West"}));
34107 reader.add("east", new CP("east", {title: "East"}));
34108
34109 reader.regions.listView.add(new CP("listView", "List"));
34110 reader.regions.preview.add(new CP("preview", "Preview"));
34111 reader.endUpdate();
34112 </code></pre>
34113 * @constructor
34114 * Create a new ReaderLayout
34115 * @param {Object} config Configuration options
34116 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
34117 * document.body if omitted)
34118 */
34119 Roo.ReaderLayout = function(config, renderTo){
34120     var c = config || {size:{}};
34121     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
34122         north: c.north !== false ? Roo.apply({
34123             split:false,
34124             initialSize: 32,
34125             titlebar: false
34126         }, c.north) : false,
34127         west: c.west !== false ? Roo.apply({
34128             split:true,
34129             initialSize: 200,
34130             minSize: 175,
34131             maxSize: 400,
34132             titlebar: true,
34133             collapsible: true,
34134             animate: true,
34135             margins:{left:5,right:0,bottom:5,top:5},
34136             cmargins:{left:5,right:5,bottom:5,top:5}
34137         }, c.west) : false,
34138         east: c.east !== false ? Roo.apply({
34139             split:true,
34140             initialSize: 200,
34141             minSize: 175,
34142             maxSize: 400,
34143             titlebar: true,
34144             collapsible: true,
34145             animate: true,
34146             margins:{left:0,right:5,bottom:5,top:5},
34147             cmargins:{left:5,right:5,bottom:5,top:5}
34148         }, c.east) : false,
34149         center: Roo.apply({
34150             tabPosition: 'top',
34151             autoScroll:false,
34152             closeOnTab: true,
34153             titlebar:false,
34154             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
34155         }, c.center)
34156     });
34157
34158     this.el.addClass('x-reader');
34159
34160     this.beginUpdate();
34161
34162     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
34163         south: c.preview !== false ? Roo.apply({
34164             split:true,
34165             initialSize: 200,
34166             minSize: 100,
34167             autoScroll:true,
34168             collapsible:true,
34169             titlebar: true,
34170             cmargins:{top:5,left:0, right:0, bottom:0}
34171         }, c.preview) : false,
34172         center: Roo.apply({
34173             autoScroll:false,
34174             titlebar:false,
34175             minHeight:200
34176         }, c.listView)
34177     });
34178     this.add('center', new Roo.NestedLayoutPanel(inner,
34179             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
34180
34181     this.endUpdate();
34182
34183     this.regions.preview = inner.getRegion('south');
34184     this.regions.listView = inner.getRegion('center');
34185 };
34186
34187 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
34188  * Based on:
34189  * Ext JS Library 1.1.1
34190  * Copyright(c) 2006-2007, Ext JS, LLC.
34191  *
34192  * Originally Released Under LGPL - original licence link has changed is not relivant.
34193  *
34194  * Fork - LGPL
34195  * <script type="text/javascript">
34196  */
34197  
34198 /**
34199  * @class Roo.grid.Grid
34200  * @extends Roo.util.Observable
34201  * This class represents the primary interface of a component based grid control.
34202  * <br><br>Usage:<pre><code>
34203  var grid = new Roo.grid.Grid("my-container-id", {
34204      ds: myDataStore,
34205      cm: myColModel,
34206      selModel: mySelectionModel,
34207      autoSizeColumns: true,
34208      monitorWindowResize: false,
34209      trackMouseOver: true
34210  });
34211  // set any options
34212  grid.render();
34213  * </code></pre>
34214  * <b>Common Problems:</b><br/>
34215  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
34216  * element will correct this<br/>
34217  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
34218  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
34219  * are unpredictable.<br/>
34220  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
34221  * grid to calculate dimensions/offsets.<br/>
34222   * @constructor
34223  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
34224  * The container MUST have some type of size defined for the grid to fill. The container will be
34225  * automatically set to position relative if it isn't already.
34226  * @param {Object} config A config object that sets properties on this grid.
34227  */
34228 Roo.grid.Grid = function(container, config){
34229         // initialize the container
34230         this.container = Roo.get(container);
34231         this.container.update("");
34232         this.container.setStyle("overflow", "hidden");
34233     this.container.addClass('x-grid-container');
34234
34235     this.id = this.container.id;
34236
34237     Roo.apply(this, config);
34238     // check and correct shorthanded configs
34239     if(this.ds){
34240         this.dataSource = this.ds;
34241         delete this.ds;
34242     }
34243     if(this.cm){
34244         this.colModel = this.cm;
34245         delete this.cm;
34246     }
34247     if(this.sm){
34248         this.selModel = this.sm;
34249         delete this.sm;
34250     }
34251
34252     if (this.selModel) {
34253         this.selModel = Roo.factory(this.selModel, Roo.grid);
34254         this.sm = this.selModel;
34255         this.sm.xmodule = this.xmodule || false;
34256     }
34257     if (typeof(this.colModel.config) == 'undefined') {
34258         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34259         this.cm = this.colModel;
34260         this.cm.xmodule = this.xmodule || false;
34261     }
34262     if (this.dataSource) {
34263         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34264         this.ds = this.dataSource;
34265         this.ds.xmodule = this.xmodule || false;
34266          
34267     }
34268     
34269     
34270     
34271     if(this.width){
34272         this.container.setWidth(this.width);
34273     }
34274
34275     if(this.height){
34276         this.container.setHeight(this.height);
34277     }
34278     /** @private */
34279         this.addEvents({
34280         // raw events
34281         /**
34282          * @event click
34283          * The raw click event for the entire grid.
34284          * @param {Roo.EventObject} e
34285          */
34286         "click" : true,
34287         /**
34288          * @event dblclick
34289          * The raw dblclick event for the entire grid.
34290          * @param {Roo.EventObject} e
34291          */
34292         "dblclick" : true,
34293         /**
34294          * @event contextmenu
34295          * The raw contextmenu event for the entire grid.
34296          * @param {Roo.EventObject} e
34297          */
34298         "contextmenu" : true,
34299         /**
34300          * @event mousedown
34301          * The raw mousedown event for the entire grid.
34302          * @param {Roo.EventObject} e
34303          */
34304         "mousedown" : true,
34305         /**
34306          * @event mouseup
34307          * The raw mouseup event for the entire grid.
34308          * @param {Roo.EventObject} e
34309          */
34310         "mouseup" : true,
34311         /**
34312          * @event mouseover
34313          * The raw mouseover event for the entire grid.
34314          * @param {Roo.EventObject} e
34315          */
34316         "mouseover" : true,
34317         /**
34318          * @event mouseout
34319          * The raw mouseout event for the entire grid.
34320          * @param {Roo.EventObject} e
34321          */
34322         "mouseout" : true,
34323         /**
34324          * @event keypress
34325          * The raw keypress event for the entire grid.
34326          * @param {Roo.EventObject} e
34327          */
34328         "keypress" : true,
34329         /**
34330          * @event keydown
34331          * The raw keydown event for the entire grid.
34332          * @param {Roo.EventObject} e
34333          */
34334         "keydown" : true,
34335
34336         // custom events
34337
34338         /**
34339          * @event cellclick
34340          * Fires when a cell is clicked
34341          * @param {Grid} this
34342          * @param {Number} rowIndex
34343          * @param {Number} columnIndex
34344          * @param {Roo.EventObject} e
34345          */
34346         "cellclick" : true,
34347         /**
34348          * @event celldblclick
34349          * Fires when a cell is double clicked
34350          * @param {Grid} this
34351          * @param {Number} rowIndex
34352          * @param {Number} columnIndex
34353          * @param {Roo.EventObject} e
34354          */
34355         "celldblclick" : true,
34356         /**
34357          * @event rowclick
34358          * Fires when a row is clicked
34359          * @param {Grid} this
34360          * @param {Number} rowIndex
34361          * @param {Roo.EventObject} e
34362          */
34363         "rowclick" : true,
34364         /**
34365          * @event rowdblclick
34366          * Fires when a row is double clicked
34367          * @param {Grid} this
34368          * @param {Number} rowIndex
34369          * @param {Roo.EventObject} e
34370          */
34371         "rowdblclick" : true,
34372         /**
34373          * @event headerclick
34374          * Fires when a header is clicked
34375          * @param {Grid} this
34376          * @param {Number} columnIndex
34377          * @param {Roo.EventObject} e
34378          */
34379         "headerclick" : true,
34380         /**
34381          * @event headerdblclick
34382          * Fires when a header cell is double clicked
34383          * @param {Grid} this
34384          * @param {Number} columnIndex
34385          * @param {Roo.EventObject} e
34386          */
34387         "headerdblclick" : true,
34388         /**
34389          * @event rowcontextmenu
34390          * Fires when a row is right clicked
34391          * @param {Grid} this
34392          * @param {Number} rowIndex
34393          * @param {Roo.EventObject} e
34394          */
34395         "rowcontextmenu" : true,
34396         /**
34397          * @event cellcontextmenu
34398          * Fires when a cell is right clicked
34399          * @param {Grid} this
34400          * @param {Number} rowIndex
34401          * @param {Number} cellIndex
34402          * @param {Roo.EventObject} e
34403          */
34404          "cellcontextmenu" : true,
34405         /**
34406          * @event headercontextmenu
34407          * Fires when a header is right clicked
34408          * @param {Grid} this
34409          * @param {Number} columnIndex
34410          * @param {Roo.EventObject} e
34411          */
34412         "headercontextmenu" : true,
34413         /**
34414          * @event bodyscroll
34415          * Fires when the body element is scrolled
34416          * @param {Number} scrollLeft
34417          * @param {Number} scrollTop
34418          */
34419         "bodyscroll" : true,
34420         /**
34421          * @event columnresize
34422          * Fires when the user resizes a column
34423          * @param {Number} columnIndex
34424          * @param {Number} newSize
34425          */
34426         "columnresize" : true,
34427         /**
34428          * @event columnmove
34429          * Fires when the user moves a column
34430          * @param {Number} oldIndex
34431          * @param {Number} newIndex
34432          */
34433         "columnmove" : true,
34434         /**
34435          * @event startdrag
34436          * Fires when row(s) start being dragged
34437          * @param {Grid} this
34438          * @param {Roo.GridDD} dd The drag drop object
34439          * @param {event} e The raw browser event
34440          */
34441         "startdrag" : true,
34442         /**
34443          * @event enddrag
34444          * Fires when a drag operation is complete
34445          * @param {Grid} this
34446          * @param {Roo.GridDD} dd The drag drop object
34447          * @param {event} e The raw browser event
34448          */
34449         "enddrag" : true,
34450         /**
34451          * @event dragdrop
34452          * Fires when dragged row(s) are dropped on a valid DD target
34453          * @param {Grid} this
34454          * @param {Roo.GridDD} dd The drag drop object
34455          * @param {String} targetId The target drag drop object
34456          * @param {event} e The raw browser event
34457          */
34458         "dragdrop" : true,
34459         /**
34460          * @event dragover
34461          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34462          * @param {Grid} this
34463          * @param {Roo.GridDD} dd The drag drop object
34464          * @param {String} targetId The target drag drop object
34465          * @param {event} e The raw browser event
34466          */
34467         "dragover" : true,
34468         /**
34469          * @event dragenter
34470          *  Fires when the dragged row(s) first cross another DD target while being dragged
34471          * @param {Grid} this
34472          * @param {Roo.GridDD} dd The drag drop object
34473          * @param {String} targetId The target drag drop object
34474          * @param {event} e The raw browser event
34475          */
34476         "dragenter" : true,
34477         /**
34478          * @event dragout
34479          * Fires when the dragged row(s) leave another DD target while being dragged
34480          * @param {Grid} this
34481          * @param {Roo.GridDD} dd The drag drop object
34482          * @param {String} targetId The target drag drop object
34483          * @param {event} e The raw browser event
34484          */
34485         "dragout" : true,
34486         /**
34487          * @event rowclass
34488          * Fires when a row is rendered, so you can change add a style to it.
34489          * @param {GridView} gridview   The grid view
34490          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
34491          */
34492         'rowclass' : true,
34493
34494         /**
34495          * @event render
34496          * Fires when the grid is rendered
34497          * @param {Grid} grid
34498          */
34499         'render' : true
34500     });
34501
34502     Roo.grid.Grid.superclass.constructor.call(this);
34503 };
34504 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34505     
34506     /**
34507      * @cfg {String} ddGroup - drag drop group.
34508      */
34509
34510     /**
34511      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34512      */
34513     minColumnWidth : 25,
34514
34515     /**
34516      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34517      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34518      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34519      */
34520     autoSizeColumns : false,
34521
34522     /**
34523      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34524      */
34525     autoSizeHeaders : true,
34526
34527     /**
34528      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34529      */
34530     monitorWindowResize : true,
34531
34532     /**
34533      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34534      * rows measured to get a columns size. Default is 0 (all rows).
34535      */
34536     maxRowsToMeasure : 0,
34537
34538     /**
34539      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34540      */
34541     trackMouseOver : true,
34542
34543     /**
34544     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34545     */
34546     
34547     /**
34548     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34549     */
34550     enableDragDrop : false,
34551     
34552     /**
34553     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34554     */
34555     enableColumnMove : true,
34556     
34557     /**
34558     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34559     */
34560     enableColumnHide : true,
34561     
34562     /**
34563     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34564     */
34565     enableRowHeightSync : false,
34566     
34567     /**
34568     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34569     */
34570     stripeRows : true,
34571     
34572     /**
34573     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34574     */
34575     autoHeight : false,
34576
34577     /**
34578      * @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.
34579      */
34580     autoExpandColumn : false,
34581
34582     /**
34583     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34584     * Default is 50.
34585     */
34586     autoExpandMin : 50,
34587
34588     /**
34589     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34590     */
34591     autoExpandMax : 1000,
34592
34593     /**
34594     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34595     */
34596     view : null,
34597
34598     /**
34599     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34600     */
34601     loadMask : false,
34602     /**
34603     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
34604     */
34605     dropTarget: false,
34606     
34607    
34608     
34609     // private
34610     rendered : false,
34611
34612     /**
34613     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34614     * of a fixed width. Default is false.
34615     */
34616     /**
34617     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34618     */
34619     /**
34620      * Called once after all setup has been completed and the grid is ready to be rendered.
34621      * @return {Roo.grid.Grid} this
34622      */
34623     render : function()
34624     {
34625         var c = this.container;
34626         // try to detect autoHeight/width mode
34627         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34628             this.autoHeight = true;
34629         }
34630         var view = this.getView();
34631         view.init(this);
34632
34633         c.on("click", this.onClick, this);
34634         c.on("dblclick", this.onDblClick, this);
34635         c.on("contextmenu", this.onContextMenu, this);
34636         c.on("keydown", this.onKeyDown, this);
34637
34638         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34639
34640         this.getSelectionModel().init(this);
34641
34642         view.render();
34643
34644         if(this.loadMask){
34645             this.loadMask = new Roo.LoadMask(this.container,
34646                     Roo.apply({store:this.dataSource}, this.loadMask));
34647         }
34648         
34649         
34650         if (this.toolbar && this.toolbar.xtype) {
34651             this.toolbar.container = this.getView().getHeaderPanel(true);
34652             this.toolbar = new Roo.Toolbar(this.toolbar);
34653         }
34654         if (this.footer && this.footer.xtype) {
34655             this.footer.dataSource = this.getDataSource();
34656             this.footer.container = this.getView().getFooterPanel(true);
34657             this.footer = Roo.factory(this.footer, Roo);
34658         }
34659         if (this.dropTarget && this.dropTarget.xtype) {
34660             delete this.dropTarget.xtype;
34661             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34662         }
34663         
34664         
34665         this.rendered = true;
34666         this.fireEvent('render', this);
34667         return this;
34668     },
34669
34670         /**
34671          * Reconfigures the grid to use a different Store and Column Model.
34672          * The View will be bound to the new objects and refreshed.
34673          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34674          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34675          */
34676     reconfigure : function(dataSource, colModel){
34677         if(this.loadMask){
34678             this.loadMask.destroy();
34679             this.loadMask = new Roo.LoadMask(this.container,
34680                     Roo.apply({store:dataSource}, this.loadMask));
34681         }
34682         this.view.bind(dataSource, colModel);
34683         this.dataSource = dataSource;
34684         this.colModel = colModel;
34685         this.view.refresh(true);
34686     },
34687
34688     // private
34689     onKeyDown : function(e){
34690         this.fireEvent("keydown", e);
34691     },
34692
34693     /**
34694      * Destroy this grid.
34695      * @param {Boolean} removeEl True to remove the element
34696      */
34697     destroy : function(removeEl, keepListeners){
34698         if(this.loadMask){
34699             this.loadMask.destroy();
34700         }
34701         var c = this.container;
34702         c.removeAllListeners();
34703         this.view.destroy();
34704         this.colModel.purgeListeners();
34705         if(!keepListeners){
34706             this.purgeListeners();
34707         }
34708         c.update("");
34709         if(removeEl === true){
34710             c.remove();
34711         }
34712     },
34713
34714     // private
34715     processEvent : function(name, e){
34716         this.fireEvent(name, e);
34717         var t = e.getTarget();
34718         var v = this.view;
34719         var header = v.findHeaderIndex(t);
34720         if(header !== false){
34721             this.fireEvent("header" + name, this, header, e);
34722         }else{
34723             var row = v.findRowIndex(t);
34724             var cell = v.findCellIndex(t);
34725             if(row !== false){
34726                 this.fireEvent("row" + name, this, row, e);
34727                 if(cell !== false){
34728                     this.fireEvent("cell" + name, this, row, cell, e);
34729                 }
34730             }
34731         }
34732     },
34733
34734     // private
34735     onClick : function(e){
34736         this.processEvent("click", e);
34737     },
34738
34739     // private
34740     onContextMenu : function(e, t){
34741         this.processEvent("contextmenu", e);
34742     },
34743
34744     // private
34745     onDblClick : function(e){
34746         this.processEvent("dblclick", e);
34747     },
34748
34749     // private
34750     walkCells : function(row, col, step, fn, scope){
34751         var cm = this.colModel, clen = cm.getColumnCount();
34752         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34753         if(step < 0){
34754             if(col < 0){
34755                 row--;
34756                 first = false;
34757             }
34758             while(row >= 0){
34759                 if(!first){
34760                     col = clen-1;
34761                 }
34762                 first = false;
34763                 while(col >= 0){
34764                     if(fn.call(scope || this, row, col, cm) === true){
34765                         return [row, col];
34766                     }
34767                     col--;
34768                 }
34769                 row--;
34770             }
34771         } else {
34772             if(col >= clen){
34773                 row++;
34774                 first = false;
34775             }
34776             while(row < rlen){
34777                 if(!first){
34778                     col = 0;
34779                 }
34780                 first = false;
34781                 while(col < clen){
34782                     if(fn.call(scope || this, row, col, cm) === true){
34783                         return [row, col];
34784                     }
34785                     col++;
34786                 }
34787                 row++;
34788             }
34789         }
34790         return null;
34791     },
34792
34793     // private
34794     getSelections : function(){
34795         return this.selModel.getSelections();
34796     },
34797
34798     /**
34799      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34800      * but if manual update is required this method will initiate it.
34801      */
34802     autoSize : function(){
34803         if(this.rendered){
34804             this.view.layout();
34805             if(this.view.adjustForScroll){
34806                 this.view.adjustForScroll();
34807             }
34808         }
34809     },
34810
34811     /**
34812      * Returns the grid's underlying element.
34813      * @return {Element} The element
34814      */
34815     getGridEl : function(){
34816         return this.container;
34817     },
34818
34819     // private for compatibility, overridden by editor grid
34820     stopEditing : function(){},
34821
34822     /**
34823      * Returns the grid's SelectionModel.
34824      * @return {SelectionModel}
34825      */
34826     getSelectionModel : function(){
34827         if(!this.selModel){
34828             this.selModel = new Roo.grid.RowSelectionModel();
34829         }
34830         return this.selModel;
34831     },
34832
34833     /**
34834      * Returns the grid's DataSource.
34835      * @return {DataSource}
34836      */
34837     getDataSource : function(){
34838         return this.dataSource;
34839     },
34840
34841     /**
34842      * Returns the grid's ColumnModel.
34843      * @return {ColumnModel}
34844      */
34845     getColumnModel : function(){
34846         return this.colModel;
34847     },
34848
34849     /**
34850      * Returns the grid's GridView object.
34851      * @return {GridView}
34852      */
34853     getView : function(){
34854         if(!this.view){
34855             this.view = new Roo.grid.GridView(this.viewConfig);
34856         }
34857         return this.view;
34858     },
34859     /**
34860      * Called to get grid's drag proxy text, by default returns this.ddText.
34861      * @return {String}
34862      */
34863     getDragDropText : function(){
34864         var count = this.selModel.getCount();
34865         return String.format(this.ddText, count, count == 1 ? '' : 's');
34866     }
34867 });
34868 /**
34869  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34870  * %0 is replaced with the number of selected rows.
34871  * @type String
34872  */
34873 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34874  * Based on:
34875  * Ext JS Library 1.1.1
34876  * Copyright(c) 2006-2007, Ext JS, LLC.
34877  *
34878  * Originally Released Under LGPL - original licence link has changed is not relivant.
34879  *
34880  * Fork - LGPL
34881  * <script type="text/javascript">
34882  */
34883  
34884 Roo.grid.AbstractGridView = function(){
34885         this.grid = null;
34886         
34887         this.events = {
34888             "beforerowremoved" : true,
34889             "beforerowsinserted" : true,
34890             "beforerefresh" : true,
34891             "rowremoved" : true,
34892             "rowsinserted" : true,
34893             "rowupdated" : true,
34894             "refresh" : true
34895         };
34896     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34897 };
34898
34899 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34900     rowClass : "x-grid-row",
34901     cellClass : "x-grid-cell",
34902     tdClass : "x-grid-td",
34903     hdClass : "x-grid-hd",
34904     splitClass : "x-grid-hd-split",
34905     
34906         init: function(grid){
34907         this.grid = grid;
34908                 var cid = this.grid.getGridEl().id;
34909         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34910         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34911         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34912         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34913         },
34914         
34915         getColumnRenderers : function(){
34916         var renderers = [];
34917         var cm = this.grid.colModel;
34918         var colCount = cm.getColumnCount();
34919         for(var i = 0; i < colCount; i++){
34920             renderers[i] = cm.getRenderer(i);
34921         }
34922         return renderers;
34923     },
34924     
34925     getColumnIds : function(){
34926         var ids = [];
34927         var cm = this.grid.colModel;
34928         var colCount = cm.getColumnCount();
34929         for(var i = 0; i < colCount; i++){
34930             ids[i] = cm.getColumnId(i);
34931         }
34932         return ids;
34933     },
34934     
34935     getDataIndexes : function(){
34936         if(!this.indexMap){
34937             this.indexMap = this.buildIndexMap();
34938         }
34939         return this.indexMap.colToData;
34940     },
34941     
34942     getColumnIndexByDataIndex : function(dataIndex){
34943         if(!this.indexMap){
34944             this.indexMap = this.buildIndexMap();
34945         }
34946         return this.indexMap.dataToCol[dataIndex];
34947     },
34948     
34949     /**
34950      * Set a css style for a column dynamically. 
34951      * @param {Number} colIndex The index of the column
34952      * @param {String} name The css property name
34953      * @param {String} value The css value
34954      */
34955     setCSSStyle : function(colIndex, name, value){
34956         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34957         Roo.util.CSS.updateRule(selector, name, value);
34958     },
34959     
34960     generateRules : function(cm){
34961         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34962         Roo.util.CSS.removeStyleSheet(rulesId);
34963         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34964             var cid = cm.getColumnId(i);
34965             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34966                          this.tdSelector, cid, " {\n}\n",
34967                          this.hdSelector, cid, " {\n}\n",
34968                          this.splitSelector, cid, " {\n}\n");
34969         }
34970         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34971     }
34972 });/*
34973  * Based on:
34974  * Ext JS Library 1.1.1
34975  * Copyright(c) 2006-2007, Ext JS, LLC.
34976  *
34977  * Originally Released Under LGPL - original licence link has changed is not relivant.
34978  *
34979  * Fork - LGPL
34980  * <script type="text/javascript">
34981  */
34982
34983 // private
34984 // This is a support class used internally by the Grid components
34985 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34986     this.grid = grid;
34987     this.view = grid.getView();
34988     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34989     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34990     if(hd2){
34991         this.setHandleElId(Roo.id(hd));
34992         this.setOuterHandleElId(Roo.id(hd2));
34993     }
34994     this.scroll = false;
34995 };
34996 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34997     maxDragWidth: 120,
34998     getDragData : function(e){
34999         var t = Roo.lib.Event.getTarget(e);
35000         var h = this.view.findHeaderCell(t);
35001         if(h){
35002             return {ddel: h.firstChild, header:h};
35003         }
35004         return false;
35005     },
35006
35007     onInitDrag : function(e){
35008         this.view.headersDisabled = true;
35009         var clone = this.dragData.ddel.cloneNode(true);
35010         clone.id = Roo.id();
35011         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
35012         this.proxy.update(clone);
35013         return true;
35014     },
35015
35016     afterValidDrop : function(){
35017         var v = this.view;
35018         setTimeout(function(){
35019             v.headersDisabled = false;
35020         }, 50);
35021     },
35022
35023     afterInvalidDrop : function(){
35024         var v = this.view;
35025         setTimeout(function(){
35026             v.headersDisabled = false;
35027         }, 50);
35028     }
35029 });
35030 /*
35031  * Based on:
35032  * Ext JS Library 1.1.1
35033  * Copyright(c) 2006-2007, Ext JS, LLC.
35034  *
35035  * Originally Released Under LGPL - original licence link has changed is not relivant.
35036  *
35037  * Fork - LGPL
35038  * <script type="text/javascript">
35039  */
35040 // private
35041 // This is a support class used internally by the Grid components
35042 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
35043     this.grid = grid;
35044     this.view = grid.getView();
35045     // split the proxies so they don't interfere with mouse events
35046     this.proxyTop = Roo.DomHelper.append(document.body, {
35047         cls:"col-move-top", html:"&#160;"
35048     }, true);
35049     this.proxyBottom = Roo.DomHelper.append(document.body, {
35050         cls:"col-move-bottom", html:"&#160;"
35051     }, true);
35052     this.proxyTop.hide = this.proxyBottom.hide = function(){
35053         this.setLeftTop(-100,-100);
35054         this.setStyle("visibility", "hidden");
35055     };
35056     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35057     // temporarily disabled
35058     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
35059     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
35060 };
35061 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
35062     proxyOffsets : [-4, -9],
35063     fly: Roo.Element.fly,
35064
35065     getTargetFromEvent : function(e){
35066         var t = Roo.lib.Event.getTarget(e);
35067         var cindex = this.view.findCellIndex(t);
35068         if(cindex !== false){
35069             return this.view.getHeaderCell(cindex);
35070         }
35071         return null;
35072     },
35073
35074     nextVisible : function(h){
35075         var v = this.view, cm = this.grid.colModel;
35076         h = h.nextSibling;
35077         while(h){
35078             if(!cm.isHidden(v.getCellIndex(h))){
35079                 return h;
35080             }
35081             h = h.nextSibling;
35082         }
35083         return null;
35084     },
35085
35086     prevVisible : function(h){
35087         var v = this.view, cm = this.grid.colModel;
35088         h = h.prevSibling;
35089         while(h){
35090             if(!cm.isHidden(v.getCellIndex(h))){
35091                 return h;
35092             }
35093             h = h.prevSibling;
35094         }
35095         return null;
35096     },
35097
35098     positionIndicator : function(h, n, e){
35099         var x = Roo.lib.Event.getPageX(e);
35100         var r = Roo.lib.Dom.getRegion(n.firstChild);
35101         var px, pt, py = r.top + this.proxyOffsets[1];
35102         if((r.right - x) <= (r.right-r.left)/2){
35103             px = r.right+this.view.borderWidth;
35104             pt = "after";
35105         }else{
35106             px = r.left;
35107             pt = "before";
35108         }
35109         var oldIndex = this.view.getCellIndex(h);
35110         var newIndex = this.view.getCellIndex(n);
35111
35112         if(this.grid.colModel.isFixed(newIndex)){
35113             return false;
35114         }
35115
35116         var locked = this.grid.colModel.isLocked(newIndex);
35117
35118         if(pt == "after"){
35119             newIndex++;
35120         }
35121         if(oldIndex < newIndex){
35122             newIndex--;
35123         }
35124         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
35125             return false;
35126         }
35127         px +=  this.proxyOffsets[0];
35128         this.proxyTop.setLeftTop(px, py);
35129         this.proxyTop.show();
35130         if(!this.bottomOffset){
35131             this.bottomOffset = this.view.mainHd.getHeight();
35132         }
35133         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
35134         this.proxyBottom.show();
35135         return pt;
35136     },
35137
35138     onNodeEnter : function(n, dd, e, data){
35139         if(data.header != n){
35140             this.positionIndicator(data.header, n, e);
35141         }
35142     },
35143
35144     onNodeOver : function(n, dd, e, data){
35145         var result = false;
35146         if(data.header != n){
35147             result = this.positionIndicator(data.header, n, e);
35148         }
35149         if(!result){
35150             this.proxyTop.hide();
35151             this.proxyBottom.hide();
35152         }
35153         return result ? this.dropAllowed : this.dropNotAllowed;
35154     },
35155
35156     onNodeOut : function(n, dd, e, data){
35157         this.proxyTop.hide();
35158         this.proxyBottom.hide();
35159     },
35160
35161     onNodeDrop : function(n, dd, e, data){
35162         var h = data.header;
35163         if(h != n){
35164             var cm = this.grid.colModel;
35165             var x = Roo.lib.Event.getPageX(e);
35166             var r = Roo.lib.Dom.getRegion(n.firstChild);
35167             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
35168             var oldIndex = this.view.getCellIndex(h);
35169             var newIndex = this.view.getCellIndex(n);
35170             var locked = cm.isLocked(newIndex);
35171             if(pt == "after"){
35172                 newIndex++;
35173             }
35174             if(oldIndex < newIndex){
35175                 newIndex--;
35176             }
35177             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
35178                 return false;
35179             }
35180             cm.setLocked(oldIndex, locked, true);
35181             cm.moveColumn(oldIndex, newIndex);
35182             this.grid.fireEvent("columnmove", oldIndex, newIndex);
35183             return true;
35184         }
35185         return false;
35186     }
35187 });
35188 /*
35189  * Based on:
35190  * Ext JS Library 1.1.1
35191  * Copyright(c) 2006-2007, Ext JS, LLC.
35192  *
35193  * Originally Released Under LGPL - original licence link has changed is not relivant.
35194  *
35195  * Fork - LGPL
35196  * <script type="text/javascript">
35197  */
35198   
35199 /**
35200  * @class Roo.grid.GridView
35201  * @extends Roo.util.Observable
35202  *
35203  * @constructor
35204  * @param {Object} config
35205  */
35206 Roo.grid.GridView = function(config){
35207     Roo.grid.GridView.superclass.constructor.call(this);
35208     this.el = null;
35209
35210     Roo.apply(this, config);
35211 };
35212
35213 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
35214
35215     unselectable :  'unselectable="on"',
35216     unselectableCls :  'x-unselectable',
35217     
35218     
35219     rowClass : "x-grid-row",
35220
35221     cellClass : "x-grid-col",
35222
35223     tdClass : "x-grid-td",
35224
35225     hdClass : "x-grid-hd",
35226
35227     splitClass : "x-grid-split",
35228
35229     sortClasses : ["sort-asc", "sort-desc"],
35230
35231     enableMoveAnim : false,
35232
35233     hlColor: "C3DAF9",
35234
35235     dh : Roo.DomHelper,
35236
35237     fly : Roo.Element.fly,
35238
35239     css : Roo.util.CSS,
35240
35241     borderWidth: 1,
35242
35243     splitOffset: 3,
35244
35245     scrollIncrement : 22,
35246
35247     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35248
35249     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35250
35251     bind : function(ds, cm){
35252         if(this.ds){
35253             this.ds.un("load", this.onLoad, this);
35254             this.ds.un("datachanged", this.onDataChange, this);
35255             this.ds.un("add", this.onAdd, this);
35256             this.ds.un("remove", this.onRemove, this);
35257             this.ds.un("update", this.onUpdate, this);
35258             this.ds.un("clear", this.onClear, this);
35259         }
35260         if(ds){
35261             ds.on("load", this.onLoad, this);
35262             ds.on("datachanged", this.onDataChange, this);
35263             ds.on("add", this.onAdd, this);
35264             ds.on("remove", this.onRemove, this);
35265             ds.on("update", this.onUpdate, this);
35266             ds.on("clear", this.onClear, this);
35267         }
35268         this.ds = ds;
35269
35270         if(this.cm){
35271             this.cm.un("widthchange", this.onColWidthChange, this);
35272             this.cm.un("headerchange", this.onHeaderChange, this);
35273             this.cm.un("hiddenchange", this.onHiddenChange, this);
35274             this.cm.un("columnmoved", this.onColumnMove, this);
35275             this.cm.un("columnlockchange", this.onColumnLock, this);
35276         }
35277         if(cm){
35278             this.generateRules(cm);
35279             cm.on("widthchange", this.onColWidthChange, this);
35280             cm.on("headerchange", this.onHeaderChange, this);
35281             cm.on("hiddenchange", this.onHiddenChange, this);
35282             cm.on("columnmoved", this.onColumnMove, this);
35283             cm.on("columnlockchange", this.onColumnLock, this);
35284         }
35285         this.cm = cm;
35286     },
35287
35288     init: function(grid){
35289         Roo.grid.GridView.superclass.init.call(this, grid);
35290
35291         this.bind(grid.dataSource, grid.colModel);
35292
35293         grid.on("headerclick", this.handleHeaderClick, this);
35294
35295         if(grid.trackMouseOver){
35296             grid.on("mouseover", this.onRowOver, this);
35297             grid.on("mouseout", this.onRowOut, this);
35298         }
35299         grid.cancelTextSelection = function(){};
35300         this.gridId = grid.id;
35301
35302         var tpls = this.templates || {};
35303
35304         if(!tpls.master){
35305             tpls.master = new Roo.Template(
35306                '<div class="x-grid" hidefocus="true">',
35307                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35308                   '<div class="x-grid-topbar"></div>',
35309                   '<div class="x-grid-scroller"><div></div></div>',
35310                   '<div class="x-grid-locked">',
35311                       '<div class="x-grid-header">{lockedHeader}</div>',
35312                       '<div class="x-grid-body">{lockedBody}</div>',
35313                   "</div>",
35314                   '<div class="x-grid-viewport">',
35315                       '<div class="x-grid-header">{header}</div>',
35316                       '<div class="x-grid-body">{body}</div>',
35317                   "</div>",
35318                   '<div class="x-grid-bottombar"></div>',
35319                  
35320                   '<div class="x-grid-resize-proxy">&#160;</div>',
35321                "</div>"
35322             );
35323             tpls.master.disableformats = true;
35324         }
35325
35326         if(!tpls.header){
35327             tpls.header = new Roo.Template(
35328                '<table border="0" cellspacing="0" cellpadding="0">',
35329                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35330                "</table>{splits}"
35331             );
35332             tpls.header.disableformats = true;
35333         }
35334         tpls.header.compile();
35335
35336         if(!tpls.hcell){
35337             tpls.hcell = new Roo.Template(
35338                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35339                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35340                 "</div></td>"
35341              );
35342              tpls.hcell.disableFormats = true;
35343         }
35344         tpls.hcell.compile();
35345
35346         if(!tpls.hsplit){
35347             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
35348                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
35349             tpls.hsplit.disableFormats = true;
35350         }
35351         tpls.hsplit.compile();
35352
35353         if(!tpls.body){
35354             tpls.body = new Roo.Template(
35355                '<table border="0" cellspacing="0" cellpadding="0">',
35356                "<tbody>{rows}</tbody>",
35357                "</table>"
35358             );
35359             tpls.body.disableFormats = true;
35360         }
35361         tpls.body.compile();
35362
35363         if(!tpls.row){
35364             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35365             tpls.row.disableFormats = true;
35366         }
35367         tpls.row.compile();
35368
35369         if(!tpls.cell){
35370             tpls.cell = new Roo.Template(
35371                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35372                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
35373                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
35374                 "</td>"
35375             );
35376             tpls.cell.disableFormats = true;
35377         }
35378         tpls.cell.compile();
35379
35380         this.templates = tpls;
35381     },
35382
35383     // remap these for backwards compat
35384     onColWidthChange : function(){
35385         this.updateColumns.apply(this, arguments);
35386     },
35387     onHeaderChange : function(){
35388         this.updateHeaders.apply(this, arguments);
35389     }, 
35390     onHiddenChange : function(){
35391         this.handleHiddenChange.apply(this, arguments);
35392     },
35393     onColumnMove : function(){
35394         this.handleColumnMove.apply(this, arguments);
35395     },
35396     onColumnLock : function(){
35397         this.handleLockChange.apply(this, arguments);
35398     },
35399
35400     onDataChange : function(){
35401         this.refresh();
35402         this.updateHeaderSortState();
35403     },
35404
35405     onClear : function(){
35406         this.refresh();
35407     },
35408
35409     onUpdate : function(ds, record){
35410         this.refreshRow(record);
35411     },
35412
35413     refreshRow : function(record){
35414         var ds = this.ds, index;
35415         if(typeof record == 'number'){
35416             index = record;
35417             record = ds.getAt(index);
35418         }else{
35419             index = ds.indexOf(record);
35420         }
35421         this.insertRows(ds, index, index, true);
35422         this.onRemove(ds, record, index+1, true);
35423         this.syncRowHeights(index, index);
35424         this.layout();
35425         this.fireEvent("rowupdated", this, index, record);
35426     },
35427
35428     onAdd : function(ds, records, index){
35429         this.insertRows(ds, index, index + (records.length-1));
35430     },
35431
35432     onRemove : function(ds, record, index, isUpdate){
35433         if(isUpdate !== true){
35434             this.fireEvent("beforerowremoved", this, index, record);
35435         }
35436         var bt = this.getBodyTable(), lt = this.getLockedTable();
35437         if(bt.rows[index]){
35438             bt.firstChild.removeChild(bt.rows[index]);
35439         }
35440         if(lt.rows[index]){
35441             lt.firstChild.removeChild(lt.rows[index]);
35442         }
35443         if(isUpdate !== true){
35444             this.stripeRows(index);
35445             this.syncRowHeights(index, index);
35446             this.layout();
35447             this.fireEvent("rowremoved", this, index, record);
35448         }
35449     },
35450
35451     onLoad : function(){
35452         this.scrollToTop();
35453     },
35454
35455     /**
35456      * Scrolls the grid to the top
35457      */
35458     scrollToTop : function(){
35459         if(this.scroller){
35460             this.scroller.dom.scrollTop = 0;
35461             this.syncScroll();
35462         }
35463     },
35464
35465     /**
35466      * Gets a panel in the header of the grid that can be used for toolbars etc.
35467      * After modifying the contents of this panel a call to grid.autoSize() may be
35468      * required to register any changes in size.
35469      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35470      * @return Roo.Element
35471      */
35472     getHeaderPanel : function(doShow){
35473         if(doShow){
35474             this.headerPanel.show();
35475         }
35476         return this.headerPanel;
35477     },
35478
35479     /**
35480      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35481      * After modifying the contents of this panel a call to grid.autoSize() may be
35482      * required to register any changes in size.
35483      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35484      * @return Roo.Element
35485      */
35486     getFooterPanel : function(doShow){
35487         if(doShow){
35488             this.footerPanel.show();
35489         }
35490         return this.footerPanel;
35491     },
35492
35493     initElements : function(){
35494         var E = Roo.Element;
35495         var el = this.grid.getGridEl().dom.firstChild;
35496         var cs = el.childNodes;
35497
35498         this.el = new E(el);
35499         
35500          this.focusEl = new E(el.firstChild);
35501         this.focusEl.swallowEvent("click", true);
35502         
35503         this.headerPanel = new E(cs[1]);
35504         this.headerPanel.enableDisplayMode("block");
35505
35506         this.scroller = new E(cs[2]);
35507         this.scrollSizer = new E(this.scroller.dom.firstChild);
35508
35509         this.lockedWrap = new E(cs[3]);
35510         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35511         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35512
35513         this.mainWrap = new E(cs[4]);
35514         this.mainHd = new E(this.mainWrap.dom.firstChild);
35515         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35516
35517         this.footerPanel = new E(cs[5]);
35518         this.footerPanel.enableDisplayMode("block");
35519
35520         this.resizeProxy = new E(cs[6]);
35521
35522         this.headerSelector = String.format(
35523            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35524            this.lockedHd.id, this.mainHd.id
35525         );
35526
35527         this.splitterSelector = String.format(
35528            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35529            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35530         );
35531     },
35532     idToCssName : function(s)
35533     {
35534         return s.replace(/[^a-z0-9]+/ig, '-');
35535     },
35536
35537     getHeaderCell : function(index){
35538         return Roo.DomQuery.select(this.headerSelector)[index];
35539     },
35540
35541     getHeaderCellMeasure : function(index){
35542         return this.getHeaderCell(index).firstChild;
35543     },
35544
35545     getHeaderCellText : function(index){
35546         return this.getHeaderCell(index).firstChild.firstChild;
35547     },
35548
35549     getLockedTable : function(){
35550         return this.lockedBody.dom.firstChild;
35551     },
35552
35553     getBodyTable : function(){
35554         return this.mainBody.dom.firstChild;
35555     },
35556
35557     getLockedRow : function(index){
35558         return this.getLockedTable().rows[index];
35559     },
35560
35561     getRow : function(index){
35562         return this.getBodyTable().rows[index];
35563     },
35564
35565     getRowComposite : function(index){
35566         if(!this.rowEl){
35567             this.rowEl = new Roo.CompositeElementLite();
35568         }
35569         var els = [], lrow, mrow;
35570         if(lrow = this.getLockedRow(index)){
35571             els.push(lrow);
35572         }
35573         if(mrow = this.getRow(index)){
35574             els.push(mrow);
35575         }
35576         this.rowEl.elements = els;
35577         return this.rowEl;
35578     },
35579     /**
35580      * Gets the 'td' of the cell
35581      * 
35582      * @param {Integer} rowIndex row to select
35583      * @param {Integer} colIndex column to select
35584      * 
35585      * @return {Object} 
35586      */
35587     getCell : function(rowIndex, colIndex){
35588         var locked = this.cm.getLockedCount();
35589         var source;
35590         if(colIndex < locked){
35591             source = this.lockedBody.dom.firstChild;
35592         }else{
35593             source = this.mainBody.dom.firstChild;
35594             colIndex -= locked;
35595         }
35596         return source.rows[rowIndex].childNodes[colIndex];
35597     },
35598
35599     getCellText : function(rowIndex, colIndex){
35600         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35601     },
35602
35603     getCellBox : function(cell){
35604         var b = this.fly(cell).getBox();
35605         if(Roo.isOpera){ // opera fails to report the Y
35606             b.y = cell.offsetTop + this.mainBody.getY();
35607         }
35608         return b;
35609     },
35610
35611     getCellIndex : function(cell){
35612         var id = String(cell.className).match(this.cellRE);
35613         if(id){
35614             return parseInt(id[1], 10);
35615         }
35616         return 0;
35617     },
35618
35619     findHeaderIndex : function(n){
35620         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35621         return r ? this.getCellIndex(r) : false;
35622     },
35623
35624     findHeaderCell : function(n){
35625         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35626         return r ? r : false;
35627     },
35628
35629     findRowIndex : function(n){
35630         if(!n){
35631             return false;
35632         }
35633         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35634         return r ? r.rowIndex : false;
35635     },
35636
35637     findCellIndex : function(node){
35638         var stop = this.el.dom;
35639         while(node && node != stop){
35640             if(this.findRE.test(node.className)){
35641                 return this.getCellIndex(node);
35642             }
35643             node = node.parentNode;
35644         }
35645         return false;
35646     },
35647
35648     getColumnId : function(index){
35649         return this.cm.getColumnId(index);
35650     },
35651
35652     getSplitters : function()
35653     {
35654         if(this.splitterSelector){
35655            return Roo.DomQuery.select(this.splitterSelector);
35656         }else{
35657             return null;
35658       }
35659     },
35660
35661     getSplitter : function(index){
35662         return this.getSplitters()[index];
35663     },
35664
35665     onRowOver : function(e, t){
35666         var row;
35667         if((row = this.findRowIndex(t)) !== false){
35668             this.getRowComposite(row).addClass("x-grid-row-over");
35669         }
35670     },
35671
35672     onRowOut : function(e, t){
35673         var row;
35674         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35675             this.getRowComposite(row).removeClass("x-grid-row-over");
35676         }
35677     },
35678
35679     renderHeaders : function(){
35680         var cm = this.cm;
35681         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35682         var cb = [], lb = [], sb = [], lsb = [], p = {};
35683         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35684             p.cellId = "x-grid-hd-0-" + i;
35685             p.splitId = "x-grid-csplit-0-" + i;
35686             p.id = cm.getColumnId(i);
35687             p.title = cm.getColumnTooltip(i) || "";
35688             p.value = cm.getColumnHeader(i) || "";
35689             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35690             if(!cm.isLocked(i)){
35691                 cb[cb.length] = ct.apply(p);
35692                 sb[sb.length] = st.apply(p);
35693             }else{
35694                 lb[lb.length] = ct.apply(p);
35695                 lsb[lsb.length] = st.apply(p);
35696             }
35697         }
35698         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35699                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35700     },
35701
35702     updateHeaders : function(){
35703         var html = this.renderHeaders();
35704         this.lockedHd.update(html[0]);
35705         this.mainHd.update(html[1]);
35706     },
35707
35708     /**
35709      * Focuses the specified row.
35710      * @param {Number} row The row index
35711      */
35712     focusRow : function(row)
35713     {
35714         //Roo.log('GridView.focusRow');
35715         var x = this.scroller.dom.scrollLeft;
35716         this.focusCell(row, 0, false);
35717         this.scroller.dom.scrollLeft = x;
35718     },
35719
35720     /**
35721      * Focuses the specified cell.
35722      * @param {Number} row The row index
35723      * @param {Number} col The column index
35724      * @param {Boolean} hscroll false to disable horizontal scrolling
35725      */
35726     focusCell : function(row, col, hscroll)
35727     {
35728         //Roo.log('GridView.focusCell');
35729         var el = this.ensureVisible(row, col, hscroll);
35730         this.focusEl.alignTo(el, "tl-tl");
35731         if(Roo.isGecko){
35732             this.focusEl.focus();
35733         }else{
35734             this.focusEl.focus.defer(1, this.focusEl);
35735         }
35736     },
35737
35738     /**
35739      * Scrolls the specified cell into view
35740      * @param {Number} row The row index
35741      * @param {Number} col The column index
35742      * @param {Boolean} hscroll false to disable horizontal scrolling
35743      */
35744     ensureVisible : function(row, col, hscroll)
35745     {
35746         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35747         //return null; //disable for testing.
35748         if(typeof row != "number"){
35749             row = row.rowIndex;
35750         }
35751         if(row < 0 && row >= this.ds.getCount()){
35752             return  null;
35753         }
35754         col = (col !== undefined ? col : 0);
35755         var cm = this.grid.colModel;
35756         while(cm.isHidden(col)){
35757             col++;
35758         }
35759
35760         var el = this.getCell(row, col);
35761         if(!el){
35762             return null;
35763         }
35764         var c = this.scroller.dom;
35765
35766         var ctop = parseInt(el.offsetTop, 10);
35767         var cleft = parseInt(el.offsetLeft, 10);
35768         var cbot = ctop + el.offsetHeight;
35769         var cright = cleft + el.offsetWidth;
35770         
35771         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35772         var stop = parseInt(c.scrollTop, 10);
35773         var sleft = parseInt(c.scrollLeft, 10);
35774         var sbot = stop + ch;
35775         var sright = sleft + c.clientWidth;
35776         /*
35777         Roo.log('GridView.ensureVisible:' +
35778                 ' ctop:' + ctop +
35779                 ' c.clientHeight:' + c.clientHeight +
35780                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35781                 ' stop:' + stop +
35782                 ' cbot:' + cbot +
35783                 ' sbot:' + sbot +
35784                 ' ch:' + ch  
35785                 );
35786         */
35787         if(ctop < stop){
35788              c.scrollTop = ctop;
35789             //Roo.log("set scrolltop to ctop DISABLE?");
35790         }else if(cbot > sbot){
35791             //Roo.log("set scrolltop to cbot-ch");
35792             c.scrollTop = cbot-ch;
35793         }
35794         
35795         if(hscroll !== false){
35796             if(cleft < sleft){
35797                 c.scrollLeft = cleft;
35798             }else if(cright > sright){
35799                 c.scrollLeft = cright-c.clientWidth;
35800             }
35801         }
35802          
35803         return el;
35804     },
35805
35806     updateColumns : function(){
35807         this.grid.stopEditing();
35808         var cm = this.grid.colModel, colIds = this.getColumnIds();
35809         //var totalWidth = cm.getTotalWidth();
35810         var pos = 0;
35811         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35812             //if(cm.isHidden(i)) continue;
35813             var w = cm.getColumnWidth(i);
35814             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35815             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35816         }
35817         this.updateSplitters();
35818     },
35819
35820     generateRules : function(cm){
35821         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35822         Roo.util.CSS.removeStyleSheet(rulesId);
35823         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35824             var cid = cm.getColumnId(i);
35825             var align = '';
35826             if(cm.config[i].align){
35827                 align = 'text-align:'+cm.config[i].align+';';
35828             }
35829             var hidden = '';
35830             if(cm.isHidden(i)){
35831                 hidden = 'display:none;';
35832             }
35833             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35834             ruleBuf.push(
35835                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35836                     this.hdSelector, cid, " {\n", align, width, "}\n",
35837                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35838                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35839         }
35840         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35841     },
35842
35843     updateSplitters : function(){
35844         var cm = this.cm, s = this.getSplitters();
35845         if(s){ // splitters not created yet
35846             var pos = 0, locked = true;
35847             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35848                 if(cm.isHidden(i)) continue;
35849                 var w = cm.getColumnWidth(i); // make sure it's a number
35850                 if(!cm.isLocked(i) && locked){
35851                     pos = 0;
35852                     locked = false;
35853                 }
35854                 pos += w;
35855                 s[i].style.left = (pos-this.splitOffset) + "px";
35856             }
35857         }
35858     },
35859
35860     handleHiddenChange : function(colModel, colIndex, hidden){
35861         if(hidden){
35862             this.hideColumn(colIndex);
35863         }else{
35864             this.unhideColumn(colIndex);
35865         }
35866     },
35867
35868     hideColumn : function(colIndex){
35869         var cid = this.getColumnId(colIndex);
35870         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35871         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35872         if(Roo.isSafari){
35873             this.updateHeaders();
35874         }
35875         this.updateSplitters();
35876         this.layout();
35877     },
35878
35879     unhideColumn : function(colIndex){
35880         var cid = this.getColumnId(colIndex);
35881         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35882         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35883
35884         if(Roo.isSafari){
35885             this.updateHeaders();
35886         }
35887         this.updateSplitters();
35888         this.layout();
35889     },
35890
35891     insertRows : function(dm, firstRow, lastRow, isUpdate){
35892         if(firstRow == 0 && lastRow == dm.getCount()-1){
35893             this.refresh();
35894         }else{
35895             if(!isUpdate){
35896                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35897             }
35898             var s = this.getScrollState();
35899             var markup = this.renderRows(firstRow, lastRow);
35900             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35901             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35902             this.restoreScroll(s);
35903             if(!isUpdate){
35904                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35905                 this.syncRowHeights(firstRow, lastRow);
35906                 this.stripeRows(firstRow);
35907                 this.layout();
35908             }
35909         }
35910     },
35911
35912     bufferRows : function(markup, target, index){
35913         var before = null, trows = target.rows, tbody = target.tBodies[0];
35914         if(index < trows.length){
35915             before = trows[index];
35916         }
35917         var b = document.createElement("div");
35918         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35919         var rows = b.firstChild.rows;
35920         for(var i = 0, len = rows.length; i < len; i++){
35921             if(before){
35922                 tbody.insertBefore(rows[0], before);
35923             }else{
35924                 tbody.appendChild(rows[0]);
35925             }
35926         }
35927         b.innerHTML = "";
35928         b = null;
35929     },
35930
35931     deleteRows : function(dm, firstRow, lastRow){
35932         if(dm.getRowCount()<1){
35933             this.fireEvent("beforerefresh", this);
35934             this.mainBody.update("");
35935             this.lockedBody.update("");
35936             this.fireEvent("refresh", this);
35937         }else{
35938             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35939             var bt = this.getBodyTable();
35940             var tbody = bt.firstChild;
35941             var rows = bt.rows;
35942             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35943                 tbody.removeChild(rows[firstRow]);
35944             }
35945             this.stripeRows(firstRow);
35946             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35947         }
35948     },
35949
35950     updateRows : function(dataSource, firstRow, lastRow){
35951         var s = this.getScrollState();
35952         this.refresh();
35953         this.restoreScroll(s);
35954     },
35955
35956     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35957         if(!noRefresh){
35958            this.refresh();
35959         }
35960         this.updateHeaderSortState();
35961     },
35962
35963     getScrollState : function(){
35964         
35965         var sb = this.scroller.dom;
35966         return {left: sb.scrollLeft, top: sb.scrollTop};
35967     },
35968
35969     stripeRows : function(startRow){
35970         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35971             return;
35972         }
35973         startRow = startRow || 0;
35974         var rows = this.getBodyTable().rows;
35975         var lrows = this.getLockedTable().rows;
35976         var cls = ' x-grid-row-alt ';
35977         for(var i = startRow, len = rows.length; i < len; i++){
35978             var row = rows[i], lrow = lrows[i];
35979             var isAlt = ((i+1) % 2 == 0);
35980             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35981             if(isAlt == hasAlt){
35982                 continue;
35983             }
35984             if(isAlt){
35985                 row.className += " x-grid-row-alt";
35986             }else{
35987                 row.className = row.className.replace("x-grid-row-alt", "");
35988             }
35989             if(lrow){
35990                 lrow.className = row.className;
35991             }
35992         }
35993     },
35994
35995     restoreScroll : function(state){
35996         //Roo.log('GridView.restoreScroll');
35997         var sb = this.scroller.dom;
35998         sb.scrollLeft = state.left;
35999         sb.scrollTop = state.top;
36000         this.syncScroll();
36001     },
36002
36003     syncScroll : function(){
36004         //Roo.log('GridView.syncScroll');
36005         var sb = this.scroller.dom;
36006         var sh = this.mainHd.dom;
36007         var bs = this.mainBody.dom;
36008         var lv = this.lockedBody.dom;
36009         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
36010         lv.scrollTop = bs.scrollTop = sb.scrollTop;
36011     },
36012
36013     handleScroll : function(e){
36014         this.syncScroll();
36015         var sb = this.scroller.dom;
36016         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
36017         e.stopEvent();
36018     },
36019
36020     handleWheel : function(e){
36021         var d = e.getWheelDelta();
36022         this.scroller.dom.scrollTop -= d*22;
36023         // set this here to prevent jumpy scrolling on large tables
36024         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
36025         e.stopEvent();
36026     },
36027
36028     renderRows : function(startRow, endRow){
36029         // pull in all the crap needed to render rows
36030         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
36031         var colCount = cm.getColumnCount();
36032
36033         if(ds.getCount() < 1){
36034             return ["", ""];
36035         }
36036
36037         // build a map for all the columns
36038         var cs = [];
36039         for(var i = 0; i < colCount; i++){
36040             var name = cm.getDataIndex(i);
36041             cs[i] = {
36042                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
36043                 renderer : cm.getRenderer(i),
36044                 id : cm.getColumnId(i),
36045                 locked : cm.isLocked(i)
36046             };
36047         }
36048
36049         startRow = startRow || 0;
36050         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
36051
36052         // records to render
36053         var rs = ds.getRange(startRow, endRow);
36054
36055         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
36056     },
36057
36058     // As much as I hate to duplicate code, this was branched because FireFox really hates
36059     // [].join("") on strings. The performance difference was substantial enough to
36060     // branch this function
36061     doRender : Roo.isGecko ?
36062             function(cs, rs, ds, startRow, colCount, stripe){
36063                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36064                 // buffers
36065                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36066                 
36067                 var hasListener = this.grid.hasListener('rowclass');
36068                 var rowcfg = {};
36069                 for(var j = 0, len = rs.length; j < len; j++){
36070                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
36071                     for(var i = 0; i < colCount; i++){
36072                         c = cs[i];
36073                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36074                         p.id = c.id;
36075                         p.css = p.attr = "";
36076                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36077                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36078                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36079                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36080                         }
36081                         var markup = ct.apply(p);
36082                         if(!c.locked){
36083                             cb+= markup;
36084                         }else{
36085                             lcb+= markup;
36086                         }
36087                     }
36088                     var alt = [];
36089                     if(stripe && ((rowIndex+1) % 2 == 0)){
36090                         alt.push("x-grid-row-alt")
36091                     }
36092                     if(r.dirty){
36093                         alt.push(  " x-grid-dirty-row");
36094                     }
36095                     rp.cells = lcb;
36096                     if(this.getRowClass){
36097                         alt.push(this.getRowClass(r, rowIndex));
36098                     }
36099                     if (hasListener) {
36100                         rowcfg = {
36101                              
36102                             record: r,
36103                             rowIndex : rowIndex,
36104                             rowClass : ''
36105                         }
36106                         this.grid.fireEvent('rowclass', this, rowcfg);
36107                         alt.push(rowcfg.rowClass);
36108                     }
36109                     rp.alt = alt.join(" ");
36110                     lbuf+= rt.apply(rp);
36111                     rp.cells = cb;
36112                     buf+=  rt.apply(rp);
36113                 }
36114                 return [lbuf, buf];
36115             } :
36116             function(cs, rs, ds, startRow, colCount, stripe){
36117                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36118                 // buffers
36119                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36120                 var hasListener = this.grid.hasListener('rowclass');
36121  
36122                 var rowcfg = {};
36123                 for(var j = 0, len = rs.length; j < len; j++){
36124                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
36125                     for(var i = 0; i < colCount; i++){
36126                         c = cs[i];
36127                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36128                         p.id = c.id;
36129                         p.css = p.attr = "";
36130                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36131                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36132                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36133                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36134                         }
36135                         
36136                         var markup = ct.apply(p);
36137                         if(!c.locked){
36138                             cb[cb.length] = markup;
36139                         }else{
36140                             lcb[lcb.length] = markup;
36141                         }
36142                     }
36143                     var alt = [];
36144                     if(stripe && ((rowIndex+1) % 2 == 0)){
36145                         alt.push( "x-grid-row-alt");
36146                     }
36147                     if(r.dirty){
36148                         alt.push(" x-grid-dirty-row");
36149                     }
36150                     rp.cells = lcb;
36151                     if(this.getRowClass){
36152                         alt.push( this.getRowClass(r, rowIndex));
36153                     }
36154                     if (hasListener) {
36155                         rowcfg = {
36156                              
36157                             record: r,
36158                             rowIndex : rowIndex,
36159                             rowClass : ''
36160                         }
36161                         this.grid.fireEvent('rowclass', this, rowcfg);
36162                         alt.push(rowcfg.rowClass);
36163                     }
36164                     rp.alt = alt.join(" ");
36165                     rp.cells = lcb.join("");
36166                     lbuf[lbuf.length] = rt.apply(rp);
36167                     rp.cells = cb.join("");
36168                     buf[buf.length] =  rt.apply(rp);
36169                 }
36170                 return [lbuf.join(""), buf.join("")];
36171             },
36172
36173     renderBody : function(){
36174         var markup = this.renderRows();
36175         var bt = this.templates.body;
36176         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
36177     },
36178
36179     /**
36180      * Refreshes the grid
36181      * @param {Boolean} headersToo
36182      */
36183     refresh : function(headersToo){
36184         this.fireEvent("beforerefresh", this);
36185         this.grid.stopEditing();
36186         var result = this.renderBody();
36187         this.lockedBody.update(result[0]);
36188         this.mainBody.update(result[1]);
36189         if(headersToo === true){
36190             this.updateHeaders();
36191             this.updateColumns();
36192             this.updateSplitters();
36193             this.updateHeaderSortState();
36194         }
36195         this.syncRowHeights();
36196         this.layout();
36197         this.fireEvent("refresh", this);
36198     },
36199
36200     handleColumnMove : function(cm, oldIndex, newIndex){
36201         this.indexMap = null;
36202         var s = this.getScrollState();
36203         this.refresh(true);
36204         this.restoreScroll(s);
36205         this.afterMove(newIndex);
36206     },
36207
36208     afterMove : function(colIndex){
36209         if(this.enableMoveAnim && Roo.enableFx){
36210             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
36211         }
36212         // if multisort - fix sortOrder, and reload..
36213         if (this.grid.dataSource.multiSort) {
36214             // the we can call sort again..
36215             var dm = this.grid.dataSource;
36216             var cm = this.grid.colModel;
36217             var so = [];
36218             for(var i = 0; i < cm.config.length; i++ ) {
36219                 
36220                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
36221                     continue; // dont' bother, it's not in sort list or being set.
36222                 }
36223                 
36224                 so.push(cm.config[i].dataIndex);
36225             };
36226             dm.sortOrder = so;
36227             dm.load(dm.lastOptions);
36228             
36229             
36230         }
36231         
36232     },
36233
36234     updateCell : function(dm, rowIndex, dataIndex){
36235         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36236         if(typeof colIndex == "undefined"){ // not present in grid
36237             return;
36238         }
36239         var cm = this.grid.colModel;
36240         var cell = this.getCell(rowIndex, colIndex);
36241         var cellText = this.getCellText(rowIndex, colIndex);
36242
36243         var p = {
36244             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36245             id : cm.getColumnId(colIndex),
36246             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36247         };
36248         var renderer = cm.getRenderer(colIndex);
36249         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36250         if(typeof val == "undefined" || val === "") val = "&#160;";
36251         cellText.innerHTML = val;
36252         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36253         this.syncRowHeights(rowIndex, rowIndex);
36254     },
36255
36256     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36257         var maxWidth = 0;
36258         if(this.grid.autoSizeHeaders){
36259             var h = this.getHeaderCellMeasure(colIndex);
36260             maxWidth = Math.max(maxWidth, h.scrollWidth);
36261         }
36262         var tb, index;
36263         if(this.cm.isLocked(colIndex)){
36264             tb = this.getLockedTable();
36265             index = colIndex;
36266         }else{
36267             tb = this.getBodyTable();
36268             index = colIndex - this.cm.getLockedCount();
36269         }
36270         if(tb && tb.rows){
36271             var rows = tb.rows;
36272             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36273             for(var i = 0; i < stopIndex; i++){
36274                 var cell = rows[i].childNodes[index].firstChild;
36275                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36276             }
36277         }
36278         return maxWidth + /*margin for error in IE*/ 5;
36279     },
36280     /**
36281      * Autofit a column to its content.
36282      * @param {Number} colIndex
36283      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36284      */
36285      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36286          if(this.cm.isHidden(colIndex)){
36287              return; // can't calc a hidden column
36288          }
36289         if(forceMinSize){
36290             var cid = this.cm.getColumnId(colIndex);
36291             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36292            if(this.grid.autoSizeHeaders){
36293                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36294            }
36295         }
36296         var newWidth = this.calcColumnWidth(colIndex);
36297         this.cm.setColumnWidth(colIndex,
36298             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36299         if(!suppressEvent){
36300             this.grid.fireEvent("columnresize", colIndex, newWidth);
36301         }
36302     },
36303
36304     /**
36305      * Autofits all columns to their content and then expands to fit any extra space in the grid
36306      */
36307      autoSizeColumns : function(){
36308         var cm = this.grid.colModel;
36309         var colCount = cm.getColumnCount();
36310         for(var i = 0; i < colCount; i++){
36311             this.autoSizeColumn(i, true, true);
36312         }
36313         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36314             this.fitColumns();
36315         }else{
36316             this.updateColumns();
36317             this.layout();
36318         }
36319     },
36320
36321     /**
36322      * Autofits all columns to the grid's width proportionate with their current size
36323      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36324      */
36325     fitColumns : function(reserveScrollSpace){
36326         var cm = this.grid.colModel;
36327         var colCount = cm.getColumnCount();
36328         var cols = [];
36329         var width = 0;
36330         var i, w;
36331         for (i = 0; i < colCount; i++){
36332             if(!cm.isHidden(i) && !cm.isFixed(i)){
36333                 w = cm.getColumnWidth(i);
36334                 cols.push(i);
36335                 cols.push(w);
36336                 width += w;
36337             }
36338         }
36339         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36340         if(reserveScrollSpace){
36341             avail -= 17;
36342         }
36343         var frac = (avail - cm.getTotalWidth())/width;
36344         while (cols.length){
36345             w = cols.pop();
36346             i = cols.pop();
36347             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36348         }
36349         this.updateColumns();
36350         this.layout();
36351     },
36352
36353     onRowSelect : function(rowIndex){
36354         var row = this.getRowComposite(rowIndex);
36355         row.addClass("x-grid-row-selected");
36356     },
36357
36358     onRowDeselect : function(rowIndex){
36359         var row = this.getRowComposite(rowIndex);
36360         row.removeClass("x-grid-row-selected");
36361     },
36362
36363     onCellSelect : function(row, col){
36364         var cell = this.getCell(row, col);
36365         if(cell){
36366             Roo.fly(cell).addClass("x-grid-cell-selected");
36367         }
36368     },
36369
36370     onCellDeselect : function(row, col){
36371         var cell = this.getCell(row, col);
36372         if(cell){
36373             Roo.fly(cell).removeClass("x-grid-cell-selected");
36374         }
36375     },
36376
36377     updateHeaderSortState : function(){
36378         
36379         // sort state can be single { field: xxx, direction : yyy}
36380         // or   { xxx=>ASC , yyy : DESC ..... }
36381         
36382         var mstate = {};
36383         if (!this.ds.multiSort) { 
36384             var state = this.ds.getSortState();
36385             if(!state){
36386                 return;
36387             }
36388             mstate[state.field] = state.direction;
36389             // FIXME... - this is not used here.. but might be elsewhere..
36390             this.sortState = state;
36391             
36392         } else {
36393             mstate = this.ds.sortToggle;
36394         }
36395         //remove existing sort classes..
36396         
36397         var sc = this.sortClasses;
36398         var hds = this.el.select(this.headerSelector).removeClass(sc);
36399         
36400         for(var f in mstate) {
36401         
36402             var sortColumn = this.cm.findColumnIndex(f);
36403             
36404             if(sortColumn != -1){
36405                 var sortDir = mstate[f];        
36406                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36407             }
36408         }
36409         
36410          
36411         
36412     },
36413
36414
36415     handleHeaderClick : function(g, index){
36416         if(this.headersDisabled){
36417             return;
36418         }
36419         var dm = g.dataSource, cm = g.colModel;
36420         if(!cm.isSortable(index)){
36421             return;
36422         }
36423         g.stopEditing();
36424         
36425         if (dm.multiSort) {
36426             // update the sortOrder
36427             var so = [];
36428             for(var i = 0; i < cm.config.length; i++ ) {
36429                 
36430                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36431                     continue; // dont' bother, it's not in sort list or being set.
36432                 }
36433                 
36434                 so.push(cm.config[i].dataIndex);
36435             };
36436             dm.sortOrder = so;
36437         }
36438         
36439         
36440         dm.sort(cm.getDataIndex(index));
36441     },
36442
36443
36444     destroy : function(){
36445         if(this.colMenu){
36446             this.colMenu.removeAll();
36447             Roo.menu.MenuMgr.unregister(this.colMenu);
36448             this.colMenu.getEl().remove();
36449             delete this.colMenu;
36450         }
36451         if(this.hmenu){
36452             this.hmenu.removeAll();
36453             Roo.menu.MenuMgr.unregister(this.hmenu);
36454             this.hmenu.getEl().remove();
36455             delete this.hmenu;
36456         }
36457         if(this.grid.enableColumnMove){
36458             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36459             if(dds){
36460                 for(var dd in dds){
36461                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36462                         var elid = dds[dd].dragElId;
36463                         dds[dd].unreg();
36464                         Roo.get(elid).remove();
36465                     } else if(dds[dd].config.isTarget){
36466                         dds[dd].proxyTop.remove();
36467                         dds[dd].proxyBottom.remove();
36468                         dds[dd].unreg();
36469                     }
36470                     if(Roo.dd.DDM.locationCache[dd]){
36471                         delete Roo.dd.DDM.locationCache[dd];
36472                     }
36473                 }
36474                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36475             }
36476         }
36477         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36478         this.bind(null, null);
36479         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36480     },
36481
36482     handleLockChange : function(){
36483         this.refresh(true);
36484     },
36485
36486     onDenyColumnLock : function(){
36487
36488     },
36489
36490     onDenyColumnHide : function(){
36491
36492     },
36493
36494     handleHdMenuClick : function(item){
36495         var index = this.hdCtxIndex;
36496         var cm = this.cm, ds = this.ds;
36497         switch(item.id){
36498             case "asc":
36499                 ds.sort(cm.getDataIndex(index), "ASC");
36500                 break;
36501             case "desc":
36502                 ds.sort(cm.getDataIndex(index), "DESC");
36503                 break;
36504             case "lock":
36505                 var lc = cm.getLockedCount();
36506                 if(cm.getColumnCount(true) <= lc+1){
36507                     this.onDenyColumnLock();
36508                     return;
36509                 }
36510                 if(lc != index){
36511                     cm.setLocked(index, true, true);
36512                     cm.moveColumn(index, lc);
36513                     this.grid.fireEvent("columnmove", index, lc);
36514                 }else{
36515                     cm.setLocked(index, true);
36516                 }
36517             break;
36518             case "unlock":
36519                 var lc = cm.getLockedCount();
36520                 if((lc-1) != index){
36521                     cm.setLocked(index, false, true);
36522                     cm.moveColumn(index, lc-1);
36523                     this.grid.fireEvent("columnmove", index, lc-1);
36524                 }else{
36525                     cm.setLocked(index, false);
36526                 }
36527             break;
36528             default:
36529                 index = cm.getIndexById(item.id.substr(4));
36530                 if(index != -1){
36531                     if(item.checked && cm.getColumnCount(true) <= 1){
36532                         this.onDenyColumnHide();
36533                         return false;
36534                     }
36535                     cm.setHidden(index, item.checked);
36536                 }
36537         }
36538         return true;
36539     },
36540
36541     beforeColMenuShow : function(){
36542         var cm = this.cm,  colCount = cm.getColumnCount();
36543         this.colMenu.removeAll();
36544         for(var i = 0; i < colCount; i++){
36545             this.colMenu.add(new Roo.menu.CheckItem({
36546                 id: "col-"+cm.getColumnId(i),
36547                 text: cm.getColumnHeader(i),
36548                 checked: !cm.isHidden(i),
36549                 hideOnClick:false
36550             }));
36551         }
36552     },
36553
36554     handleHdCtx : function(g, index, e){
36555         e.stopEvent();
36556         var hd = this.getHeaderCell(index);
36557         this.hdCtxIndex = index;
36558         var ms = this.hmenu.items, cm = this.cm;
36559         ms.get("asc").setDisabled(!cm.isSortable(index));
36560         ms.get("desc").setDisabled(!cm.isSortable(index));
36561         if(this.grid.enableColLock !== false){
36562             ms.get("lock").setDisabled(cm.isLocked(index));
36563             ms.get("unlock").setDisabled(!cm.isLocked(index));
36564         }
36565         this.hmenu.show(hd, "tl-bl");
36566     },
36567
36568     handleHdOver : function(e){
36569         var hd = this.findHeaderCell(e.getTarget());
36570         if(hd && !this.headersDisabled){
36571             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36572                this.fly(hd).addClass("x-grid-hd-over");
36573             }
36574         }
36575     },
36576
36577     handleHdOut : function(e){
36578         var hd = this.findHeaderCell(e.getTarget());
36579         if(hd){
36580             this.fly(hd).removeClass("x-grid-hd-over");
36581         }
36582     },
36583
36584     handleSplitDblClick : function(e, t){
36585         var i = this.getCellIndex(t);
36586         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36587             this.autoSizeColumn(i, true);
36588             this.layout();
36589         }
36590     },
36591
36592     render : function(){
36593
36594         var cm = this.cm;
36595         var colCount = cm.getColumnCount();
36596
36597         if(this.grid.monitorWindowResize === true){
36598             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36599         }
36600         var header = this.renderHeaders();
36601         var body = this.templates.body.apply({rows:""});
36602         var html = this.templates.master.apply({
36603             lockedBody: body,
36604             body: body,
36605             lockedHeader: header[0],
36606             header: header[1]
36607         });
36608
36609         //this.updateColumns();
36610
36611         this.grid.getGridEl().dom.innerHTML = html;
36612
36613         this.initElements();
36614         
36615         // a kludge to fix the random scolling effect in webkit
36616         this.el.on("scroll", function() {
36617             this.el.dom.scrollTop=0; // hopefully not recursive..
36618         },this);
36619
36620         this.scroller.on("scroll", this.handleScroll, this);
36621         this.lockedBody.on("mousewheel", this.handleWheel, this);
36622         this.mainBody.on("mousewheel", this.handleWheel, this);
36623
36624         this.mainHd.on("mouseover", this.handleHdOver, this);
36625         this.mainHd.on("mouseout", this.handleHdOut, this);
36626         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36627                 {delegate: "."+this.splitClass});
36628
36629         this.lockedHd.on("mouseover", this.handleHdOver, this);
36630         this.lockedHd.on("mouseout", this.handleHdOut, this);
36631         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36632                 {delegate: "."+this.splitClass});
36633
36634         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36635             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36636         }
36637
36638         this.updateSplitters();
36639
36640         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36641             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36642             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36643         }
36644
36645         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36646             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36647             this.hmenu.add(
36648                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36649                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36650             );
36651             if(this.grid.enableColLock !== false){
36652                 this.hmenu.add('-',
36653                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36654                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36655                 );
36656             }
36657             if(this.grid.enableColumnHide !== false){
36658
36659                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36660                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36661                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36662
36663                 this.hmenu.add('-',
36664                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36665                 );
36666             }
36667             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36668
36669             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36670         }
36671
36672         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36673             this.dd = new Roo.grid.GridDragZone(this.grid, {
36674                 ddGroup : this.grid.ddGroup || 'GridDD'
36675             });
36676             
36677         }
36678
36679         /*
36680         for(var i = 0; i < colCount; i++){
36681             if(cm.isHidden(i)){
36682                 this.hideColumn(i);
36683             }
36684             if(cm.config[i].align){
36685                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36686                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36687             }
36688         }*/
36689         
36690         this.updateHeaderSortState();
36691
36692         this.beforeInitialResize();
36693         this.layout(true);
36694
36695         // two part rendering gives faster view to the user
36696         this.renderPhase2.defer(1, this);
36697     },
36698
36699     renderPhase2 : function(){
36700         // render the rows now
36701         this.refresh();
36702         if(this.grid.autoSizeColumns){
36703             this.autoSizeColumns();
36704         }
36705     },
36706
36707     beforeInitialResize : function(){
36708
36709     },
36710
36711     onColumnSplitterMoved : function(i, w){
36712         this.userResized = true;
36713         var cm = this.grid.colModel;
36714         cm.setColumnWidth(i, w, true);
36715         var cid = cm.getColumnId(i);
36716         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36717         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36718         this.updateSplitters();
36719         this.layout();
36720         this.grid.fireEvent("columnresize", i, w);
36721     },
36722
36723     syncRowHeights : function(startIndex, endIndex){
36724         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36725             startIndex = startIndex || 0;
36726             var mrows = this.getBodyTable().rows;
36727             var lrows = this.getLockedTable().rows;
36728             var len = mrows.length-1;
36729             endIndex = Math.min(endIndex || len, len);
36730             for(var i = startIndex; i <= endIndex; i++){
36731                 var m = mrows[i], l = lrows[i];
36732                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36733                 m.style.height = l.style.height = h + "px";
36734             }
36735         }
36736     },
36737
36738     layout : function(initialRender, is2ndPass){
36739         var g = this.grid;
36740         var auto = g.autoHeight;
36741         var scrollOffset = 16;
36742         var c = g.getGridEl(), cm = this.cm,
36743                 expandCol = g.autoExpandColumn,
36744                 gv = this;
36745         //c.beginMeasure();
36746
36747         if(!c.dom.offsetWidth){ // display:none?
36748             if(initialRender){
36749                 this.lockedWrap.show();
36750                 this.mainWrap.show();
36751             }
36752             return;
36753         }
36754
36755         var hasLock = this.cm.isLocked(0);
36756
36757         var tbh = this.headerPanel.getHeight();
36758         var bbh = this.footerPanel.getHeight();
36759
36760         if(auto){
36761             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36762             var newHeight = ch + c.getBorderWidth("tb");
36763             if(g.maxHeight){
36764                 newHeight = Math.min(g.maxHeight, newHeight);
36765             }
36766             c.setHeight(newHeight);
36767         }
36768
36769         if(g.autoWidth){
36770             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36771         }
36772
36773         var s = this.scroller;
36774
36775         var csize = c.getSize(true);
36776
36777         this.el.setSize(csize.width, csize.height);
36778
36779         this.headerPanel.setWidth(csize.width);
36780         this.footerPanel.setWidth(csize.width);
36781
36782         var hdHeight = this.mainHd.getHeight();
36783         var vw = csize.width;
36784         var vh = csize.height - (tbh + bbh);
36785
36786         s.setSize(vw, vh);
36787
36788         var bt = this.getBodyTable();
36789         var ltWidth = hasLock ?
36790                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36791
36792         var scrollHeight = bt.offsetHeight;
36793         var scrollWidth = ltWidth + bt.offsetWidth;
36794         var vscroll = false, hscroll = false;
36795
36796         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36797
36798         var lw = this.lockedWrap, mw = this.mainWrap;
36799         var lb = this.lockedBody, mb = this.mainBody;
36800
36801         setTimeout(function(){
36802             var t = s.dom.offsetTop;
36803             var w = s.dom.clientWidth,
36804                 h = s.dom.clientHeight;
36805
36806             lw.setTop(t);
36807             lw.setSize(ltWidth, h);
36808
36809             mw.setLeftTop(ltWidth, t);
36810             mw.setSize(w-ltWidth, h);
36811
36812             lb.setHeight(h-hdHeight);
36813             mb.setHeight(h-hdHeight);
36814
36815             if(is2ndPass !== true && !gv.userResized && expandCol){
36816                 // high speed resize without full column calculation
36817                 
36818                 var ci = cm.getIndexById(expandCol);
36819                 if (ci < 0) {
36820                     ci = cm.findColumnIndex(expandCol);
36821                 }
36822                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36823                 var expandId = cm.getColumnId(ci);
36824                 var  tw = cm.getTotalWidth(false);
36825                 var currentWidth = cm.getColumnWidth(ci);
36826                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36827                 if(currentWidth != cw){
36828                     cm.setColumnWidth(ci, cw, true);
36829                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36830                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36831                     gv.updateSplitters();
36832                     gv.layout(false, true);
36833                 }
36834             }
36835
36836             if(initialRender){
36837                 lw.show();
36838                 mw.show();
36839             }
36840             //c.endMeasure();
36841         }, 10);
36842     },
36843
36844     onWindowResize : function(){
36845         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36846             return;
36847         }
36848         this.layout();
36849     },
36850
36851     appendFooter : function(parentEl){
36852         return null;
36853     },
36854
36855     sortAscText : "Sort Ascending",
36856     sortDescText : "Sort Descending",
36857     lockText : "Lock Column",
36858     unlockText : "Unlock Column",
36859     columnsText : "Columns"
36860 });
36861
36862
36863 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36864     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36865     this.proxy.el.addClass('x-grid3-col-dd');
36866 };
36867
36868 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36869     handleMouseDown : function(e){
36870
36871     },
36872
36873     callHandleMouseDown : function(e){
36874         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36875     }
36876 });
36877 /*
36878  * Based on:
36879  * Ext JS Library 1.1.1
36880  * Copyright(c) 2006-2007, Ext JS, LLC.
36881  *
36882  * Originally Released Under LGPL - original licence link has changed is not relivant.
36883  *
36884  * Fork - LGPL
36885  * <script type="text/javascript">
36886  */
36887  
36888 // private
36889 // This is a support class used internally by the Grid components
36890 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36891     this.grid = grid;
36892     this.view = grid.getView();
36893     this.proxy = this.view.resizeProxy;
36894     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36895         "gridSplitters" + this.grid.getGridEl().id, {
36896         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36897     });
36898     this.setHandleElId(Roo.id(hd));
36899     this.setOuterHandleElId(Roo.id(hd2));
36900     this.scroll = false;
36901 };
36902 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36903     fly: Roo.Element.fly,
36904
36905     b4StartDrag : function(x, y){
36906         this.view.headersDisabled = true;
36907         this.proxy.setHeight(this.view.mainWrap.getHeight());
36908         var w = this.cm.getColumnWidth(this.cellIndex);
36909         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36910         this.resetConstraints();
36911         this.setXConstraint(minw, 1000);
36912         this.setYConstraint(0, 0);
36913         this.minX = x - minw;
36914         this.maxX = x + 1000;
36915         this.startPos = x;
36916         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36917     },
36918
36919
36920     handleMouseDown : function(e){
36921         ev = Roo.EventObject.setEvent(e);
36922         var t = this.fly(ev.getTarget());
36923         if(t.hasClass("x-grid-split")){
36924             this.cellIndex = this.view.getCellIndex(t.dom);
36925             this.split = t.dom;
36926             this.cm = this.grid.colModel;
36927             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36928                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36929             }
36930         }
36931     },
36932
36933     endDrag : function(e){
36934         this.view.headersDisabled = false;
36935         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36936         var diff = endX - this.startPos;
36937         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36938     },
36939
36940     autoOffset : function(){
36941         this.setDelta(0,0);
36942     }
36943 });/*
36944  * Based on:
36945  * Ext JS Library 1.1.1
36946  * Copyright(c) 2006-2007, Ext JS, LLC.
36947  *
36948  * Originally Released Under LGPL - original licence link has changed is not relivant.
36949  *
36950  * Fork - LGPL
36951  * <script type="text/javascript">
36952  */
36953  
36954 // private
36955 // This is a support class used internally by the Grid components
36956 Roo.grid.GridDragZone = function(grid, config){
36957     this.view = grid.getView();
36958     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36959     if(this.view.lockedBody){
36960         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36961         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36962     }
36963     this.scroll = false;
36964     this.grid = grid;
36965     this.ddel = document.createElement('div');
36966     this.ddel.className = 'x-grid-dd-wrap';
36967 };
36968
36969 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36970     ddGroup : "GridDD",
36971
36972     getDragData : function(e){
36973         var t = Roo.lib.Event.getTarget(e);
36974         var rowIndex = this.view.findRowIndex(t);
36975         var sm = this.grid.selModel;
36976             
36977         //Roo.log(rowIndex);
36978         
36979         if (sm.getSelectedCell) {
36980             // cell selection..
36981             if (!sm.getSelectedCell()) {
36982                 return false;
36983             }
36984             if (rowIndex != sm.getSelectedCell()[0]) {
36985                 return false;
36986             }
36987         
36988         }
36989         
36990         if(rowIndex !== false){
36991             
36992             // if editorgrid.. 
36993             
36994             
36995             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
36996                
36997             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36998               //  
36999             //}
37000             if (e.hasModifier()){
37001                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
37002             }
37003             
37004             Roo.log("getDragData");
37005             
37006             return {
37007                 grid: this.grid,
37008                 ddel: this.ddel,
37009                 rowIndex: rowIndex,
37010                 selections:sm.getSelections ? sm.getSelections() : (
37011                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
37012                 )
37013             };
37014         }
37015         return false;
37016     },
37017
37018     onInitDrag : function(e){
37019         var data = this.dragData;
37020         this.ddel.innerHTML = this.grid.getDragDropText();
37021         this.proxy.update(this.ddel);
37022         // fire start drag?
37023     },
37024
37025     afterRepair : function(){
37026         this.dragging = false;
37027     },
37028
37029     getRepairXY : function(e, data){
37030         return false;
37031     },
37032
37033     onEndDrag : function(data, e){
37034         // fire end drag?
37035     },
37036
37037     onValidDrop : function(dd, e, id){
37038         // fire drag drop?
37039         this.hideProxy();
37040     },
37041
37042     beforeInvalidDrop : function(e, id){
37043
37044     }
37045 });/*
37046  * Based on:
37047  * Ext JS Library 1.1.1
37048  * Copyright(c) 2006-2007, Ext JS, LLC.
37049  *
37050  * Originally Released Under LGPL - original licence link has changed is not relivant.
37051  *
37052  * Fork - LGPL
37053  * <script type="text/javascript">
37054  */
37055  
37056
37057 /**
37058  * @class Roo.grid.ColumnModel
37059  * @extends Roo.util.Observable
37060  * This is the default implementation of a ColumnModel used by the Grid. It defines
37061  * the columns in the grid.
37062  * <br>Usage:<br>
37063  <pre><code>
37064  var colModel = new Roo.grid.ColumnModel([
37065         {header: "Ticker", width: 60, sortable: true, locked: true},
37066         {header: "Company Name", width: 150, sortable: true},
37067         {header: "Market Cap.", width: 100, sortable: true},
37068         {header: "$ Sales", width: 100, sortable: true, renderer: money},
37069         {header: "Employees", width: 100, sortable: true, resizable: false}
37070  ]);
37071  </code></pre>
37072  * <p>
37073  
37074  * The config options listed for this class are options which may appear in each
37075  * individual column definition.
37076  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
37077  * @constructor
37078  * @param {Object} config An Array of column config objects. See this class's
37079  * config objects for details.
37080 */
37081 Roo.grid.ColumnModel = function(config){
37082         /**
37083      * The config passed into the constructor
37084      */
37085     this.config = config;
37086     this.lookup = {};
37087
37088     // if no id, create one
37089     // if the column does not have a dataIndex mapping,
37090     // map it to the order it is in the config
37091     for(var i = 0, len = config.length; i < len; i++){
37092         var c = config[i];
37093         if(typeof c.dataIndex == "undefined"){
37094             c.dataIndex = i;
37095         }
37096         if(typeof c.renderer == "string"){
37097             c.renderer = Roo.util.Format[c.renderer];
37098         }
37099         if(typeof c.id == "undefined"){
37100             c.id = Roo.id();
37101         }
37102         if(c.editor && c.editor.xtype){
37103             c.editor  = Roo.factory(c.editor, Roo.grid);
37104         }
37105         if(c.editor && c.editor.isFormField){
37106             c.editor = new Roo.grid.GridEditor(c.editor);
37107         }
37108         this.lookup[c.id] = c;
37109     }
37110
37111     /**
37112      * The width of columns which have no width specified (defaults to 100)
37113      * @type Number
37114      */
37115     this.defaultWidth = 100;
37116
37117     /**
37118      * Default sortable of columns which have no sortable specified (defaults to false)
37119      * @type Boolean
37120      */
37121     this.defaultSortable = false;
37122
37123     this.addEvents({
37124         /**
37125              * @event widthchange
37126              * Fires when the width of a column changes.
37127              * @param {ColumnModel} this
37128              * @param {Number} columnIndex The column index
37129              * @param {Number} newWidth The new width
37130              */
37131             "widthchange": true,
37132         /**
37133              * @event headerchange
37134              * Fires when the text of a header changes.
37135              * @param {ColumnModel} this
37136              * @param {Number} columnIndex The column index
37137              * @param {Number} newText The new header text
37138              */
37139             "headerchange": true,
37140         /**
37141              * @event hiddenchange
37142              * Fires when a column is hidden or "unhidden".
37143              * @param {ColumnModel} this
37144              * @param {Number} columnIndex The column index
37145              * @param {Boolean} hidden true if hidden, false otherwise
37146              */
37147             "hiddenchange": true,
37148             /**
37149          * @event columnmoved
37150          * Fires when a column is moved.
37151          * @param {ColumnModel} this
37152          * @param {Number} oldIndex
37153          * @param {Number} newIndex
37154          */
37155         "columnmoved" : true,
37156         /**
37157          * @event columlockchange
37158          * Fires when a column's locked state is changed
37159          * @param {ColumnModel} this
37160          * @param {Number} colIndex
37161          * @param {Boolean} locked true if locked
37162          */
37163         "columnlockchange" : true
37164     });
37165     Roo.grid.ColumnModel.superclass.constructor.call(this);
37166 };
37167 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
37168     /**
37169      * @cfg {String} header The header text to display in the Grid view.
37170      */
37171     /**
37172      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
37173      * {@link Roo.data.Record} definition from which to draw the column's value. If not
37174      * specified, the column's index is used as an index into the Record's data Array.
37175      */
37176     /**
37177      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
37178      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
37179      */
37180     /**
37181      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
37182      * Defaults to the value of the {@link #defaultSortable} property.
37183      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
37184      */
37185     /**
37186      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
37187      */
37188     /**
37189      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
37190      */
37191     /**
37192      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
37193      */
37194     /**
37195      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
37196      */
37197     /**
37198      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
37199      * given the cell's data value. See {@link #setRenderer}. If not specified, the
37200      * default renderer uses the raw data value.
37201      */
37202        /**
37203      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
37204      */
37205     /**
37206      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
37207      */
37208
37209     /**
37210      * Returns the id of the column at the specified index.
37211      * @param {Number} index The column index
37212      * @return {String} the id
37213      */
37214     getColumnId : function(index){
37215         return this.config[index].id;
37216     },
37217
37218     /**
37219      * Returns the column for a specified id.
37220      * @param {String} id The column id
37221      * @return {Object} the column
37222      */
37223     getColumnById : function(id){
37224         return this.lookup[id];
37225     },
37226
37227     
37228     /**
37229      * Returns the column for a specified dataIndex.
37230      * @param {String} dataIndex The column dataIndex
37231      * @return {Object|Boolean} the column or false if not found
37232      */
37233     getColumnByDataIndex: function(dataIndex){
37234         var index = this.findColumnIndex(dataIndex);
37235         return index > -1 ? this.config[index] : false;
37236     },
37237     
37238     /**
37239      * Returns the index for a specified column id.
37240      * @param {String} id The column id
37241      * @return {Number} the index, or -1 if not found
37242      */
37243     getIndexById : function(id){
37244         for(var i = 0, len = this.config.length; i < len; i++){
37245             if(this.config[i].id == id){
37246                 return i;
37247             }
37248         }
37249         return -1;
37250     },
37251     
37252     /**
37253      * Returns the index for a specified column dataIndex.
37254      * @param {String} dataIndex The column dataIndex
37255      * @return {Number} the index, or -1 if not found
37256      */
37257     
37258     findColumnIndex : function(dataIndex){
37259         for(var i = 0, len = this.config.length; i < len; i++){
37260             if(this.config[i].dataIndex == dataIndex){
37261                 return i;
37262             }
37263         }
37264         return -1;
37265     },
37266     
37267     
37268     moveColumn : function(oldIndex, newIndex){
37269         var c = this.config[oldIndex];
37270         this.config.splice(oldIndex, 1);
37271         this.config.splice(newIndex, 0, c);
37272         this.dataMap = null;
37273         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37274     },
37275
37276     isLocked : function(colIndex){
37277         return this.config[colIndex].locked === true;
37278     },
37279
37280     setLocked : function(colIndex, value, suppressEvent){
37281         if(this.isLocked(colIndex) == value){
37282             return;
37283         }
37284         this.config[colIndex].locked = value;
37285         if(!suppressEvent){
37286             this.fireEvent("columnlockchange", this, colIndex, value);
37287         }
37288     },
37289
37290     getTotalLockedWidth : function(){
37291         var totalWidth = 0;
37292         for(var i = 0; i < this.config.length; i++){
37293             if(this.isLocked(i) && !this.isHidden(i)){
37294                 this.totalWidth += this.getColumnWidth(i);
37295             }
37296         }
37297         return totalWidth;
37298     },
37299
37300     getLockedCount : function(){
37301         for(var i = 0, len = this.config.length; i < len; i++){
37302             if(!this.isLocked(i)){
37303                 return i;
37304             }
37305         }
37306     },
37307
37308     /**
37309      * Returns the number of columns.
37310      * @return {Number}
37311      */
37312     getColumnCount : function(visibleOnly){
37313         if(visibleOnly === true){
37314             var c = 0;
37315             for(var i = 0, len = this.config.length; i < len; i++){
37316                 if(!this.isHidden(i)){
37317                     c++;
37318                 }
37319             }
37320             return c;
37321         }
37322         return this.config.length;
37323     },
37324
37325     /**
37326      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37327      * @param {Function} fn
37328      * @param {Object} scope (optional)
37329      * @return {Array} result
37330      */
37331     getColumnsBy : function(fn, scope){
37332         var r = [];
37333         for(var i = 0, len = this.config.length; i < len; i++){
37334             var c = this.config[i];
37335             if(fn.call(scope||this, c, i) === true){
37336                 r[r.length] = c;
37337             }
37338         }
37339         return r;
37340     },
37341
37342     /**
37343      * Returns true if the specified column is sortable.
37344      * @param {Number} col The column index
37345      * @return {Boolean}
37346      */
37347     isSortable : function(col){
37348         if(typeof this.config[col].sortable == "undefined"){
37349             return this.defaultSortable;
37350         }
37351         return this.config[col].sortable;
37352     },
37353
37354     /**
37355      * Returns the rendering (formatting) function defined for the column.
37356      * @param {Number} col The column index.
37357      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37358      */
37359     getRenderer : function(col){
37360         if(!this.config[col].renderer){
37361             return Roo.grid.ColumnModel.defaultRenderer;
37362         }
37363         return this.config[col].renderer;
37364     },
37365
37366     /**
37367      * Sets the rendering (formatting) function for a column.
37368      * @param {Number} col The column index
37369      * @param {Function} fn The function to use to process the cell's raw data
37370      * to return HTML markup for the grid view. The render function is called with
37371      * the following parameters:<ul>
37372      * <li>Data value.</li>
37373      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37374      * <li>css A CSS style string to apply to the table cell.</li>
37375      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37376      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37377      * <li>Row index</li>
37378      * <li>Column index</li>
37379      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37380      */
37381     setRenderer : function(col, fn){
37382         this.config[col].renderer = fn;
37383     },
37384
37385     /**
37386      * Returns the width for the specified column.
37387      * @param {Number} col The column index
37388      * @return {Number}
37389      */
37390     getColumnWidth : function(col){
37391         return this.config[col].width * 1 || this.defaultWidth;
37392     },
37393
37394     /**
37395      * Sets the width for a column.
37396      * @param {Number} col The column index
37397      * @param {Number} width The new width
37398      */
37399     setColumnWidth : function(col, width, suppressEvent){
37400         this.config[col].width = width;
37401         this.totalWidth = null;
37402         if(!suppressEvent){
37403              this.fireEvent("widthchange", this, col, width);
37404         }
37405     },
37406
37407     /**
37408      * Returns the total width of all columns.
37409      * @param {Boolean} includeHidden True to include hidden column widths
37410      * @return {Number}
37411      */
37412     getTotalWidth : function(includeHidden){
37413         if(!this.totalWidth){
37414             this.totalWidth = 0;
37415             for(var i = 0, len = this.config.length; i < len; i++){
37416                 if(includeHidden || !this.isHidden(i)){
37417                     this.totalWidth += this.getColumnWidth(i);
37418                 }
37419             }
37420         }
37421         return this.totalWidth;
37422     },
37423
37424     /**
37425      * Returns the header for the specified column.
37426      * @param {Number} col The column index
37427      * @return {String}
37428      */
37429     getColumnHeader : function(col){
37430         return this.config[col].header;
37431     },
37432
37433     /**
37434      * Sets the header for a column.
37435      * @param {Number} col The column index
37436      * @param {String} header The new header
37437      */
37438     setColumnHeader : function(col, header){
37439         this.config[col].header = header;
37440         this.fireEvent("headerchange", this, col, header);
37441     },
37442
37443     /**
37444      * Returns the tooltip for the specified column.
37445      * @param {Number} col The column index
37446      * @return {String}
37447      */
37448     getColumnTooltip : function(col){
37449             return this.config[col].tooltip;
37450     },
37451     /**
37452      * Sets the tooltip for a column.
37453      * @param {Number} col The column index
37454      * @param {String} tooltip The new tooltip
37455      */
37456     setColumnTooltip : function(col, tooltip){
37457             this.config[col].tooltip = tooltip;
37458     },
37459
37460     /**
37461      * Returns the dataIndex for the specified column.
37462      * @param {Number} col The column index
37463      * @return {Number}
37464      */
37465     getDataIndex : function(col){
37466         return this.config[col].dataIndex;
37467     },
37468
37469     /**
37470      * Sets the dataIndex for a column.
37471      * @param {Number} col The column index
37472      * @param {Number} dataIndex The new dataIndex
37473      */
37474     setDataIndex : function(col, dataIndex){
37475         this.config[col].dataIndex = dataIndex;
37476     },
37477
37478     
37479     
37480     /**
37481      * Returns true if the cell is editable.
37482      * @param {Number} colIndex The column index
37483      * @param {Number} rowIndex The row index
37484      * @return {Boolean}
37485      */
37486     isCellEditable : function(colIndex, rowIndex){
37487         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37488     },
37489
37490     /**
37491      * Returns the editor defined for the cell/column.
37492      * return false or null to disable editing.
37493      * @param {Number} colIndex The column index
37494      * @param {Number} rowIndex The row index
37495      * @return {Object}
37496      */
37497     getCellEditor : function(colIndex, rowIndex){
37498         return this.config[colIndex].editor;
37499     },
37500
37501     /**
37502      * Sets if a column is editable.
37503      * @param {Number} col The column index
37504      * @param {Boolean} editable True if the column is editable
37505      */
37506     setEditable : function(col, editable){
37507         this.config[col].editable = editable;
37508     },
37509
37510
37511     /**
37512      * Returns true if the column is hidden.
37513      * @param {Number} colIndex The column index
37514      * @return {Boolean}
37515      */
37516     isHidden : function(colIndex){
37517         return this.config[colIndex].hidden;
37518     },
37519
37520
37521     /**
37522      * Returns true if the column width cannot be changed
37523      */
37524     isFixed : function(colIndex){
37525         return this.config[colIndex].fixed;
37526     },
37527
37528     /**
37529      * Returns true if the column can be resized
37530      * @return {Boolean}
37531      */
37532     isResizable : function(colIndex){
37533         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37534     },
37535     /**
37536      * Sets if a column is hidden.
37537      * @param {Number} colIndex The column index
37538      * @param {Boolean} hidden True if the column is hidden
37539      */
37540     setHidden : function(colIndex, hidden){
37541         this.config[colIndex].hidden = hidden;
37542         this.totalWidth = null;
37543         this.fireEvent("hiddenchange", this, colIndex, hidden);
37544     },
37545
37546     /**
37547      * Sets the editor for a column.
37548      * @param {Number} col The column index
37549      * @param {Object} editor The editor object
37550      */
37551     setEditor : function(col, editor){
37552         this.config[col].editor = editor;
37553     }
37554 });
37555
37556 Roo.grid.ColumnModel.defaultRenderer = function(value){
37557         if(typeof value == "string" && value.length < 1){
37558             return "&#160;";
37559         }
37560         return value;
37561 };
37562
37563 // Alias for backwards compatibility
37564 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
37565 /*
37566  * Based on:
37567  * Ext JS Library 1.1.1
37568  * Copyright(c) 2006-2007, Ext JS, LLC.
37569  *
37570  * Originally Released Under LGPL - original licence link has changed is not relivant.
37571  *
37572  * Fork - LGPL
37573  * <script type="text/javascript">
37574  */
37575
37576 /**
37577  * @class Roo.grid.AbstractSelectionModel
37578  * @extends Roo.util.Observable
37579  * Abstract base class for grid SelectionModels.  It provides the interface that should be
37580  * implemented by descendant classes.  This class should not be directly instantiated.
37581  * @constructor
37582  */
37583 Roo.grid.AbstractSelectionModel = function(){
37584     this.locked = false;
37585     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
37586 };
37587
37588 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
37589     /** @ignore Called by the grid automatically. Do not call directly. */
37590     init : function(grid){
37591         this.grid = grid;
37592         this.initEvents();
37593     },
37594
37595     /**
37596      * Locks the selections.
37597      */
37598     lock : function(){
37599         this.locked = true;
37600     },
37601
37602     /**
37603      * Unlocks the selections.
37604      */
37605     unlock : function(){
37606         this.locked = false;
37607     },
37608
37609     /**
37610      * Returns true if the selections are locked.
37611      * @return {Boolean}
37612      */
37613     isLocked : function(){
37614         return this.locked;
37615     }
37616 });/*
37617  * Based on:
37618  * Ext JS Library 1.1.1
37619  * Copyright(c) 2006-2007, Ext JS, LLC.
37620  *
37621  * Originally Released Under LGPL - original licence link has changed is not relivant.
37622  *
37623  * Fork - LGPL
37624  * <script type="text/javascript">
37625  */
37626 /**
37627  * @extends Roo.grid.AbstractSelectionModel
37628  * @class Roo.grid.RowSelectionModel
37629  * The default SelectionModel used by {@link Roo.grid.Grid}.
37630  * It supports multiple selections and keyboard selection/navigation. 
37631  * @constructor
37632  * @param {Object} config
37633  */
37634 Roo.grid.RowSelectionModel = function(config){
37635     Roo.apply(this, config);
37636     this.selections = new Roo.util.MixedCollection(false, function(o){
37637         return o.id;
37638     });
37639
37640     this.last = false;
37641     this.lastActive = false;
37642
37643     this.addEvents({
37644         /**
37645              * @event selectionchange
37646              * Fires when the selection changes
37647              * @param {SelectionModel} this
37648              */
37649             "selectionchange" : true,
37650         /**
37651              * @event afterselectionchange
37652              * Fires after the selection changes (eg. by key press or clicking)
37653              * @param {SelectionModel} this
37654              */
37655             "afterselectionchange" : true,
37656         /**
37657              * @event beforerowselect
37658              * Fires when a row is selected being selected, return false to cancel.
37659              * @param {SelectionModel} this
37660              * @param {Number} rowIndex The selected index
37661              * @param {Boolean} keepExisting False if other selections will be cleared
37662              */
37663             "beforerowselect" : true,
37664         /**
37665              * @event rowselect
37666              * Fires when a row is selected.
37667              * @param {SelectionModel} this
37668              * @param {Number} rowIndex The selected index
37669              * @param {Roo.data.Record} r The record
37670              */
37671             "rowselect" : true,
37672         /**
37673              * @event rowdeselect
37674              * Fires when a row is deselected.
37675              * @param {SelectionModel} this
37676              * @param {Number} rowIndex The selected index
37677              */
37678         "rowdeselect" : true
37679     });
37680     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37681     this.locked = false;
37682 };
37683
37684 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37685     /**
37686      * @cfg {Boolean} singleSelect
37687      * True to allow selection of only one row at a time (defaults to false)
37688      */
37689     singleSelect : false,
37690
37691     // private
37692     initEvents : function(){
37693
37694         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37695             this.grid.on("mousedown", this.handleMouseDown, this);
37696         }else{ // allow click to work like normal
37697             this.grid.on("rowclick", this.handleDragableRowClick, this);
37698         }
37699
37700         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37701             "up" : function(e){
37702                 if(!e.shiftKey){
37703                     this.selectPrevious(e.shiftKey);
37704                 }else if(this.last !== false && this.lastActive !== false){
37705                     var last = this.last;
37706                     this.selectRange(this.last,  this.lastActive-1);
37707                     this.grid.getView().focusRow(this.lastActive);
37708                     if(last !== false){
37709                         this.last = last;
37710                     }
37711                 }else{
37712                     this.selectFirstRow();
37713                 }
37714                 this.fireEvent("afterselectionchange", this);
37715             },
37716             "down" : function(e){
37717                 if(!e.shiftKey){
37718                     this.selectNext(e.shiftKey);
37719                 }else if(this.last !== false && this.lastActive !== false){
37720                     var last = this.last;
37721                     this.selectRange(this.last,  this.lastActive+1);
37722                     this.grid.getView().focusRow(this.lastActive);
37723                     if(last !== false){
37724                         this.last = last;
37725                     }
37726                 }else{
37727                     this.selectFirstRow();
37728                 }
37729                 this.fireEvent("afterselectionchange", this);
37730             },
37731             scope: this
37732         });
37733
37734         var view = this.grid.view;
37735         view.on("refresh", this.onRefresh, this);
37736         view.on("rowupdated", this.onRowUpdated, this);
37737         view.on("rowremoved", this.onRemove, this);
37738     },
37739
37740     // private
37741     onRefresh : function(){
37742         var ds = this.grid.dataSource, i, v = this.grid.view;
37743         var s = this.selections;
37744         s.each(function(r){
37745             if((i = ds.indexOfId(r.id)) != -1){
37746                 v.onRowSelect(i);
37747             }else{
37748                 s.remove(r);
37749             }
37750         });
37751     },
37752
37753     // private
37754     onRemove : function(v, index, r){
37755         this.selections.remove(r);
37756     },
37757
37758     // private
37759     onRowUpdated : function(v, index, r){
37760         if(this.isSelected(r)){
37761             v.onRowSelect(index);
37762         }
37763     },
37764
37765     /**
37766      * Select records.
37767      * @param {Array} records The records to select
37768      * @param {Boolean} keepExisting (optional) True to keep existing selections
37769      */
37770     selectRecords : function(records, keepExisting){
37771         if(!keepExisting){
37772             this.clearSelections();
37773         }
37774         var ds = this.grid.dataSource;
37775         for(var i = 0, len = records.length; i < len; i++){
37776             this.selectRow(ds.indexOf(records[i]), true);
37777         }
37778     },
37779
37780     /**
37781      * Gets the number of selected rows.
37782      * @return {Number}
37783      */
37784     getCount : function(){
37785         return this.selections.length;
37786     },
37787
37788     /**
37789      * Selects the first row in the grid.
37790      */
37791     selectFirstRow : function(){
37792         this.selectRow(0);
37793     },
37794
37795     /**
37796      * Select the last row.
37797      * @param {Boolean} keepExisting (optional) True to keep existing selections
37798      */
37799     selectLastRow : function(keepExisting){
37800         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37801     },
37802
37803     /**
37804      * Selects the row immediately following the last selected row.
37805      * @param {Boolean} keepExisting (optional) True to keep existing selections
37806      */
37807     selectNext : function(keepExisting){
37808         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37809             this.selectRow(this.last+1, keepExisting);
37810             this.grid.getView().focusRow(this.last);
37811         }
37812     },
37813
37814     /**
37815      * Selects the row that precedes the last selected row.
37816      * @param {Boolean} keepExisting (optional) True to keep existing selections
37817      */
37818     selectPrevious : function(keepExisting){
37819         if(this.last){
37820             this.selectRow(this.last-1, keepExisting);
37821             this.grid.getView().focusRow(this.last);
37822         }
37823     },
37824
37825     /**
37826      * Returns the selected records
37827      * @return {Array} Array of selected records
37828      */
37829     getSelections : function(){
37830         return [].concat(this.selections.items);
37831     },
37832
37833     /**
37834      * Returns the first selected record.
37835      * @return {Record}
37836      */
37837     getSelected : function(){
37838         return this.selections.itemAt(0);
37839     },
37840
37841
37842     /**
37843      * Clears all selections.
37844      */
37845     clearSelections : function(fast){
37846         if(this.locked) return;
37847         if(fast !== true){
37848             var ds = this.grid.dataSource;
37849             var s = this.selections;
37850             s.each(function(r){
37851                 this.deselectRow(ds.indexOfId(r.id));
37852             }, this);
37853             s.clear();
37854         }else{
37855             this.selections.clear();
37856         }
37857         this.last = false;
37858     },
37859
37860
37861     /**
37862      * Selects all rows.
37863      */
37864     selectAll : function(){
37865         if(this.locked) return;
37866         this.selections.clear();
37867         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37868             this.selectRow(i, true);
37869         }
37870     },
37871
37872     /**
37873      * Returns True if there is a selection.
37874      * @return {Boolean}
37875      */
37876     hasSelection : function(){
37877         return this.selections.length > 0;
37878     },
37879
37880     /**
37881      * Returns True if the specified row is selected.
37882      * @param {Number/Record} record The record or index of the record to check
37883      * @return {Boolean}
37884      */
37885     isSelected : function(index){
37886         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37887         return (r && this.selections.key(r.id) ? true : false);
37888     },
37889
37890     /**
37891      * Returns True if the specified record id is selected.
37892      * @param {String} id The id of record to check
37893      * @return {Boolean}
37894      */
37895     isIdSelected : function(id){
37896         return (this.selections.key(id) ? true : false);
37897     },
37898
37899     // private
37900     handleMouseDown : function(e, t){
37901         var view = this.grid.getView(), rowIndex;
37902         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37903             return;
37904         };
37905         if(e.shiftKey && this.last !== false){
37906             var last = this.last;
37907             this.selectRange(last, rowIndex, e.ctrlKey);
37908             this.last = last; // reset the last
37909             view.focusRow(rowIndex);
37910         }else{
37911             var isSelected = this.isSelected(rowIndex);
37912             if(e.button !== 0 && isSelected){
37913                 view.focusRow(rowIndex);
37914             }else if(e.ctrlKey && isSelected){
37915                 this.deselectRow(rowIndex);
37916             }else if(!isSelected){
37917                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37918                 view.focusRow(rowIndex);
37919             }
37920         }
37921         this.fireEvent("afterselectionchange", this);
37922     },
37923     // private
37924     handleDragableRowClick :  function(grid, rowIndex, e) 
37925     {
37926         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37927             this.selectRow(rowIndex, false);
37928             grid.view.focusRow(rowIndex);
37929              this.fireEvent("afterselectionchange", this);
37930         }
37931     },
37932     
37933     /**
37934      * Selects multiple rows.
37935      * @param {Array} rows Array of the indexes of the row to select
37936      * @param {Boolean} keepExisting (optional) True to keep existing selections
37937      */
37938     selectRows : function(rows, keepExisting){
37939         if(!keepExisting){
37940             this.clearSelections();
37941         }
37942         for(var i = 0, len = rows.length; i < len; i++){
37943             this.selectRow(rows[i], true);
37944         }
37945     },
37946
37947     /**
37948      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37949      * @param {Number} startRow The index of the first row in the range
37950      * @param {Number} endRow The index of the last row in the range
37951      * @param {Boolean} keepExisting (optional) True to retain existing selections
37952      */
37953     selectRange : function(startRow, endRow, keepExisting){
37954         if(this.locked) return;
37955         if(!keepExisting){
37956             this.clearSelections();
37957         }
37958         if(startRow <= endRow){
37959             for(var i = startRow; i <= endRow; i++){
37960                 this.selectRow(i, true);
37961             }
37962         }else{
37963             for(var i = startRow; i >= endRow; i--){
37964                 this.selectRow(i, true);
37965             }
37966         }
37967     },
37968
37969     /**
37970      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37971      * @param {Number} startRow The index of the first row in the range
37972      * @param {Number} endRow The index of the last row in the range
37973      */
37974     deselectRange : function(startRow, endRow, preventViewNotify){
37975         if(this.locked) return;
37976         for(var i = startRow; i <= endRow; i++){
37977             this.deselectRow(i, preventViewNotify);
37978         }
37979     },
37980
37981     /**
37982      * Selects a row.
37983      * @param {Number} row The index of the row to select
37984      * @param {Boolean} keepExisting (optional) True to keep existing selections
37985      */
37986     selectRow : function(index, keepExisting, preventViewNotify){
37987         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37988         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37989             if(!keepExisting || this.singleSelect){
37990                 this.clearSelections();
37991             }
37992             var r = this.grid.dataSource.getAt(index);
37993             this.selections.add(r);
37994             this.last = this.lastActive = index;
37995             if(!preventViewNotify){
37996                 this.grid.getView().onRowSelect(index);
37997             }
37998             this.fireEvent("rowselect", this, index, r);
37999             this.fireEvent("selectionchange", this);
38000         }
38001     },
38002
38003     /**
38004      * Deselects a row.
38005      * @param {Number} row The index of the row to deselect
38006      */
38007     deselectRow : function(index, preventViewNotify){
38008         if(this.locked) return;
38009         if(this.last == index){
38010             this.last = false;
38011         }
38012         if(this.lastActive == index){
38013             this.lastActive = false;
38014         }
38015         var r = this.grid.dataSource.getAt(index);
38016         this.selections.remove(r);
38017         if(!preventViewNotify){
38018             this.grid.getView().onRowDeselect(index);
38019         }
38020         this.fireEvent("rowdeselect", this, index);
38021         this.fireEvent("selectionchange", this);
38022     },
38023
38024     // private
38025     restoreLast : function(){
38026         if(this._last){
38027             this.last = this._last;
38028         }
38029     },
38030
38031     // private
38032     acceptsNav : function(row, col, cm){
38033         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38034     },
38035
38036     // private
38037     onEditorKey : function(field, e){
38038         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
38039         if(k == e.TAB){
38040             e.stopEvent();
38041             ed.completeEdit();
38042             if(e.shiftKey){
38043                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38044             }else{
38045                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38046             }
38047         }else if(k == e.ENTER && !e.ctrlKey){
38048             e.stopEvent();
38049             ed.completeEdit();
38050             if(e.shiftKey){
38051                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
38052             }else{
38053                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
38054             }
38055         }else if(k == e.ESC){
38056             ed.cancelEdit();
38057         }
38058         if(newCell){
38059             g.startEditing(newCell[0], newCell[1]);
38060         }
38061     }
38062 });/*
38063  * Based on:
38064  * Ext JS Library 1.1.1
38065  * Copyright(c) 2006-2007, Ext JS, LLC.
38066  *
38067  * Originally Released Under LGPL - original licence link has changed is not relivant.
38068  *
38069  * Fork - LGPL
38070  * <script type="text/javascript">
38071  */
38072 /**
38073  * @class Roo.grid.CellSelectionModel
38074  * @extends Roo.grid.AbstractSelectionModel
38075  * This class provides the basic implementation for cell selection in a grid.
38076  * @constructor
38077  * @param {Object} config The object containing the configuration of this model.
38078  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
38079  */
38080 Roo.grid.CellSelectionModel = function(config){
38081     Roo.apply(this, config);
38082
38083     this.selection = null;
38084
38085     this.addEvents({
38086         /**
38087              * @event beforerowselect
38088              * Fires before a cell is selected.
38089              * @param {SelectionModel} this
38090              * @param {Number} rowIndex The selected row index
38091              * @param {Number} colIndex The selected cell index
38092              */
38093             "beforecellselect" : true,
38094         /**
38095              * @event cellselect
38096              * Fires when a cell is selected.
38097              * @param {SelectionModel} this
38098              * @param {Number} rowIndex The selected row index
38099              * @param {Number} colIndex The selected cell index
38100              */
38101             "cellselect" : true,
38102         /**
38103              * @event selectionchange
38104              * Fires when the active selection changes.
38105              * @param {SelectionModel} this
38106              * @param {Object} selection null for no selection or an object (o) with two properties
38107                 <ul>
38108                 <li>o.record: the record object for the row the selection is in</li>
38109                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
38110                 </ul>
38111              */
38112             "selectionchange" : true,
38113         /**
38114              * @event tabend
38115              * Fires when the tab (or enter) was pressed on the last editable cell
38116              * You can use this to trigger add new row.
38117              * @param {SelectionModel} this
38118              */
38119             "tabend" : true,
38120          /**
38121              * @event beforeeditnext
38122              * Fires before the next editable sell is made active
38123              * You can use this to skip to another cell or fire the tabend
38124              *    if you set cell to false
38125              * @param {Object} eventdata object : { cell : [ row, col ] } 
38126              */
38127             "beforeeditnext" : true
38128     });
38129     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
38130 };
38131
38132 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
38133     
38134     enter_is_tab: false,
38135
38136     /** @ignore */
38137     initEvents : function(){
38138         this.grid.on("mousedown", this.handleMouseDown, this);
38139         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
38140         var view = this.grid.view;
38141         view.on("refresh", this.onViewChange, this);
38142         view.on("rowupdated", this.onRowUpdated, this);
38143         view.on("beforerowremoved", this.clearSelections, this);
38144         view.on("beforerowsinserted", this.clearSelections, this);
38145         if(this.grid.isEditor){
38146             this.grid.on("beforeedit", this.beforeEdit,  this);
38147         }
38148     },
38149
38150         //private
38151     beforeEdit : function(e){
38152         this.select(e.row, e.column, false, true, e.record);
38153     },
38154
38155         //private
38156     onRowUpdated : function(v, index, r){
38157         if(this.selection && this.selection.record == r){
38158             v.onCellSelect(index, this.selection.cell[1]);
38159         }
38160     },
38161
38162         //private
38163     onViewChange : function(){
38164         this.clearSelections(true);
38165     },
38166
38167         /**
38168          * Returns the currently selected cell,.
38169          * @return {Array} The selected cell (row, column) or null if none selected.
38170          */
38171     getSelectedCell : function(){
38172         return this.selection ? this.selection.cell : null;
38173     },
38174
38175     /**
38176      * Clears all selections.
38177      * @param {Boolean} true to prevent the gridview from being notified about the change.
38178      */
38179     clearSelections : function(preventNotify){
38180         var s = this.selection;
38181         if(s){
38182             if(preventNotify !== true){
38183                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
38184             }
38185             this.selection = null;
38186             this.fireEvent("selectionchange", this, null);
38187         }
38188     },
38189
38190     /**
38191      * Returns true if there is a selection.
38192      * @return {Boolean}
38193      */
38194     hasSelection : function(){
38195         return this.selection ? true : false;
38196     },
38197
38198     /** @ignore */
38199     handleMouseDown : function(e, t){
38200         var v = this.grid.getView();
38201         if(this.isLocked()){
38202             return;
38203         };
38204         var row = v.findRowIndex(t);
38205         var cell = v.findCellIndex(t);
38206         if(row !== false && cell !== false){
38207             this.select(row, cell);
38208         }
38209     },
38210
38211     /**
38212      * Selects a cell.
38213      * @param {Number} rowIndex
38214      * @param {Number} collIndex
38215      */
38216     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38217         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38218             this.clearSelections();
38219             r = r || this.grid.dataSource.getAt(rowIndex);
38220             this.selection = {
38221                 record : r,
38222                 cell : [rowIndex, colIndex]
38223             };
38224             if(!preventViewNotify){
38225                 var v = this.grid.getView();
38226                 v.onCellSelect(rowIndex, colIndex);
38227                 if(preventFocus !== true){
38228                     v.focusCell(rowIndex, colIndex);
38229                 }
38230             }
38231             this.fireEvent("cellselect", this, rowIndex, colIndex);
38232             this.fireEvent("selectionchange", this, this.selection);
38233         }
38234     },
38235
38236         //private
38237     isSelectable : function(rowIndex, colIndex, cm){
38238         return !cm.isHidden(colIndex);
38239     },
38240
38241     /** @ignore */
38242     handleKeyDown : function(e){
38243         //Roo.log('Cell Sel Model handleKeyDown');
38244         if(!e.isNavKeyPress()){
38245             return;
38246         }
38247         var g = this.grid, s = this.selection;
38248         if(!s){
38249             e.stopEvent();
38250             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38251             if(cell){
38252                 this.select(cell[0], cell[1]);
38253             }
38254             return;
38255         }
38256         var sm = this;
38257         var walk = function(row, col, step){
38258             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38259         };
38260         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38261         var newCell;
38262
38263       
38264
38265         switch(k){
38266             case e.TAB:
38267                 // handled by onEditorKey
38268                 if (g.isEditor && g.editing) {
38269                     return;
38270                 }
38271                 if(e.shiftKey) {
38272                     newCell = walk(r, c-1, -1);
38273                 } else {
38274                     newCell = walk(r, c+1, 1);
38275                 }
38276                 break;
38277             
38278             case e.DOWN:
38279                newCell = walk(r+1, c, 1);
38280                 break;
38281             
38282             case e.UP:
38283                 newCell = walk(r-1, c, -1);
38284                 break;
38285             
38286             case e.RIGHT:
38287                 newCell = walk(r, c+1, 1);
38288                 break;
38289             
38290             case e.LEFT:
38291                 newCell = walk(r, c-1, -1);
38292                 break;
38293             
38294             case e.ENTER:
38295                 
38296                 if(g.isEditor && !g.editing){
38297                    g.startEditing(r, c);
38298                    e.stopEvent();
38299                    return;
38300                 }
38301                 
38302                 
38303              break;
38304         };
38305         if(newCell){
38306             this.select(newCell[0], newCell[1]);
38307             e.stopEvent();
38308             
38309         }
38310     },
38311
38312     acceptsNav : function(row, col, cm){
38313         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38314     },
38315     /**
38316      * Selects a cell.
38317      * @param {Number} field (not used) - as it's normally used as a listener
38318      * @param {Number} e - event - fake it by using
38319      *
38320      * var e = Roo.EventObjectImpl.prototype;
38321      * e.keyCode = e.TAB
38322      *
38323      * 
38324      */
38325     onEditorKey : function(field, e){
38326         
38327         var k = e.getKey(),
38328             newCell,
38329             g = this.grid,
38330             ed = g.activeEditor,
38331             forward = false;
38332         ///Roo.log('onEditorKey' + k);
38333         
38334         
38335         if (this.enter_is_tab && k == e.ENTER) {
38336             k = e.TAB;
38337         }
38338         
38339         if(k == e.TAB){
38340             if(e.shiftKey){
38341                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38342             }else{
38343                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38344                 forward = true;
38345             }
38346             
38347             e.stopEvent();
38348             
38349         } else if(k == e.ENTER &&  !e.ctrlKey){
38350             ed.completeEdit();
38351             e.stopEvent();
38352             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38353         
38354                 } else if(k == e.ESC){
38355             ed.cancelEdit();
38356         }
38357                 
38358         if (newCell) {
38359             var ecall = { cell : newCell, forward : forward };
38360             this.fireEvent('beforeeditnext', ecall );
38361             newCell = ecall.cell;
38362                         forward = ecall.forward;
38363         }
38364                 
38365         if(newCell){
38366             //Roo.log('next cell after edit');
38367             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38368         } else if (forward) {
38369             // tabbed past last
38370             this.fireEvent.defer(100, this, ['tabend',this]);
38371         }
38372     }
38373 });/*
38374  * Based on:
38375  * Ext JS Library 1.1.1
38376  * Copyright(c) 2006-2007, Ext JS, LLC.
38377  *
38378  * Originally Released Under LGPL - original licence link has changed is not relivant.
38379  *
38380  * Fork - LGPL
38381  * <script type="text/javascript">
38382  */
38383  
38384 /**
38385  * @class Roo.grid.EditorGrid
38386  * @extends Roo.grid.Grid
38387  * Class for creating and editable grid.
38388  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38389  * The container MUST have some type of size defined for the grid to fill. The container will be 
38390  * automatically set to position relative if it isn't already.
38391  * @param {Object} dataSource The data model to bind to
38392  * @param {Object} colModel The column model with info about this grid's columns
38393  */
38394 Roo.grid.EditorGrid = function(container, config){
38395     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38396     this.getGridEl().addClass("xedit-grid");
38397
38398     if(!this.selModel){
38399         this.selModel = new Roo.grid.CellSelectionModel();
38400     }
38401
38402     this.activeEditor = null;
38403
38404         this.addEvents({
38405             /**
38406              * @event beforeedit
38407              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38408              * <ul style="padding:5px;padding-left:16px;">
38409              * <li>grid - This grid</li>
38410              * <li>record - The record being edited</li>
38411              * <li>field - The field name being edited</li>
38412              * <li>value - The value for the field being edited.</li>
38413              * <li>row - The grid row index</li>
38414              * <li>column - The grid column index</li>
38415              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38416              * </ul>
38417              * @param {Object} e An edit event (see above for description)
38418              */
38419             "beforeedit" : true,
38420             /**
38421              * @event afteredit
38422              * Fires after a cell is edited. <br />
38423              * <ul style="padding:5px;padding-left:16px;">
38424              * <li>grid - This grid</li>
38425              * <li>record - The record being edited</li>
38426              * <li>field - The field name being edited</li>
38427              * <li>value - The value being set</li>
38428              * <li>originalValue - The original value for the field, before the edit.</li>
38429              * <li>row - The grid row index</li>
38430              * <li>column - The grid column index</li>
38431              * </ul>
38432              * @param {Object} e An edit event (see above for description)
38433              */
38434             "afteredit" : true,
38435             /**
38436              * @event validateedit
38437              * Fires after a cell is edited, but before the value is set in the record. 
38438          * You can use this to modify the value being set in the field, Return false
38439              * to cancel the change. The edit event object has the following properties <br />
38440              * <ul style="padding:5px;padding-left:16px;">
38441          * <li>editor - This editor</li>
38442              * <li>grid - This grid</li>
38443              * <li>record - The record being edited</li>
38444              * <li>field - The field name being edited</li>
38445              * <li>value - The value being set</li>
38446              * <li>originalValue - The original value for the field, before the edit.</li>
38447              * <li>row - The grid row index</li>
38448              * <li>column - The grid column index</li>
38449              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38450              * </ul>
38451              * @param {Object} e An edit event (see above for description)
38452              */
38453             "validateedit" : true
38454         });
38455     this.on("bodyscroll", this.stopEditing,  this);
38456     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38457 };
38458
38459 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38460     /**
38461      * @cfg {Number} clicksToEdit
38462      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38463      */
38464     clicksToEdit: 2,
38465
38466     // private
38467     isEditor : true,
38468     // private
38469     trackMouseOver: false, // causes very odd FF errors
38470
38471     onCellDblClick : function(g, row, col){
38472         this.startEditing(row, col);
38473     },
38474
38475     onEditComplete : function(ed, value, startValue){
38476         this.editing = false;
38477         this.activeEditor = null;
38478         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38479         var r = ed.record;
38480         var field = this.colModel.getDataIndex(ed.col);
38481         var e = {
38482             grid: this,
38483             record: r,
38484             field: field,
38485             originalValue: startValue,
38486             value: value,
38487             row: ed.row,
38488             column: ed.col,
38489             cancel:false,
38490             editor: ed
38491         };
38492         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
38493         cell.show();
38494           
38495         if(String(value) !== String(startValue)){
38496             
38497             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38498                 r.set(field, e.value);
38499                 // if we are dealing with a combo box..
38500                 // then we also set the 'name' colum to be the displayField
38501                 if (ed.field.displayField && ed.field.name) {
38502                     r.set(ed.field.name, ed.field.el.dom.value);
38503                 }
38504                 
38505                 delete e.cancel; //?? why!!!
38506                 this.fireEvent("afteredit", e);
38507             }
38508         } else {
38509             this.fireEvent("afteredit", e); // always fire it!
38510         }
38511         this.view.focusCell(ed.row, ed.col);
38512     },
38513
38514     /**
38515      * Starts editing the specified for the specified row/column
38516      * @param {Number} rowIndex
38517      * @param {Number} colIndex
38518      */
38519     startEditing : function(row, col){
38520         this.stopEditing();
38521         if(this.colModel.isCellEditable(col, row)){
38522             this.view.ensureVisible(row, col, true);
38523           
38524             var r = this.dataSource.getAt(row);
38525             var field = this.colModel.getDataIndex(col);
38526             var cell = Roo.get(this.view.getCell(row,col));
38527             var e = {
38528                 grid: this,
38529                 record: r,
38530                 field: field,
38531                 value: r.data[field],
38532                 row: row,
38533                 column: col,
38534                 cancel:false 
38535             };
38536             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38537                 this.editing = true;
38538                 var ed = this.colModel.getCellEditor(col, row);
38539                 
38540                 if (!ed) {
38541                     return;
38542                 }
38543                 if(!ed.rendered){
38544                     ed.render(ed.parentEl || document.body);
38545                 }
38546                 ed.field.reset();
38547                
38548                 cell.hide();
38549                 
38550                 (function(){ // complex but required for focus issues in safari, ie and opera
38551                     ed.row = row;
38552                     ed.col = col;
38553                     ed.record = r;
38554                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38555                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
38556                     this.activeEditor = ed;
38557                     var v = r.data[field];
38558                     ed.startEdit(this.view.getCell(row, col), v);
38559                     // combo's with 'displayField and name set
38560                     if (ed.field.displayField && ed.field.name) {
38561                         ed.field.el.dom.value = r.data[ed.field.name];
38562                     }
38563                     
38564                     
38565                 }).defer(50, this);
38566             }
38567         }
38568     },
38569         
38570     /**
38571      * Stops any active editing
38572      */
38573     stopEditing : function(){
38574         if(this.activeEditor){
38575             this.activeEditor.completeEdit();
38576         }
38577         this.activeEditor = null;
38578     },
38579         
38580          /**
38581      * Called to get grid's drag proxy text, by default returns this.ddText.
38582      * @return {String}
38583      */
38584     getDragDropText : function(){
38585         var count = this.selModel.getSelectedCell() ? 1 : 0;
38586         return String.format(this.ddText, count, count == 1 ? '' : 's');
38587     }
38588         
38589 });/*
38590  * Based on:
38591  * Ext JS Library 1.1.1
38592  * Copyright(c) 2006-2007, Ext JS, LLC.
38593  *
38594  * Originally Released Under LGPL - original licence link has changed is not relivant.
38595  *
38596  * Fork - LGPL
38597  * <script type="text/javascript">
38598  */
38599
38600 // private - not really -- you end up using it !
38601 // This is a support class used internally by the Grid components
38602
38603 /**
38604  * @class Roo.grid.GridEditor
38605  * @extends Roo.Editor
38606  * Class for creating and editable grid elements.
38607  * @param {Object} config any settings (must include field)
38608  */
38609 Roo.grid.GridEditor = function(field, config){
38610     if (!config && field.field) {
38611         config = field;
38612         field = Roo.factory(config.field, Roo.form);
38613     }
38614     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
38615     field.monitorTab = false;
38616 };
38617
38618 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
38619     
38620     /**
38621      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
38622      */
38623     
38624     alignment: "tl-tl",
38625     autoSize: "width",
38626     hideEl : false,
38627     cls: "x-small-editor x-grid-editor",
38628     shim:false,
38629     shadow:"frame"
38630 });/*
38631  * Based on:
38632  * Ext JS Library 1.1.1
38633  * Copyright(c) 2006-2007, Ext JS, LLC.
38634  *
38635  * Originally Released Under LGPL - original licence link has changed is not relivant.
38636  *
38637  * Fork - LGPL
38638  * <script type="text/javascript">
38639  */
38640   
38641
38642   
38643 Roo.grid.PropertyRecord = Roo.data.Record.create([
38644     {name:'name',type:'string'},  'value'
38645 ]);
38646
38647
38648 Roo.grid.PropertyStore = function(grid, source){
38649     this.grid = grid;
38650     this.store = new Roo.data.Store({
38651         recordType : Roo.grid.PropertyRecord
38652     });
38653     this.store.on('update', this.onUpdate,  this);
38654     if(source){
38655         this.setSource(source);
38656     }
38657     Roo.grid.PropertyStore.superclass.constructor.call(this);
38658 };
38659
38660
38661
38662 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38663     setSource : function(o){
38664         this.source = o;
38665         this.store.removeAll();
38666         var data = [];
38667         for(var k in o){
38668             if(this.isEditableValue(o[k])){
38669                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38670             }
38671         }
38672         this.store.loadRecords({records: data}, {}, true);
38673     },
38674
38675     onUpdate : function(ds, record, type){
38676         if(type == Roo.data.Record.EDIT){
38677             var v = record.data['value'];
38678             var oldValue = record.modified['value'];
38679             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38680                 this.source[record.id] = v;
38681                 record.commit();
38682                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38683             }else{
38684                 record.reject();
38685             }
38686         }
38687     },
38688
38689     getProperty : function(row){
38690        return this.store.getAt(row);
38691     },
38692
38693     isEditableValue: function(val){
38694         if(val && val instanceof Date){
38695             return true;
38696         }else if(typeof val == 'object' || typeof val == 'function'){
38697             return false;
38698         }
38699         return true;
38700     },
38701
38702     setValue : function(prop, value){
38703         this.source[prop] = value;
38704         this.store.getById(prop).set('value', value);
38705     },
38706
38707     getSource : function(){
38708         return this.source;
38709     }
38710 });
38711
38712 Roo.grid.PropertyColumnModel = function(grid, store){
38713     this.grid = grid;
38714     var g = Roo.grid;
38715     g.PropertyColumnModel.superclass.constructor.call(this, [
38716         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38717         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38718     ]);
38719     this.store = store;
38720     this.bselect = Roo.DomHelper.append(document.body, {
38721         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38722             {tag: 'option', value: 'true', html: 'true'},
38723             {tag: 'option', value: 'false', html: 'false'}
38724         ]
38725     });
38726     Roo.id(this.bselect);
38727     var f = Roo.form;
38728     this.editors = {
38729         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38730         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38731         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38732         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38733         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38734     };
38735     this.renderCellDelegate = this.renderCell.createDelegate(this);
38736     this.renderPropDelegate = this.renderProp.createDelegate(this);
38737 };
38738
38739 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38740     
38741     
38742     nameText : 'Name',
38743     valueText : 'Value',
38744     
38745     dateFormat : 'm/j/Y',
38746     
38747     
38748     renderDate : function(dateVal){
38749         return dateVal.dateFormat(this.dateFormat);
38750     },
38751
38752     renderBool : function(bVal){
38753         return bVal ? 'true' : 'false';
38754     },
38755
38756     isCellEditable : function(colIndex, rowIndex){
38757         return colIndex == 1;
38758     },
38759
38760     getRenderer : function(col){
38761         return col == 1 ?
38762             this.renderCellDelegate : this.renderPropDelegate;
38763     },
38764
38765     renderProp : function(v){
38766         return this.getPropertyName(v);
38767     },
38768
38769     renderCell : function(val){
38770         var rv = val;
38771         if(val instanceof Date){
38772             rv = this.renderDate(val);
38773         }else if(typeof val == 'boolean'){
38774             rv = this.renderBool(val);
38775         }
38776         return Roo.util.Format.htmlEncode(rv);
38777     },
38778
38779     getPropertyName : function(name){
38780         var pn = this.grid.propertyNames;
38781         return pn && pn[name] ? pn[name] : name;
38782     },
38783
38784     getCellEditor : function(colIndex, rowIndex){
38785         var p = this.store.getProperty(rowIndex);
38786         var n = p.data['name'], val = p.data['value'];
38787         
38788         if(typeof(this.grid.customEditors[n]) == 'string'){
38789             return this.editors[this.grid.customEditors[n]];
38790         }
38791         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38792             return this.grid.customEditors[n];
38793         }
38794         if(val instanceof Date){
38795             return this.editors['date'];
38796         }else if(typeof val == 'number'){
38797             return this.editors['number'];
38798         }else if(typeof val == 'boolean'){
38799             return this.editors['boolean'];
38800         }else{
38801             return this.editors['string'];
38802         }
38803     }
38804 });
38805
38806 /**
38807  * @class Roo.grid.PropertyGrid
38808  * @extends Roo.grid.EditorGrid
38809  * This class represents the  interface of a component based property grid control.
38810  * <br><br>Usage:<pre><code>
38811  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38812       
38813  });
38814  // set any options
38815  grid.render();
38816  * </code></pre>
38817   
38818  * @constructor
38819  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38820  * The container MUST have some type of size defined for the grid to fill. The container will be
38821  * automatically set to position relative if it isn't already.
38822  * @param {Object} config A config object that sets properties on this grid.
38823  */
38824 Roo.grid.PropertyGrid = function(container, config){
38825     config = config || {};
38826     var store = new Roo.grid.PropertyStore(this);
38827     this.store = store;
38828     var cm = new Roo.grid.PropertyColumnModel(this, store);
38829     store.store.sort('name', 'ASC');
38830     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38831         ds: store.store,
38832         cm: cm,
38833         enableColLock:false,
38834         enableColumnMove:false,
38835         stripeRows:false,
38836         trackMouseOver: false,
38837         clicksToEdit:1
38838     }, config));
38839     this.getGridEl().addClass('x-props-grid');
38840     this.lastEditRow = null;
38841     this.on('columnresize', this.onColumnResize, this);
38842     this.addEvents({
38843          /**
38844              * @event beforepropertychange
38845              * Fires before a property changes (return false to stop?)
38846              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38847              * @param {String} id Record Id
38848              * @param {String} newval New Value
38849          * @param {String} oldval Old Value
38850              */
38851         "beforepropertychange": true,
38852         /**
38853              * @event propertychange
38854              * Fires after a property changes
38855              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38856              * @param {String} id Record Id
38857              * @param {String} newval New Value
38858          * @param {String} oldval Old Value
38859              */
38860         "propertychange": true
38861     });
38862     this.customEditors = this.customEditors || {};
38863 };
38864 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38865     
38866      /**
38867      * @cfg {Object} customEditors map of colnames=> custom editors.
38868      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38869      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38870      * false disables editing of the field.
38871          */
38872     
38873       /**
38874      * @cfg {Object} propertyNames map of property Names to their displayed value
38875          */
38876     
38877     render : function(){
38878         Roo.grid.PropertyGrid.superclass.render.call(this);
38879         this.autoSize.defer(100, this);
38880     },
38881
38882     autoSize : function(){
38883         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38884         if(this.view){
38885             this.view.fitColumns();
38886         }
38887     },
38888
38889     onColumnResize : function(){
38890         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38891         this.autoSize();
38892     },
38893     /**
38894      * Sets the data for the Grid
38895      * accepts a Key => Value object of all the elements avaiable.
38896      * @param {Object} data  to appear in grid.
38897      */
38898     setSource : function(source){
38899         this.store.setSource(source);
38900         //this.autoSize();
38901     },
38902     /**
38903      * Gets all the data from the grid.
38904      * @return {Object} data  data stored in grid
38905      */
38906     getSource : function(){
38907         return this.store.getSource();
38908     }
38909 });/*
38910  * Based on:
38911  * Ext JS Library 1.1.1
38912  * Copyright(c) 2006-2007, Ext JS, LLC.
38913  *
38914  * Originally Released Under LGPL - original licence link has changed is not relivant.
38915  *
38916  * Fork - LGPL
38917  * <script type="text/javascript">
38918  */
38919  
38920 /**
38921  * @class Roo.LoadMask
38922  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38923  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38924  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38925  * element's UpdateManager load indicator and will be destroyed after the initial load.
38926  * @constructor
38927  * Create a new LoadMask
38928  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38929  * @param {Object} config The config object
38930  */
38931 Roo.LoadMask = function(el, config){
38932     this.el = Roo.get(el);
38933     Roo.apply(this, config);
38934     if(this.store){
38935         this.store.on('beforeload', this.onBeforeLoad, this);
38936         this.store.on('load', this.onLoad, this);
38937         this.store.on('loadexception', this.onLoadException, this);
38938         this.removeMask = false;
38939     }else{
38940         var um = this.el.getUpdateManager();
38941         um.showLoadIndicator = false; // disable the default indicator
38942         um.on('beforeupdate', this.onBeforeLoad, this);
38943         um.on('update', this.onLoad, this);
38944         um.on('failure', this.onLoad, this);
38945         this.removeMask = true;
38946     }
38947 };
38948
38949 Roo.LoadMask.prototype = {
38950     /**
38951      * @cfg {Boolean} removeMask
38952      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38953      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38954      */
38955     /**
38956      * @cfg {String} msg
38957      * The text to display in a centered loading message box (defaults to 'Loading...')
38958      */
38959     msg : 'Loading...',
38960     /**
38961      * @cfg {String} msgCls
38962      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38963      */
38964     msgCls : 'x-mask-loading',
38965
38966     /**
38967      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38968      * @type Boolean
38969      */
38970     disabled: false,
38971
38972     /**
38973      * Disables the mask to prevent it from being displayed
38974      */
38975     disable : function(){
38976        this.disabled = true;
38977     },
38978
38979     /**
38980      * Enables the mask so that it can be displayed
38981      */
38982     enable : function(){
38983         this.disabled = false;
38984     },
38985     
38986     onLoadException : function()
38987     {
38988         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38989             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38990         }
38991         this.el.unmask(this.removeMask);
38992     },
38993     // private
38994     onLoad : function()
38995     {
38996         this.el.unmask(this.removeMask);
38997     },
38998
38999     // private
39000     onBeforeLoad : function(){
39001         if(!this.disabled){
39002             this.el.mask(this.msg, this.msgCls);
39003         }
39004     },
39005
39006     // private
39007     destroy : function(){
39008         if(this.store){
39009             this.store.un('beforeload', this.onBeforeLoad, this);
39010             this.store.un('load', this.onLoad, this);
39011             this.store.un('loadexception', this.onLoadException, this);
39012         }else{
39013             var um = this.el.getUpdateManager();
39014             um.un('beforeupdate', this.onBeforeLoad, this);
39015             um.un('update', this.onLoad, this);
39016             um.un('failure', this.onLoad, this);
39017         }
39018     }
39019 };/*
39020  * Based on:
39021  * Ext JS Library 1.1.1
39022  * Copyright(c) 2006-2007, Ext JS, LLC.
39023  *
39024  * Originally Released Under LGPL - original licence link has changed is not relivant.
39025  *
39026  * Fork - LGPL
39027  * <script type="text/javascript">
39028  */
39029
39030
39031 /**
39032  * @class Roo.XTemplate
39033  * @extends Roo.Template
39034  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
39035 <pre><code>
39036 var t = new Roo.XTemplate(
39037         '&lt;select name="{name}"&gt;',
39038                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
39039         '&lt;/select&gt;'
39040 );
39041  
39042 // then append, applying the master template values
39043  </code></pre>
39044  *
39045  * Supported features:
39046  *
39047  *  Tags:
39048
39049 <pre><code>
39050       {a_variable} - output encoded.
39051       {a_variable.format:("Y-m-d")} - call a method on the variable
39052       {a_variable:raw} - unencoded output
39053       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
39054       {a_variable:this.method_on_template(...)} - call a method on the template object.
39055  
39056 </code></pre>
39057  *  The tpl tag:
39058 <pre><code>
39059         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
39060         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
39061         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
39062         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
39063   
39064         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
39065         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
39066 </code></pre>
39067  *      
39068  */
39069 Roo.XTemplate = function()
39070 {
39071     Roo.XTemplate.superclass.constructor.apply(this, arguments);
39072     if (this.html) {
39073         this.compile();
39074     }
39075 };
39076
39077
39078 Roo.extend(Roo.XTemplate, Roo.Template, {
39079
39080     /**
39081      * The various sub templates
39082      */
39083     tpls : false,
39084     /**
39085      *
39086      * basic tag replacing syntax
39087      * WORD:WORD()
39088      *
39089      * // you can fake an object call by doing this
39090      *  x.t:(test,tesT) 
39091      * 
39092      */
39093     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
39094
39095     /**
39096      * compile the template
39097      *
39098      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
39099      *
39100      */
39101     compile: function()
39102     {
39103         var s = this.html;
39104      
39105         s = ['<tpl>', s, '</tpl>'].join('');
39106     
39107         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
39108             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
39109             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
39110             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
39111             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
39112             m,
39113             id     = 0,
39114             tpls   = [];
39115     
39116         while(true == !!(m = s.match(re))){
39117             var forMatch   = m[0].match(nameRe),
39118                 ifMatch   = m[0].match(ifRe),
39119                 execMatch   = m[0].match(execRe),
39120                 namedMatch   = m[0].match(namedRe),
39121                 
39122                 exp  = null, 
39123                 fn   = null,
39124                 exec = null,
39125                 name = forMatch && forMatch[1] ? forMatch[1] : '';
39126                 
39127             if (ifMatch) {
39128                 // if - puts fn into test..
39129                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
39130                 if(exp){
39131                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
39132                 }
39133             }
39134             
39135             if (execMatch) {
39136                 // exec - calls a function... returns empty if true is  returned.
39137                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
39138                 if(exp){
39139                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
39140                 }
39141             }
39142             
39143             
39144             if (name) {
39145                 // for = 
39146                 switch(name){
39147                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
39148                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
39149                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
39150                 }
39151             }
39152             var uid = namedMatch ? namedMatch[1] : id;
39153             
39154             
39155             tpls.push({
39156                 id:     namedMatch ? namedMatch[1] : id,
39157                 target: name,
39158                 exec:   exec,
39159                 test:   fn,
39160                 body:   m[1] || ''
39161             });
39162             if (namedMatch) {
39163                 s = s.replace(m[0], '');
39164             } else { 
39165                 s = s.replace(m[0], '{xtpl'+ id + '}');
39166             }
39167             ++id;
39168         }
39169         this.tpls = [];
39170         for(var i = tpls.length-1; i >= 0; --i){
39171             this.compileTpl(tpls[i]);
39172             this.tpls[tpls[i].id] = tpls[i];
39173         }
39174         this.master = tpls[tpls.length-1];
39175         return this;
39176     },
39177     /**
39178      * same as applyTemplate, except it's done to one of the subTemplates
39179      * when using named templates, you can do:
39180      *
39181      * var str = pl.applySubTemplate('your-name', values);
39182      *
39183      * 
39184      * @param {Number} id of the template
39185      * @param {Object} values to apply to template
39186      * @param {Object} parent (normaly the instance of this object)
39187      */
39188     applySubTemplate : function(id, values, parent)
39189     {
39190         
39191         
39192         var t = this.tpls[id];
39193         
39194         
39195         try { 
39196             if(t.test && !t.test.call(this, values, parent)){
39197                 return '';
39198             }
39199         } catch(e) {
39200             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
39201             Roo.log(e.toString());
39202             Roo.log(t.test);
39203             return ''
39204         }
39205         try { 
39206             
39207             if(t.exec && t.exec.call(this, values, parent)){
39208                 return '';
39209             }
39210         } catch(e) {
39211             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
39212             Roo.log(e.toString());
39213             Roo.log(t.exec);
39214             return ''
39215         }
39216         try {
39217             var vs = t.target ? t.target.call(this, values, parent) : values;
39218             parent = t.target ? values : parent;
39219             if(t.target && vs instanceof Array){
39220                 var buf = [];
39221                 for(var i = 0, len = vs.length; i < len; i++){
39222                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
39223                 }
39224                 return buf.join('');
39225             }
39226             return t.compiled.call(this, vs, parent);
39227         } catch (e) {
39228             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
39229             Roo.log(e.toString());
39230             Roo.log(t.compiled);
39231             return '';
39232         }
39233     },
39234
39235     compileTpl : function(tpl)
39236     {
39237         var fm = Roo.util.Format;
39238         var useF = this.disableFormats !== true;
39239         var sep = Roo.isGecko ? "+" : ",";
39240         var undef = function(str) {
39241             Roo.log("Property not found :"  + str);
39242             return '';
39243         };
39244         
39245         var fn = function(m, name, format, args)
39246         {
39247             //Roo.log(arguments);
39248             args = args ? args.replace(/\\'/g,"'") : args;
39249             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
39250             if (typeof(format) == 'undefined') {
39251                 format= 'htmlEncode';
39252             }
39253             if (format == 'raw' ) {
39254                 format = false;
39255             }
39256             
39257             if(name.substr(0, 4) == 'xtpl'){
39258                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
39259             }
39260             
39261             // build an array of options to determine if value is undefined..
39262             
39263             // basically get 'xxxx.yyyy' then do
39264             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
39265             //    (function () { Roo.log("Property not found"); return ''; })() :
39266             //    ......
39267             
39268             var udef_ar = [];
39269             var lookfor = '';
39270             Roo.each(name.split('.'), function(st) {
39271                 lookfor += (lookfor.length ? '.': '') + st;
39272                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
39273             });
39274             
39275             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
39276             
39277             
39278             if(format && useF){
39279                 
39280                 args = args ? ',' + args : "";
39281                  
39282                 if(format.substr(0, 5) != "this."){
39283                     format = "fm." + format + '(';
39284                 }else{
39285                     format = 'this.call("'+ format.substr(5) + '", ';
39286                     args = ", values";
39287                 }
39288                 
39289                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
39290             }
39291              
39292             if (args.length) {
39293                 // called with xxyx.yuu:(test,test)
39294                 // change to ()
39295                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
39296             }
39297             // raw.. - :raw modifier..
39298             return "'"+ sep + udef_st  + name + ")"+sep+"'";
39299             
39300         };
39301         var body;
39302         // branched to use + in gecko and [].join() in others
39303         if(Roo.isGecko){
39304             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
39305                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
39306                     "';};};";
39307         }else{
39308             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
39309             body.push(tpl.body.replace(/(\r\n|\n)/g,
39310                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
39311             body.push("'].join('');};};");
39312             body = body.join('');
39313         }
39314         
39315         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
39316        
39317         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
39318         eval(body);
39319         
39320         return this;
39321     },
39322
39323     applyTemplate : function(values){
39324         return this.master.compiled.call(this, values, {});
39325         //var s = this.subs;
39326     },
39327
39328     apply : function(){
39329         return this.applyTemplate.apply(this, arguments);
39330     }
39331
39332  });
39333
39334 Roo.XTemplate.from = function(el){
39335     el = Roo.getDom(el);
39336     return new Roo.XTemplate(el.value || el.innerHTML);
39337 };/*
39338  * Original code for Roojs - LGPL
39339  * <script type="text/javascript">
39340  */
39341  
39342 /**
39343  * @class Roo.XComponent
39344  * A delayed Element creator...
39345  * Or a way to group chunks of interface together.
39346  * 
39347  * Mypart.xyx = new Roo.XComponent({
39348
39349     parent : 'Mypart.xyz', // empty == document.element.!!
39350     order : '001',
39351     name : 'xxxx'
39352     region : 'xxxx'
39353     disabled : function() {} 
39354      
39355     tree : function() { // return an tree of xtype declared components
39356         var MODULE = this;
39357         return 
39358         {
39359             xtype : 'NestedLayoutPanel',
39360             // technicall
39361         }
39362      ]
39363  *})
39364  *
39365  *
39366  * It can be used to build a big heiracy, with parent etc.
39367  * or you can just use this to render a single compoent to a dom element
39368  * MYPART.render(Roo.Element | String(id) | dom_element )
39369  * 
39370  * @extends Roo.util.Observable
39371  * @constructor
39372  * @param cfg {Object} configuration of component
39373  * 
39374  */
39375 Roo.XComponent = function(cfg) {
39376     Roo.apply(this, cfg);
39377     this.addEvents({ 
39378         /**
39379              * @event built
39380              * Fires when this the componnt is built
39381              * @param {Roo.XComponent} c the component
39382              */
39383         'built' : true
39384         
39385     });
39386     this.region = this.region || 'center'; // default..
39387     Roo.XComponent.register(this);
39388     this.modules = false;
39389     this.el = false; // where the layout goes..
39390     
39391     
39392 }
39393 Roo.extend(Roo.XComponent, Roo.util.Observable, {
39394     /**
39395      * @property el
39396      * The created element (with Roo.factory())
39397      * @type {Roo.Layout}
39398      */
39399     el  : false,
39400     
39401     /**
39402      * @property el
39403      * for BC  - use el in new code
39404      * @type {Roo.Layout}
39405      */
39406     panel : false,
39407     
39408     /**
39409      * @property layout
39410      * for BC  - use el in new code
39411      * @type {Roo.Layout}
39412      */
39413     layout : false,
39414     
39415      /**
39416      * @cfg {Function|boolean} disabled
39417      * If this module is disabled by some rule, return true from the funtion
39418      */
39419     disabled : false,
39420     
39421     /**
39422      * @cfg {String} parent 
39423      * Name of parent element which it get xtype added to..
39424      */
39425     parent: false,
39426     
39427     /**
39428      * @cfg {String} order
39429      * Used to set the order in which elements are created (usefull for multiple tabs)
39430      */
39431     
39432     order : false,
39433     /**
39434      * @cfg {String} name
39435      * String to display while loading.
39436      */
39437     name : false,
39438     /**
39439      * @cfg {String} region
39440      * Region to render component to (defaults to center)
39441      */
39442     region : 'center',
39443     
39444     /**
39445      * @cfg {Array} items
39446      * A single item array - the first element is the root of the tree..
39447      * It's done this way to stay compatible with the Xtype system...
39448      */
39449     items : false,
39450     
39451     /**
39452      * @property _tree
39453      * The method that retuns the tree of parts that make up this compoennt 
39454      * @type {function}
39455      */
39456     _tree  : false,
39457     
39458      /**
39459      * render
39460      * render element to dom or tree
39461      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
39462      */
39463     
39464     render : function(el)
39465     {
39466         
39467         el = el || false;
39468         var hp = this.parent ? 1 : 0;
39469         
39470         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
39471             // if parent is a '#.....' string, then let's use that..
39472             var ename = this.parent.substr(1)
39473             this.parent = false;
39474             el = Roo.get(ename);
39475             if (!el) {
39476                 Roo.log("Warning - element can not be found :#" + ename );
39477                 return;
39478             }
39479         }
39480         
39481         
39482         if (!this.parent) {
39483             
39484             el = el ? Roo.get(el) : false;      
39485             
39486             // it's a top level one..
39487             this.parent =  {
39488                 el : new Roo.BorderLayout(el || document.body, {
39489                 
39490                      center: {
39491                          titlebar: false,
39492                          autoScroll:false,
39493                          closeOnTab: true,
39494                          tabPosition: 'top',
39495                           //resizeTabs: true,
39496                          alwaysShowTabs: el && hp? false :  true,
39497                          hideTabs: el || !hp ? true :  false,
39498                          minTabWidth: 140
39499                      }
39500                  })
39501             }
39502         }
39503         
39504                 if (!this.parent.el) {
39505                         // probably an old style ctor, which has been disabled.
39506                         return;
39507                         
39508                 }
39509                 // The 'tree' method is  '_tree now' 
39510             
39511         var tree = this._tree ? this._tree() : this.tree();
39512         tree.region = tree.region || this.region;
39513         this.el = this.parent.el.addxtype(tree);
39514         this.fireEvent('built', this);
39515         
39516         this.panel = this.el;
39517         this.layout = this.panel.layout;
39518                 this.parentLayout = this.parent.layout  || false;  
39519          
39520     }
39521     
39522 });
39523
39524 Roo.apply(Roo.XComponent, {
39525     /**
39526      * @property  hideProgress
39527      * true to disable the building progress bar.. usefull on single page renders.
39528      * @type Boolean
39529      */
39530     hideProgress : false,
39531     /**
39532      * @property  buildCompleted
39533      * True when the builder has completed building the interface.
39534      * @type Boolean
39535      */
39536     buildCompleted : false,
39537      
39538     /**
39539      * @property  topModule
39540      * the upper most module - uses document.element as it's constructor.
39541      * @type Object
39542      */
39543      
39544     topModule  : false,
39545       
39546     /**
39547      * @property  modules
39548      * array of modules to be created by registration system.
39549      * @type {Array} of Roo.XComponent
39550      */
39551     
39552     modules : [],
39553     /**
39554      * @property  elmodules
39555      * array of modules to be created by which use #ID 
39556      * @type {Array} of Roo.XComponent
39557      */
39558      
39559     elmodules : [],
39560
39561     
39562     /**
39563      * Register components to be built later.
39564      *
39565      * This solves the following issues
39566      * - Building is not done on page load, but after an authentication process has occured.
39567      * - Interface elements are registered on page load
39568      * - Parent Interface elements may not be loaded before child, so this handles that..
39569      * 
39570      *
39571      * example:
39572      * 
39573      * MyApp.register({
39574           order : '000001',
39575           module : 'Pman.Tab.projectMgr',
39576           region : 'center',
39577           parent : 'Pman.layout',
39578           disabled : false,  // or use a function..
39579         })
39580      
39581      * * @param {Object} details about module
39582      */
39583     register : function(obj) {
39584                 
39585         Roo.XComponent.event.fireEvent('register', obj);
39586         switch(typeof(obj.disabled) ) {
39587                 
39588             case 'undefined':
39589                 break;
39590             
39591             case 'function':
39592                 if ( obj.disabled() ) {
39593                         return;
39594                 }
39595                 break;
39596             
39597             default:
39598                 if (obj.disabled) {
39599                         return;
39600                 }
39601                 break;
39602         }
39603                 
39604         this.modules.push(obj);
39605          
39606     },
39607     /**
39608      * convert a string to an object..
39609      * eg. 'AAA.BBB' -> finds AAA.BBB
39610
39611      */
39612     
39613     toObject : function(str)
39614     {
39615         if (!str || typeof(str) == 'object') {
39616             return str;
39617         }
39618         if (str.substring(0,1) == '#') {
39619             return str;
39620         }
39621
39622         var ar = str.split('.');
39623         var rt, o;
39624         rt = ar.shift();
39625             /** eval:var:o */
39626         try {
39627             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
39628         } catch (e) {
39629             throw "Module not found : " + str;
39630         }
39631         
39632         if (o === false) {
39633             throw "Module not found : " + str;
39634         }
39635         Roo.each(ar, function(e) {
39636             if (typeof(o[e]) == 'undefined') {
39637                 throw "Module not found : " + str;
39638             }
39639             o = o[e];
39640         });
39641         
39642         return o;
39643         
39644     },
39645     
39646     
39647     /**
39648      * move modules into their correct place in the tree..
39649      * 
39650      */
39651     preBuild : function ()
39652     {
39653         var _t = this;
39654         Roo.each(this.modules , function (obj)
39655         {
39656             Roo.XComponent.event.fireEvent('beforebuild', obj);
39657             
39658             var opar = obj.parent;
39659             try { 
39660                 obj.parent = this.toObject(opar);
39661             } catch(e) {
39662                 Roo.log("parent:toObject failed: " + e.toString());
39663                 return;
39664             }
39665             
39666             if (!obj.parent) {
39667                 Roo.debug && Roo.log("GOT top level module");
39668                 Roo.debug && Roo.log(obj);
39669                 obj.modules = new Roo.util.MixedCollection(false, 
39670                     function(o) { return o.order + '' }
39671                 );
39672                 this.topModule = obj;
39673                 return;
39674             }
39675                         // parent is a string (usually a dom element name..)
39676             if (typeof(obj.parent) == 'string') {
39677                 this.elmodules.push(obj);
39678                 return;
39679             }
39680             if (obj.parent.constructor != Roo.XComponent) {
39681                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
39682             }
39683             if (!obj.parent.modules) {
39684                 obj.parent.modules = new Roo.util.MixedCollection(false, 
39685                     function(o) { return o.order + '' }
39686                 );
39687             }
39688             if (obj.parent.disabled) {
39689                 obj.disabled = true;
39690             }
39691             obj.parent.modules.add(obj);
39692         }, this);
39693     },
39694     
39695      /**
39696      * make a list of modules to build.
39697      * @return {Array} list of modules. 
39698      */ 
39699     
39700     buildOrder : function()
39701     {
39702         var _this = this;
39703         var cmp = function(a,b) {   
39704             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
39705         };
39706         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
39707             throw "No top level modules to build";
39708         }
39709         
39710         // make a flat list in order of modules to build.
39711         var mods = this.topModule ? [ this.topModule ] : [];
39712                 
39713         // elmodules (is a list of DOM based modules )
39714         Roo.each(this.elmodules, function(e) {
39715             mods.push(e)
39716         });
39717
39718         
39719         // add modules to their parents..
39720         var addMod = function(m) {
39721             Roo.debug && Roo.log("build Order: add: " + m.name);
39722             
39723         mods.push(m);
39724         if (m.modules && !m.disabled) {
39725             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39726             m.modules.keySort('ASC',  cmp );
39727             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39728
39729             m.modules.each(addMod);
39730         } else {
39731             Roo.debug && Roo.log("build Order: no child modules");
39732             }
39733             // not sure if this is used any more..
39734             if (m.finalize) {
39735                 m.finalize.name = m.name + " (clean up) ";
39736                 mods.push(m.finalize);
39737             }
39738             
39739         }
39740         if (this.topModule) { 
39741             this.topModule.modules.keySort('ASC',  cmp );
39742             this.topModule.modules.each(addMod);
39743         }
39744         return mods;
39745     },
39746     
39747      /**
39748      * Build the registered modules.
39749      * @param {Object} parent element.
39750      * @param {Function} optional method to call after module has been added.
39751      * 
39752      */ 
39753    
39754     build : function() 
39755     {
39756         
39757         this.preBuild();
39758         var mods = this.buildOrder();
39759       
39760         //this.allmods = mods;
39761         //Roo.debug && Roo.log(mods);
39762         //return;
39763         if (!mods.length) { // should not happen
39764             throw "NO modules!!!";
39765         }
39766         
39767         
39768         var msg = "Building Interface...";
39769         // flash it up as modal - so we store the mask!?
39770         if (!this.hideProgress) {
39771             Roo.MessageBox.show({ title: 'loading' });
39772             Roo.MessageBox.show({
39773                title: "Please wait...",
39774                msg: msg,
39775                width:450,
39776                progress:true,
39777                closable:false,
39778                modal: false
39779               
39780             });
39781         }
39782         var total = mods.length;
39783         
39784         var _this = this;
39785         var progressRun = function() {
39786             if (!mods.length) {
39787                 Roo.debug && Roo.log('hide?');
39788                 if (!this.hideProgress) {
39789                     Roo.MessageBox.hide();
39790                 }
39791                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39792                 
39793                 // THE END...
39794                 return false;   
39795             }
39796             
39797             var m = mods.shift();
39798             
39799             
39800             Roo.debug && Roo.log(m);
39801             // not sure if this is supported any more.. - modules that are are just function
39802             if (typeof(m) == 'function') { 
39803                 m.call(this);
39804                 return progressRun.defer(10, _this);
39805             } 
39806             
39807             
39808             msg = "Building Interface " + (total  - mods.length) + 
39809                     " of " + total + 
39810                     (m.name ? (' - ' + m.name) : '');
39811                         Roo.debug && Roo.log(msg);
39812             if (!this.hideProgress) { 
39813                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39814             }
39815             
39816          
39817             // is the module disabled?
39818             var disabled = (typeof(m.disabled) == 'function') ?
39819                 m.disabled.call(m.module.disabled) : m.disabled;    
39820             
39821             
39822             if (disabled) {
39823                 return progressRun(); // we do not update the display!
39824             }
39825             
39826             // now build 
39827             
39828                         
39829                         
39830             m.render();
39831             // it's 10 on top level, and 1 on others??? why...
39832             return progressRun.defer(10, _this);
39833              
39834         }
39835         progressRun.defer(1, _this);
39836      
39837         
39838         
39839     },
39840         
39841         
39842         /**
39843          * Event Object.
39844          *
39845          *
39846          */
39847         event: false, 
39848     /**
39849          * wrapper for event.on - aliased later..  
39850          * Typically use to register a event handler for register:
39851          *
39852          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39853          *
39854          */
39855     on : false
39856    
39857     
39858     
39859 });
39860
39861 Roo.XComponent.event = new Roo.util.Observable({
39862                 events : { 
39863                         /**
39864                          * @event register
39865                          * Fires when an Component is registered,
39866                          * set the disable property on the Component to stop registration.
39867                          * @param {Roo.XComponent} c the component being registerd.
39868                          * 
39869                          */
39870                         'register' : true,
39871             /**
39872                          * @event beforebuild
39873                          * Fires before each Component is built
39874                          * can be used to apply permissions.
39875                          * @param {Roo.XComponent} c the component being registerd.
39876                          * 
39877                          */
39878                         'beforebuild' : true,
39879                         /**
39880                          * @event buildcomplete
39881                          * Fires on the top level element when all elements have been built
39882                          * @param {Roo.XComponent} the top level component.
39883                          */
39884                         'buildcomplete' : true
39885                         
39886                 }
39887 });
39888
39889 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39890  //<script type="text/javascript">
39891
39892
39893 /**
39894  * @class Roo.Login
39895  * @extends Roo.LayoutDialog
39896  * A generic Login Dialog..... - only one needed in theory!?!?
39897  *
39898  * Fires XComponent builder on success...
39899  * 
39900  * Sends 
39901  *    username,password, lang = for login actions.
39902  *    check = 1 for periodic checking that sesion is valid.
39903  *    passwordRequest = email request password
39904  *    logout = 1 = to logout
39905  * 
39906  * Affects: (this id="????" elements)
39907  *   loading  (removed) (used to indicate application is loading)
39908  *   loading-mask (hides) (used to hide application when it's building loading)
39909  *   
39910  * 
39911  * Usage: 
39912  *    
39913  * 
39914  * Myapp.login = Roo.Login({
39915      url: xxxx,
39916    
39917      realm : 'Myapp', 
39918      
39919      
39920      method : 'POST',
39921      
39922      
39923      * 
39924  })
39925  * 
39926  * 
39927  * 
39928  **/
39929  
39930 Roo.Login = function(cfg)
39931 {
39932     this.addEvents({
39933         'refreshed' : true
39934     });
39935     
39936     Roo.apply(this,cfg);
39937     
39938     Roo.onReady(function() {
39939         this.onLoad();
39940     }, this);
39941     // call parent..
39942     
39943    
39944     Roo.Login.superclass.constructor.call(this, this);
39945     //this.addxtype(this.items[0]);
39946     
39947     
39948 }
39949
39950
39951 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39952     
39953     /**
39954      * @cfg {String} method
39955      * Method used to query for login details.
39956      */
39957     
39958     method : 'POST',
39959     /**
39960      * @cfg {String} url
39961      * URL to query login data. - eg. baseURL + '/Login.php'
39962      */
39963     url : '',
39964     
39965     /**
39966      * @property user
39967      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39968      * @type {Object} 
39969      */
39970     user : false,
39971     /**
39972      * @property checkFails
39973      * Number of times we have attempted to get authentication check, and failed.
39974      * @type {Number} 
39975      */
39976     checkFails : 0,
39977       /**
39978      * @property intervalID
39979      * The window interval that does the constant login checking.
39980      * @type {Number} 
39981      */
39982     intervalID : 0,
39983     
39984     
39985     onLoad : function() // called on page load...
39986     {
39987         // load 
39988          
39989         if (Roo.get('loading')) { // clear any loading indicator..
39990             Roo.get('loading').remove();
39991         }
39992         
39993         //this.switchLang('en'); // set the language to english..
39994        
39995         this.check({
39996             success:  function(response, opts)  {  // check successfull...
39997             
39998                 var res = this.processResponse(response);
39999                 this.checkFails =0;
40000                 if (!res.success) { // error!
40001                     this.checkFails = 5;
40002                     //console.log('call failure');
40003                     return this.failure(response,opts);
40004                 }
40005                 
40006                 if (!res.data.id) { // id=0 == login failure.
40007                     return this.show();
40008                 }
40009                 
40010                               
40011                         //console.log(success);
40012                 this.fillAuth(res.data);   
40013                 this.checkFails =0;
40014                 Roo.XComponent.build();
40015             },
40016             failure : this.show
40017         });
40018         
40019     }, 
40020     
40021     
40022     check: function(cfg) // called every so often to refresh cookie etc..
40023     {
40024         if (cfg.again) { // could be undefined..
40025             this.checkFails++;
40026         } else {
40027             this.checkFails = 0;
40028         }
40029         var _this = this;
40030         if (this.sending) {
40031             if ( this.checkFails > 4) {
40032                 Roo.MessageBox.alert("Error",  
40033                     "Error getting authentication status. - try reloading, or wait a while", function() {
40034                         _this.sending = false;
40035                     }); 
40036                 return;
40037             }
40038             cfg.again = true;
40039             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
40040             return;
40041         }
40042         this.sending = true;
40043         
40044         Roo.Ajax.request({  
40045             url: this.url,
40046             params: {
40047                 getAuthUser: true
40048             },  
40049             method: this.method,
40050             success:  cfg.success || this.success,
40051             failure : cfg.failure || this.failure,
40052             scope : this,
40053             callCfg : cfg
40054               
40055         });  
40056     }, 
40057     
40058     
40059     logout: function()
40060     {
40061         window.onbeforeunload = function() { }; // false does not work for IE..
40062         this.user = false;
40063         var _this = this;
40064         
40065         Roo.Ajax.request({  
40066             url: this.url,
40067             params: {
40068                 logout: 1
40069             },  
40070             method: 'GET',
40071             failure : function() {
40072                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
40073                     document.location = document.location.toString() + '?ts=' + Math.random();
40074                 });
40075                 
40076             },
40077             success : function() {
40078                 _this.user = false;
40079                 this.checkFails =0;
40080                 // fixme..
40081                 document.location = document.location.toString() + '?ts=' + Math.random();
40082             }
40083               
40084               
40085         }); 
40086     },
40087     
40088     processResponse : function (response)
40089     {
40090         var res = '';
40091         try {
40092             res = Roo.decode(response.responseText);
40093             // oops...
40094             if (typeof(res) != 'object') {
40095                 res = { success : false, errorMsg : res, errors : true };
40096             }
40097             if (typeof(res.success) == 'undefined') {
40098                 res.success = false;
40099             }
40100             
40101         } catch(e) {
40102             res = { success : false,  errorMsg : response.responseText, errors : true };
40103         }
40104         return res;
40105     },
40106     
40107     success : function(response, opts)  // check successfull...
40108     {  
40109         this.sending = false;
40110         var res = this.processResponse(response);
40111         if (!res.success) {
40112             return this.failure(response, opts);
40113         }
40114         if (!res.data || !res.data.id) {
40115             return this.failure(response,opts);
40116         }
40117         //console.log(res);
40118         this.fillAuth(res.data);
40119         
40120         this.checkFails =0;
40121         
40122     },
40123     
40124     
40125     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
40126     {
40127         this.authUser = -1;
40128         this.sending = false;
40129         var res = this.processResponse(response);
40130         //console.log(res);
40131         if ( this.checkFails > 2) {
40132         
40133             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
40134                 "Error getting authentication status. - try reloading"); 
40135             return;
40136         }
40137         opts.callCfg.again = true;
40138         this.check.defer(1000, this, [ opts.callCfg ]);
40139         return;  
40140     },
40141     
40142     
40143     
40144     fillAuth: function(au) {
40145         this.startAuthCheck();
40146         this.authUserId = au.id;
40147         this.authUser = au;
40148         this.lastChecked = new Date();
40149         this.fireEvent('refreshed', au);
40150         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
40151         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
40152         au.lang = au.lang || 'en';
40153         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
40154         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
40155         this.switchLang(au.lang );
40156         
40157      
40158         // open system... - -on setyp..
40159         if (this.authUserId  < 0) {
40160             Roo.MessageBox.alert("Warning", 
40161                 "This is an open system - please set up a admin user with a password.");  
40162         }
40163          
40164         //Pman.onload(); // which should do nothing if it's a re-auth result...
40165         
40166              
40167     },
40168     
40169     startAuthCheck : function() // starter for timeout checking..
40170     {
40171         if (this.intervalID) { // timer already in place...
40172             return false;
40173         }
40174         var _this = this;
40175         this.intervalID =  window.setInterval(function() {
40176               _this.check(false);
40177             }, 120000); // every 120 secs = 2mins..
40178         
40179         
40180     },
40181          
40182     
40183     switchLang : function (lang) 
40184     {
40185         _T = typeof(_T) == 'undefined' ? false : _T;
40186           if (!_T || !lang.length) {
40187             return;
40188         }
40189         
40190         if (!_T && lang != 'en') {
40191             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
40192             return;
40193         }
40194         
40195         if (typeof(_T.en) == 'undefined') {
40196             _T.en = {};
40197             Roo.apply(_T.en, _T);
40198         }
40199         
40200         if (typeof(_T[lang]) == 'undefined') {
40201             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
40202             return;
40203         }
40204         
40205         
40206         Roo.apply(_T, _T[lang]);
40207         // just need to set the text values for everything...
40208         var _this = this;
40209         /* this will not work ...
40210         if (this.form) { 
40211             
40212                
40213             function formLabel(name, val) {
40214                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
40215             }
40216             
40217             formLabel('password', "Password"+':');
40218             formLabel('username', "Email Address"+':');
40219             formLabel('lang', "Language"+':');
40220             this.dialog.setTitle("Login");
40221             this.dialog.buttons[0].setText("Forgot Password");
40222             this.dialog.buttons[1].setText("Login");
40223         }
40224         */
40225         
40226         
40227     },
40228     
40229     
40230     title: "Login",
40231     modal: true,
40232     width:  350,
40233     //height: 230,
40234     height: 180,
40235     shadow: true,
40236     minWidth:200,
40237     minHeight:180,
40238     //proxyDrag: true,
40239     closable: false,
40240     draggable: false,
40241     collapsible: false,
40242     resizable: false,
40243     center: {  // needed??
40244         autoScroll:false,
40245         titlebar: false,
40246        // tabPosition: 'top',
40247         hideTabs: true,
40248         closeOnTab: true,
40249         alwaysShowTabs: false
40250     } ,
40251     listeners : {
40252         
40253         show  : function(dlg)
40254         {
40255             //console.log(this);
40256             this.form = this.layout.getRegion('center').activePanel.form;
40257             this.form.dialog = dlg;
40258             this.buttons[0].form = this.form;
40259             this.buttons[0].dialog = dlg;
40260             this.buttons[1].form = this.form;
40261             this.buttons[1].dialog = dlg;
40262            
40263            //this.resizeToLogo.defer(1000,this);
40264             // this is all related to resizing for logos..
40265             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
40266            //// if (!sz) {
40267              //   this.resizeToLogo.defer(1000,this);
40268              //   return;
40269            // }
40270             //var w = Ext.lib.Dom.getViewWidth() - 100;
40271             //var h = Ext.lib.Dom.getViewHeight() - 100;
40272             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
40273             //this.center();
40274             if (this.disabled) {
40275                 this.hide();
40276                 return;
40277             }
40278             
40279             if (this.user.id < 0) { // used for inital setup situations.
40280                 return;
40281             }
40282             
40283             if (this.intervalID) {
40284                 // remove the timer
40285                 window.clearInterval(this.intervalID);
40286                 this.intervalID = false;
40287             }
40288             
40289             
40290             if (Roo.get('loading')) {
40291                 Roo.get('loading').remove();
40292             }
40293             if (Roo.get('loading-mask')) {
40294                 Roo.get('loading-mask').hide();
40295             }
40296             
40297             //incomming._node = tnode;
40298             this.form.reset();
40299             //this.dialog.modal = !modal;
40300             //this.dialog.show();
40301             this.el.unmask(); 
40302             
40303             
40304             this.form.setValues({
40305                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
40306                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
40307             });
40308             
40309             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
40310             if (this.form.findField('username').getValue().length > 0 ){
40311                 this.form.findField('password').focus();
40312             } else {
40313                this.form.findField('username').focus();
40314             }
40315     
40316         }
40317     },
40318     items : [
40319          {
40320        
40321             xtype : 'ContentPanel',
40322             xns : Roo,
40323             region: 'center',
40324             fitToFrame : true,
40325             
40326             items : [
40327     
40328                 {
40329                
40330                     xtype : 'Form',
40331                     xns : Roo.form,
40332                     labelWidth: 100,
40333                     style : 'margin: 10px;',
40334                     
40335                     listeners : {
40336                         actionfailed : function(f, act) {
40337                             // form can return { errors: .... }
40338                                 
40339                             //act.result.errors // invalid form element list...
40340                             //act.result.errorMsg// invalid form element list...
40341                             
40342                             this.dialog.el.unmask();
40343                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
40344                                         "Login failed - communication error - try again.");
40345                                       
40346                         },
40347                         actioncomplete: function(re, act) {
40348                              
40349                             Roo.state.Manager.set(
40350                                 this.dialog.realm + '.username',  
40351                                     this.findField('username').getValue()
40352                             );
40353                             Roo.state.Manager.set(
40354                                 this.dialog.realm + '.lang',  
40355                                 this.findField('lang').getValue() 
40356                             );
40357                             
40358                             this.dialog.fillAuth(act.result.data);
40359                               
40360                             this.dialog.hide();
40361                             
40362                             if (Roo.get('loading-mask')) {
40363                                 Roo.get('loading-mask').show();
40364                             }
40365                             Roo.XComponent.build();
40366                             
40367                              
40368                             
40369                         }
40370                     },
40371                     items : [
40372                         {
40373                             xtype : 'TextField',
40374                             xns : Roo.form,
40375                             fieldLabel: "Email Address",
40376                             name: 'username',
40377                             width:200,
40378                             autoCreate : {tag: "input", type: "text", size: "20"}
40379                         },
40380                         {
40381                             xtype : 'TextField',
40382                             xns : Roo.form,
40383                             fieldLabel: "Password",
40384                             inputType: 'password',
40385                             name: 'password',
40386                             width:200,
40387                             autoCreate : {tag: "input", type: "text", size: "20"},
40388                             listeners : {
40389                                 specialkey : function(e,ev) {
40390                                     if (ev.keyCode == 13) {
40391                                         this.form.dialog.el.mask("Logging in");
40392                                         this.form.doAction('submit', {
40393                                             url: this.form.dialog.url,
40394                                             method: this.form.dialog.method
40395                                         });
40396                                     }
40397                                 }
40398                             }  
40399                         },
40400                         {
40401                             xtype : 'ComboBox',
40402                             xns : Roo.form,
40403                             fieldLabel: "Language",
40404                             name : 'langdisp',
40405                             store: {
40406                                 xtype : 'SimpleStore',
40407                                 fields: ['lang', 'ldisp'],
40408                                 data : [
40409                                     [ 'en', 'English' ],
40410                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
40411                                     [ 'zh_CN', '\u7C21\u4E2D' ]
40412                                 ]
40413                             },
40414                             
40415                             valueField : 'lang',
40416                             hiddenName:  'lang',
40417                             width: 200,
40418                             displayField:'ldisp',
40419                             typeAhead: false,
40420                             editable: false,
40421                             mode: 'local',
40422                             triggerAction: 'all',
40423                             emptyText:'Select a Language...',
40424                             selectOnFocus:true,
40425                             listeners : {
40426                                 select :  function(cb, rec, ix) {
40427                                     this.form.switchLang(rec.data.lang);
40428                                 }
40429                             }
40430                         
40431                         }
40432                     ]
40433                 }
40434                   
40435                 
40436             ]
40437         }
40438     ],
40439     buttons : [
40440         {
40441             xtype : 'Button',
40442             xns : 'Roo',
40443             text : "Forgot Password",
40444             listeners : {
40445                 click : function() {
40446                     //console.log(this);
40447                     var n = this.form.findField('username').getValue();
40448                     if (!n.length) {
40449                         Roo.MessageBox.alert("Error", "Fill in your email address");
40450                         return;
40451                     }
40452                     Roo.Ajax.request({
40453                         url: this.dialog.url,
40454                         params: {
40455                             passwordRequest: n
40456                         },
40457                         method: this.dialog.method,
40458                         success:  function(response, opts)  {  // check successfull...
40459                         
40460                             var res = this.dialog.processResponse(response);
40461                             if (!res.success) { // error!
40462                                Roo.MessageBox.alert("Error" ,
40463                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
40464                                return;
40465                             }
40466                             Roo.MessageBox.alert("Notice" ,
40467                                 "Please check you email for the Password Reset message");
40468                         },
40469                         failure : function() {
40470                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
40471                         }
40472                         
40473                     });
40474                 }
40475             }
40476         },
40477         {
40478             xtype : 'Button',
40479             xns : 'Roo',
40480             text : "Login",
40481             listeners : {
40482                 
40483                 click : function () {
40484                         
40485                     this.dialog.el.mask("Logging in");
40486                     this.form.doAction('submit', {
40487                             url: this.dialog.url,
40488                             method: this.dialog.method
40489                     });
40490                 }
40491             }
40492         }
40493     ]
40494   
40495   
40496 })
40497  
40498
40499
40500