Roo/form/ComboBoxArray.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 load
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         load : true,
4822         /**
4823          * @event loadexception
4824          * Fires if an exception occurs in the Proxy during loading.
4825          * Called with the signature of the Proxy's "loadexception" event.
4826          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4827          * 
4828          * @param {Proxy} 
4829          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4830          * @param {Object} load options 
4831          * @param {Object} jsonData from your request (normally this contains the Exception)
4832          */
4833         loadexception : true
4834     });
4835     
4836     if(this.proxy){
4837         this.proxy = Roo.factory(this.proxy, Roo.data);
4838         this.proxy.xmodule = this.xmodule || false;
4839         this.relayEvents(this.proxy,  ["loadexception"]);
4840     }
4841     this.sortToggle = {};
4842     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4843
4844     Roo.data.Store.superclass.constructor.call(this);
4845
4846     if(this.inlineData){
4847         this.loadData(this.inlineData);
4848         delete this.inlineData;
4849     }
4850 };
4851 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4852      /**
4853     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4854     * without a remote query - used by combo/forms at present.
4855     */
4856     
4857     /**
4858     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4859     */
4860     /**
4861     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4862     */
4863     /**
4864     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4865     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4866     */
4867     /**
4868     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4869     * on any HTTP request
4870     */
4871     /**
4872     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4873     */
4874     /**
4875     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4876     */
4877     multiSort: false,
4878     /**
4879     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4880     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4881     */
4882     remoteSort : false,
4883
4884     /**
4885     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4886      * loaded or when a record is removed. (defaults to false).
4887     */
4888     pruneModifiedRecords : false,
4889
4890     // private
4891     lastOptions : null,
4892
4893     /**
4894      * Add Records to the Store and fires the add event.
4895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896      */
4897     add : function(records){
4898         records = [].concat(records);
4899         for(var i = 0, len = records.length; i < len; i++){
4900             records[i].join(this);
4901         }
4902         var index = this.data.length;
4903         this.data.addAll(records);
4904         this.fireEvent("add", this, records, index);
4905     },
4906
4907     /**
4908      * Remove a Record from the Store and fires the remove event.
4909      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4910      */
4911     remove : function(record){
4912         var index = this.data.indexOf(record);
4913         this.data.removeAt(index);
4914         if(this.pruneModifiedRecords){
4915             this.modified.remove(record);
4916         }
4917         this.fireEvent("remove", this, record, index);
4918     },
4919
4920     /**
4921      * Remove all Records from the Store and fires the clear event.
4922      */
4923     removeAll : function(){
4924         this.data.clear();
4925         if(this.pruneModifiedRecords){
4926             this.modified = [];
4927         }
4928         this.fireEvent("clear", this);
4929     },
4930
4931     /**
4932      * Inserts Records to the Store at the given index and fires the add event.
4933      * @param {Number} index The start index at which to insert the passed Records.
4934      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4935      */
4936     insert : function(index, records){
4937         records = [].concat(records);
4938         for(var i = 0, len = records.length; i < len; i++){
4939             this.data.insert(index, records[i]);
4940             records[i].join(this);
4941         }
4942         this.fireEvent("add", this, records, index);
4943     },
4944
4945     /**
4946      * Get the index within the cache of the passed Record.
4947      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4948      * @return {Number} The index of the passed Record. Returns -1 if not found.
4949      */
4950     indexOf : function(record){
4951         return this.data.indexOf(record);
4952     },
4953
4954     /**
4955      * Get the index within the cache of the Record with the passed id.
4956      * @param {String} id The id of the Record to find.
4957      * @return {Number} The index of the Record. Returns -1 if not found.
4958      */
4959     indexOfId : function(id){
4960         return this.data.indexOfKey(id);
4961     },
4962
4963     /**
4964      * Get the Record with the specified id.
4965      * @param {String} id The id of the Record to find.
4966      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4967      */
4968     getById : function(id){
4969         return this.data.key(id);
4970     },
4971
4972     /**
4973      * Get the Record at the specified index.
4974      * @param {Number} index The index of the Record to find.
4975      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4976      */
4977     getAt : function(index){
4978         return this.data.itemAt(index);
4979     },
4980
4981     /**
4982      * Returns a range of Records between specified indices.
4983      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4984      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4985      * @return {Roo.data.Record[]} An array of Records
4986      */
4987     getRange : function(start, end){
4988         return this.data.getRange(start, end);
4989     },
4990
4991     // private
4992     storeOptions : function(o){
4993         o = Roo.apply({}, o);
4994         delete o.callback;
4995         delete o.scope;
4996         this.lastOptions = o;
4997     },
4998
4999     /**
5000      * Loads the Record cache from the configured Proxy using the configured Reader.
5001      * <p>
5002      * If using remote paging, then the first load call must specify the <em>start</em>
5003      * and <em>limit</em> properties in the options.params property to establish the initial
5004      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5005      * <p>
5006      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5007      * and this call will return before the new data has been loaded. Perform any post-processing
5008      * in a callback function, or in a "load" event handler.</strong>
5009      * <p>
5010      * @param {Object} options An object containing properties which control loading options:<ul>
5011      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5012      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5013      * passed the following arguments:<ul>
5014      * <li>r : Roo.data.Record[]</li>
5015      * <li>options: Options object from the load call</li>
5016      * <li>success: Boolean success indicator</li></ul></li>
5017      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5018      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5019      * </ul>
5020      */
5021     load : function(options){
5022         options = options || {};
5023         if(this.fireEvent("beforeload", this, options) !== false){
5024             this.storeOptions(options);
5025             var p = Roo.apply(options.params || {}, this.baseParams);
5026             // if meta was not loaded from remote source.. try requesting it.
5027             if (!this.reader.metaFromRemote) {
5028                 p._requestMeta = 1;
5029             }
5030             if(this.sortInfo && this.remoteSort){
5031                 var pn = this.paramNames;
5032                 p[pn["sort"]] = this.sortInfo.field;
5033                 p[pn["dir"]] = this.sortInfo.direction;
5034             }
5035             if (this.multiSort) {
5036                 var pn = this.paramNames;
5037                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5038             }
5039             
5040             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5041         }
5042     },
5043
5044     /**
5045      * Reloads the Record cache from the configured Proxy using the configured Reader and
5046      * the options from the last load operation performed.
5047      * @param {Object} options (optional) An object containing properties which may override the options
5048      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5049      * the most recently used options are reused).
5050      */
5051     reload : function(options){
5052         this.load(Roo.applyIf(options||{}, this.lastOptions));
5053     },
5054
5055     // private
5056     // Called as a callback by the Reader during a load operation.
5057     loadRecords : function(o, options, success){
5058         if(!o || success === false){
5059             if(success !== false){
5060                 this.fireEvent("load", this, [], options);
5061             }
5062             if(options.callback){
5063                 options.callback.call(options.scope || this, [], options, false);
5064             }
5065             return;
5066         }
5067         // if data returned failure - throw an exception.
5068         if (o.success === false) {
5069             // show a message if no listener is registered.
5070             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5071                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5072             }
5073             // loadmask wil be hooked into this..
5074             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5075             return;
5076         }
5077         var r = o.records, t = o.totalRecords || r.length;
5078         if(!options || options.add !== true){
5079             if(this.pruneModifiedRecords){
5080                 this.modified = [];
5081             }
5082             for(var i = 0, len = r.length; i < len; i++){
5083                 r[i].join(this);
5084             }
5085             if(this.snapshot){
5086                 this.data = this.snapshot;
5087                 delete this.snapshot;
5088             }
5089             this.data.clear();
5090             this.data.addAll(r);
5091             this.totalLength = t;
5092             this.applySort();
5093             this.fireEvent("datachanged", this);
5094         }else{
5095             this.totalLength = Math.max(t, this.data.length+r.length);
5096             this.add(r);
5097         }
5098         this.fireEvent("load", this, r, options);
5099         if(options.callback){
5100             options.callback.call(options.scope || this, r, options, true);
5101         }
5102     },
5103
5104
5105     /**
5106      * Loads data from a passed data block. A Reader which understands the format of the data
5107      * must have been configured in the constructor.
5108      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5109      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5110      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5111      */
5112     loadData : function(o, append){
5113         var r = this.reader.readRecords(o);
5114         this.loadRecords(r, {add: append}, true);
5115     },
5116
5117     /**
5118      * Gets the number of cached records.
5119      * <p>
5120      * <em>If using paging, this may not be the total size of the dataset. If the data object
5121      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5122      * the data set size</em>
5123      */
5124     getCount : function(){
5125         return this.data.length || 0;
5126     },
5127
5128     /**
5129      * Gets the total number of records in the dataset as returned by the server.
5130      * <p>
5131      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5132      * the dataset size</em>
5133      */
5134     getTotalCount : function(){
5135         return this.totalLength || 0;
5136     },
5137
5138     /**
5139      * Returns the sort state of the Store as an object with two properties:
5140      * <pre><code>
5141  field {String} The name of the field by which the Records are sorted
5142  direction {String} The sort order, "ASC" or "DESC"
5143      * </code></pre>
5144      */
5145     getSortState : function(){
5146         return this.sortInfo;
5147     },
5148
5149     // private
5150     applySort : function(){
5151         if(this.sortInfo && !this.remoteSort){
5152             var s = this.sortInfo, f = s.field;
5153             var st = this.fields.get(f).sortType;
5154             var fn = function(r1, r2){
5155                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5156                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5157             };
5158             this.data.sort(s.direction, fn);
5159             if(this.snapshot && this.snapshot != this.data){
5160                 this.snapshot.sort(s.direction, fn);
5161             }
5162         }
5163     },
5164
5165     /**
5166      * Sets the default sort column and order to be used by the next load operation.
5167      * @param {String} fieldName The name of the field to sort by.
5168      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5169      */
5170     setDefaultSort : function(field, dir){
5171         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5172     },
5173
5174     /**
5175      * Sort the Records.
5176      * If remote sorting is used, the sort is performed on the server, and the cache is
5177      * reloaded. If local sorting is used, the cache is sorted internally.
5178      * @param {String} fieldName The name of the field to sort by.
5179      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5180      */
5181     sort : function(fieldName, dir){
5182         var f = this.fields.get(fieldName);
5183         if(!dir){
5184             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5185             
5186             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5187                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5188             }else{
5189                 dir = f.sortDir;
5190             }
5191         }
5192         this.sortToggle[f.name] = dir;
5193         this.sortInfo = {field: f.name, direction: dir};
5194         if(!this.remoteSort){
5195             this.applySort();
5196             this.fireEvent("datachanged", this);
5197         }else{
5198             this.load(this.lastOptions);
5199         }
5200     },
5201
5202     /**
5203      * Calls the specified function for each of the Records in the cache.
5204      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5205      * Returning <em>false</em> aborts and exits the iteration.
5206      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5207      */
5208     each : function(fn, scope){
5209         this.data.each(fn, scope);
5210     },
5211
5212     /**
5213      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5214      * (e.g., during paging).
5215      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5216      */
5217     getModifiedRecords : function(){
5218         return this.modified;
5219     },
5220
5221     // private
5222     createFilterFn : function(property, value, anyMatch){
5223         if(!value.exec){ // not a regex
5224             value = String(value);
5225             if(value.length == 0){
5226                 return false;
5227             }
5228             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5229         }
5230         return function(r){
5231             return value.test(r.data[property]);
5232         };
5233     },
5234
5235     /**
5236      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5237      * @param {String} property A field on your records
5238      * @param {Number} start The record index to start at (defaults to 0)
5239      * @param {Number} end The last record index to include (defaults to length - 1)
5240      * @return {Number} The sum
5241      */
5242     sum : function(property, start, end){
5243         var rs = this.data.items, v = 0;
5244         start = start || 0;
5245         end = (end || end === 0) ? end : rs.length-1;
5246
5247         for(var i = start; i <= end; i++){
5248             v += (rs[i].data[property] || 0);
5249         }
5250         return v;
5251     },
5252
5253     /**
5254      * Filter the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      */
5260     filter : function(property, value, anyMatch){
5261         var fn = this.createFilterFn(property, value, anyMatch);
5262         return fn ? this.filterBy(fn) : this.clearFilter();
5263     },
5264
5265     /**
5266      * Filter by a function. The specified function will be called with each
5267      * record in this data source. If the function returns true the record is included,
5268      * otherwise it is filtered.
5269      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5270      * @param {Object} scope (optional) The scope of the function (defaults to this)
5271      */
5272     filterBy : function(fn, scope){
5273         this.snapshot = this.snapshot || this.data;
5274         this.data = this.queryBy(fn, scope||this);
5275         this.fireEvent("datachanged", this);
5276     },
5277
5278     /**
5279      * Query the records by a specified property.
5280      * @param {String} field A field on your records
5281      * @param {String/RegExp} value Either a string that the field
5282      * should start with or a RegExp to test against the field
5283      * @param {Boolean} anyMatch True to match any part not just the beginning
5284      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5285      */
5286     query : function(property, value, anyMatch){
5287         var fn = this.createFilterFn(property, value, anyMatch);
5288         return fn ? this.queryBy(fn) : this.data.clone();
5289     },
5290
5291     /**
5292      * Query by a function. The specified function will be called with each
5293      * record in this data source. If the function returns true the record is included
5294      * in the results.
5295      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5296      * @param {Object} scope (optional) The scope of the function (defaults to this)
5297       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      **/
5299     queryBy : function(fn, scope){
5300         var data = this.snapshot || this.data;
5301         return data.filterBy(fn, scope||this);
5302     },
5303
5304     /**
5305      * Collects unique values for a particular dataIndex from this store.
5306      * @param {String} dataIndex The property to collect
5307      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5308      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5309      * @return {Array} An array of the unique values
5310      **/
5311     collect : function(dataIndex, allowNull, bypassFilter){
5312         var d = (bypassFilter === true && this.snapshot) ?
5313                 this.snapshot.items : this.data.items;
5314         var v, sv, r = [], l = {};
5315         for(var i = 0, len = d.length; i < len; i++){
5316             v = d[i].data[dataIndex];
5317             sv = String(v);
5318             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5319                 l[sv] = true;
5320                 r[r.length] = v;
5321             }
5322         }
5323         return r;
5324     },
5325
5326     /**
5327      * Revert to a view of the Record cache with no filtering applied.
5328      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5329      */
5330     clearFilter : function(suppressEvent){
5331         if(this.snapshot && this.snapshot != this.data){
5332             this.data = this.snapshot;
5333             delete this.snapshot;
5334             if(suppressEvent !== true){
5335                 this.fireEvent("datachanged", this);
5336             }
5337         }
5338     },
5339
5340     // private
5341     afterEdit : function(record){
5342         if(this.modified.indexOf(record) == -1){
5343             this.modified.push(record);
5344         }
5345         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5346     },
5347     
5348     // private
5349     afterReject : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5352     },
5353
5354     // private
5355     afterCommit : function(record){
5356         this.modified.remove(record);
5357         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5358     },
5359
5360     /**
5361      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5362      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5363      */
5364     commitChanges : function(){
5365         var m = this.modified.slice(0);
5366         this.modified = [];
5367         for(var i = 0, len = m.length; i < len; i++){
5368             m[i].commit();
5369         }
5370     },
5371
5372     /**
5373      * Cancel outstanding changes on all changed records.
5374      */
5375     rejectChanges : function(){
5376         var m = this.modified.slice(0);
5377         this.modified = [];
5378         for(var i = 0, len = m.length; i < len; i++){
5379             m[i].reject();
5380         }
5381     },
5382
5383     onMetaChange : function(meta, rtype, o){
5384         this.recordType = rtype;
5385         this.fields = rtype.prototype.fields;
5386         delete this.snapshot;
5387         this.sortInfo = meta.sortInfo || this.sortInfo;
5388         this.modified = [];
5389         this.fireEvent('metachange', this, this.reader.meta);
5390     }
5391 });/*
5392  * Based on:
5393  * Ext JS Library 1.1.1
5394  * Copyright(c) 2006-2007, Ext JS, LLC.
5395  *
5396  * Originally Released Under LGPL - original licence link has changed is not relivant.
5397  *
5398  * Fork - LGPL
5399  * <script type="text/javascript">
5400  */
5401
5402 /**
5403  * @class Roo.data.SimpleStore
5404  * @extends Roo.data.Store
5405  * Small helper class to make creating Stores from Array data easier.
5406  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5407  * @cfg {Array} fields An array of field definition objects, or field name strings.
5408  * @cfg {Array} data The multi-dimensional array of data
5409  * @constructor
5410  * @param {Object} config
5411  */
5412 Roo.data.SimpleStore = function(config){
5413     Roo.data.SimpleStore.superclass.constructor.call(this, {
5414         isLocal : true,
5415         reader: new Roo.data.ArrayReader({
5416                 id: config.id
5417             },
5418             Roo.data.Record.create(config.fields)
5419         ),
5420         proxy : new Roo.data.MemoryProxy(config.data)
5421     });
5422     this.load();
5423 };
5424 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5425  * Based on:
5426  * Ext JS Library 1.1.1
5427  * Copyright(c) 2006-2007, Ext JS, LLC.
5428  *
5429  * Originally Released Under LGPL - original licence link has changed is not relivant.
5430  *
5431  * Fork - LGPL
5432  * <script type="text/javascript">
5433  */
5434
5435 /**
5436 /**
5437  * @extends Roo.data.Store
5438  * @class Roo.data.JsonStore
5439  * Small helper class to make creating Stores for JSON data easier. <br/>
5440 <pre><code>
5441 var store = new Roo.data.JsonStore({
5442     url: 'get-images.php',
5443     root: 'images',
5444     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5445 });
5446 </code></pre>
5447  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5448  * JsonReader and HttpProxy (unless inline data is provided).</b>
5449  * @cfg {Array} fields An array of field definition objects, or field name strings.
5450  * @constructor
5451  * @param {Object} config
5452  */
5453 Roo.data.JsonStore = function(c){
5454     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5455         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5456         reader: new Roo.data.JsonReader(c, c.fields)
5457     }));
5458 };
5459 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5460  * Based on:
5461  * Ext JS Library 1.1.1
5462  * Copyright(c) 2006-2007, Ext JS, LLC.
5463  *
5464  * Originally Released Under LGPL - original licence link has changed is not relivant.
5465  *
5466  * Fork - LGPL
5467  * <script type="text/javascript">
5468  */
5469
5470  
5471 Roo.data.Field = function(config){
5472     if(typeof config == "string"){
5473         config = {name: config};
5474     }
5475     Roo.apply(this, config);
5476     
5477     if(!this.type){
5478         this.type = "auto";
5479     }
5480     
5481     var st = Roo.data.SortTypes;
5482     // named sortTypes are supported, here we look them up
5483     if(typeof this.sortType == "string"){
5484         this.sortType = st[this.sortType];
5485     }
5486     
5487     // set default sortType for strings and dates
5488     if(!this.sortType){
5489         switch(this.type){
5490             case "string":
5491                 this.sortType = st.asUCString;
5492                 break;
5493             case "date":
5494                 this.sortType = st.asDate;
5495                 break;
5496             default:
5497                 this.sortType = st.none;
5498         }
5499     }
5500
5501     // define once
5502     var stripRe = /[\$,%]/g;
5503
5504     // prebuilt conversion function for this field, instead of
5505     // switching every time we're reading a value
5506     if(!this.convert){
5507         var cv, dateFormat = this.dateFormat;
5508         switch(this.type){
5509             case "":
5510             case "auto":
5511             case undefined:
5512                 cv = function(v){ return v; };
5513                 break;
5514             case "string":
5515                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5516                 break;
5517             case "int":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5521                     };
5522                 break;
5523             case "float":
5524                 cv = function(v){
5525                     return v !== undefined && v !== null && v !== '' ?
5526                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5527                     };
5528                 break;
5529             case "bool":
5530             case "boolean":
5531                 cv = function(v){ return v === true || v === "true" || v == 1; };
5532                 break;
5533             case "date":
5534                 cv = function(v){
5535                     if(!v){
5536                         return '';
5537                     }
5538                     if(v instanceof Date){
5539                         return v;
5540                     }
5541                     if(dateFormat){
5542                         if(dateFormat == "timestamp"){
5543                             return new Date(v*1000);
5544                         }
5545                         return Date.parseDate(v, dateFormat);
5546                     }
5547                     var parsed = Date.parse(v);
5548                     return parsed ? new Date(parsed) : null;
5549                 };
5550              break;
5551             
5552         }
5553         this.convert = cv;
5554     }
5555 };
5556
5557 Roo.data.Field.prototype = {
5558     dateFormat: null,
5559     defaultValue: "",
5560     mapping: null,
5561     sortType : null,
5562     sortDir : "ASC"
5563 };/*
5564  * Based on:
5565  * Ext JS Library 1.1.1
5566  * Copyright(c) 2006-2007, Ext JS, LLC.
5567  *
5568  * Originally Released Under LGPL - original licence link has changed is not relivant.
5569  *
5570  * Fork - LGPL
5571  * <script type="text/javascript">
5572  */
5573  
5574 // Base class for reading structured data from a data source.  This class is intended to be
5575 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5576
5577 /**
5578  * @class Roo.data.DataReader
5579  * Base class for reading structured data from a data source.  This class is intended to be
5580  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5581  */
5582
5583 Roo.data.DataReader = function(meta, recordType){
5584     
5585     this.meta = meta;
5586     
5587     this.recordType = recordType instanceof Array ? 
5588         Roo.data.Record.create(recordType) : recordType;
5589 };
5590
5591 Roo.data.DataReader.prototype = {
5592      /**
5593      * Create an empty record
5594      * @param {Object} data (optional) - overlay some values
5595      * @return {Roo.data.Record} record created.
5596      */
5597     newRow :  function(d) {
5598         var da =  {};
5599         this.recordType.prototype.fields.each(function(c) {
5600             switch( c.type) {
5601                 case 'int' : da[c.name] = 0; break;
5602                 case 'date' : da[c.name] = new Date(); break;
5603                 case 'float' : da[c.name] = 0.0; break;
5604                 case 'boolean' : da[c.name] = false; break;
5605                 default : da[c.name] = ""; break;
5606             }
5607             
5608         });
5609         return new this.recordType(Roo.apply(da, d));
5610     }
5611     
5612 };/*
5613  * Based on:
5614  * Ext JS Library 1.1.1
5615  * Copyright(c) 2006-2007, Ext JS, LLC.
5616  *
5617  * Originally Released Under LGPL - original licence link has changed is not relivant.
5618  *
5619  * Fork - LGPL
5620  * <script type="text/javascript">
5621  */
5622
5623 /**
5624  * @class Roo.data.DataProxy
5625  * @extends Roo.data.Observable
5626  * This class is an abstract base class for implementations which provide retrieval of
5627  * unformatted data objects.<br>
5628  * <p>
5629  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5630  * (of the appropriate type which knows how to parse the data object) to provide a block of
5631  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5632  * <p>
5633  * Custom implementations must implement the load method as described in
5634  * {@link Roo.data.HttpProxy#load}.
5635  */
5636 Roo.data.DataProxy = function(){
5637     this.addEvents({
5638         /**
5639          * @event beforeload
5640          * Fires before a network request is made to retrieve a data object.
5641          * @param {Object} This DataProxy object.
5642          * @param {Object} params The params parameter to the load function.
5643          */
5644         beforeload : true,
5645         /**
5646          * @event load
5647          * Fires before the load method's callback is called.
5648          * @param {Object} This DataProxy object.
5649          * @param {Object} o The data object.
5650          * @param {Object} arg The callback argument object passed to the load function.
5651          */
5652         load : true,
5653         /**
5654          * @event loadexception
5655          * Fires if an Exception occurs during data retrieval.
5656          * @param {Object} This DataProxy object.
5657          * @param {Object} o The data object.
5658          * @param {Object} arg The callback argument object passed to the load function.
5659          * @param {Object} e The Exception.
5660          */
5661         loadexception : true
5662     });
5663     Roo.data.DataProxy.superclass.constructor.call(this);
5664 };
5665
5666 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5667
5668     /**
5669      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5670      */
5671 /*
5672  * Based on:
5673  * Ext JS Library 1.1.1
5674  * Copyright(c) 2006-2007, Ext JS, LLC.
5675  *
5676  * Originally Released Under LGPL - original licence link has changed is not relivant.
5677  *
5678  * Fork - LGPL
5679  * <script type="text/javascript">
5680  */
5681 /**
5682  * @class Roo.data.MemoryProxy
5683  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5684  * to the Reader when its load method is called.
5685  * @constructor
5686  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5687  */
5688 Roo.data.MemoryProxy = function(data){
5689     if (data.data) {
5690         data = data.data;
5691     }
5692     Roo.data.MemoryProxy.superclass.constructor.call(this);
5693     this.data = data;
5694 };
5695
5696 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5697     /**
5698      * Load data from the requested source (in this case an in-memory
5699      * data object passed to the constructor), read the data object into
5700      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5701      * process that block using the passed callback.
5702      * @param {Object} params This parameter is not used by the MemoryProxy class.
5703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5704      * object into a block of Roo.data.Records.
5705      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5706      * The function must be passed <ul>
5707      * <li>The Record block object</li>
5708      * <li>The "arg" argument from the load function</li>
5709      * <li>A boolean success indicator</li>
5710      * </ul>
5711      * @param {Object} scope The scope in which to call the callback
5712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713      */
5714     load : function(params, reader, callback, scope, arg){
5715         params = params || {};
5716         var result;
5717         try {
5718             result = reader.readRecords(this.data);
5719         }catch(e){
5720             this.fireEvent("loadexception", this, arg, null, e);
5721             callback.call(scope, null, arg, false);
5722             return;
5723         }
5724         callback.call(scope, result, arg, true);
5725     },
5726     
5727     // private
5728     update : function(params, records){
5729         
5730     }
5731 });/*
5732  * Based on:
5733  * Ext JS Library 1.1.1
5734  * Copyright(c) 2006-2007, Ext JS, LLC.
5735  *
5736  * Originally Released Under LGPL - original licence link has changed is not relivant.
5737  *
5738  * Fork - LGPL
5739  * <script type="text/javascript">
5740  */
5741 /**
5742  * @class Roo.data.HttpProxy
5743  * @extends Roo.data.DataProxy
5744  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5745  * configured to reference a certain URL.<br><br>
5746  * <p>
5747  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5748  * from which the running page was served.<br><br>
5749  * <p>
5750  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5751  * <p>
5752  * Be aware that to enable the browser to parse an XML document, the server must set
5753  * the Content-Type header in the HTTP response to "text/xml".
5754  * @constructor
5755  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5756  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5757  * will be used to make the request.
5758  */
5759 Roo.data.HttpProxy = function(conn){
5760     Roo.data.HttpProxy.superclass.constructor.call(this);
5761     // is conn a conn config or a real conn?
5762     this.conn = conn;
5763     this.useAjax = !conn || !conn.events;
5764   
5765 };
5766
5767 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5768     // thse are take from connection...
5769     
5770     /**
5771      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5772      */
5773     /**
5774      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5775      * extra parameters to each request made by this object. (defaults to undefined)
5776      */
5777     /**
5778      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5779      *  to each request made by this object. (defaults to undefined)
5780      */
5781     /**
5782      * @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)
5783      */
5784     /**
5785      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5786      */
5787      /**
5788      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5789      * @type Boolean
5790      */
5791   
5792
5793     /**
5794      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5795      * @type Boolean
5796      */
5797     /**
5798      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5799      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5800      * a finer-grained basis than the DataProxy events.
5801      */
5802     getConnection : function(){
5803         return this.useAjax ? Roo.Ajax : this.conn;
5804     },
5805
5806     /**
5807      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5808      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5809      * process that block using the passed callback.
5810      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5811      * for the request to the remote server.
5812      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5813      * object into a block of Roo.data.Records.
5814      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5815      * The function must be passed <ul>
5816      * <li>The Record block object</li>
5817      * <li>The "arg" argument from the load function</li>
5818      * <li>A boolean success indicator</li>
5819      * </ul>
5820      * @param {Object} scope The scope in which to call the callback
5821      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5822      */
5823     load : function(params, reader, callback, scope, arg){
5824         if(this.fireEvent("beforeload", this, params) !== false){
5825             var  o = {
5826                 params : params || {},
5827                 request: {
5828                     callback : callback,
5829                     scope : scope,
5830                     arg : arg
5831                 },
5832                 reader: reader,
5833                 callback : this.loadResponse,
5834                 scope: this
5835             };
5836             if(this.useAjax){
5837                 Roo.applyIf(o, this.conn);
5838                 if(this.activeRequest){
5839                     Roo.Ajax.abort(this.activeRequest);
5840                 }
5841                 this.activeRequest = Roo.Ajax.request(o);
5842             }else{
5843                 this.conn.request(o);
5844             }
5845         }else{
5846             callback.call(scope||this, null, arg, false);
5847         }
5848     },
5849
5850     // private
5851     loadResponse : function(o, success, response){
5852         delete this.activeRequest;
5853         if(!success){
5854             this.fireEvent("loadexception", this, o, response);
5855             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5856             return;
5857         }
5858         var result;
5859         try {
5860             result = o.reader.read(response);
5861         }catch(e){
5862             this.fireEvent("loadexception", this, o, response, e);
5863             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5864             return;
5865         }
5866         
5867         this.fireEvent("load", this, o, o.request.arg);
5868         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5869     },
5870
5871     // private
5872     update : function(dataSet){
5873
5874     },
5875
5876     // private
5877     updateResponse : function(dataSet){
5878
5879     }
5880 });/*
5881  * Based on:
5882  * Ext JS Library 1.1.1
5883  * Copyright(c) 2006-2007, Ext JS, LLC.
5884  *
5885  * Originally Released Under LGPL - original licence link has changed is not relivant.
5886  *
5887  * Fork - LGPL
5888  * <script type="text/javascript">
5889  */
5890
5891 /**
5892  * @class Roo.data.ScriptTagProxy
5893  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5894  * other than the originating domain of the running page.<br><br>
5895  * <p>
5896  * <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
5897  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5898  * <p>
5899  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5900  * source code that is used as the source inside a &lt;script> tag.<br><br>
5901  * <p>
5902  * In order for the browser to process the returned data, the server must wrap the data object
5903  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5904  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5905  * depending on whether the callback name was passed:
5906  * <p>
5907  * <pre><code>
5908 boolean scriptTag = false;
5909 String cb = request.getParameter("callback");
5910 if (cb != null) {
5911     scriptTag = true;
5912     response.setContentType("text/javascript");
5913 } else {
5914     response.setContentType("application/x-json");
5915 }
5916 Writer out = response.getWriter();
5917 if (scriptTag) {
5918     out.write(cb + "(");
5919 }
5920 out.print(dataBlock.toJsonString());
5921 if (scriptTag) {
5922     out.write(");");
5923 }
5924 </pre></code>
5925  *
5926  * @constructor
5927  * @param {Object} config A configuration object.
5928  */
5929 Roo.data.ScriptTagProxy = function(config){
5930     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5931     Roo.apply(this, config);
5932     this.head = document.getElementsByTagName("head")[0];
5933 };
5934
5935 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5936
5937 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5938     /**
5939      * @cfg {String} url The URL from which to request the data object.
5940      */
5941     /**
5942      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5943      */
5944     timeout : 30000,
5945     /**
5946      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5947      * the server the name of the callback function set up by the load call to process the returned data object.
5948      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5949      * javascript output which calls this named function passing the data object as its only parameter.
5950      */
5951     callbackParam : "callback",
5952     /**
5953      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5954      * name to the request.
5955      */
5956     nocache : true,
5957
5958     /**
5959      * Load data from the configured URL, read the data object into
5960      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5961      * process that block using the passed callback.
5962      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5963      * for the request to the remote server.
5964      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5965      * object into a block of Roo.data.Records.
5966      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5967      * The function must be passed <ul>
5968      * <li>The Record block object</li>
5969      * <li>The "arg" argument from the load function</li>
5970      * <li>A boolean success indicator</li>
5971      * </ul>
5972      * @param {Object} scope The scope in which to call the callback
5973      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5974      */
5975     load : function(params, reader, callback, scope, arg){
5976         if(this.fireEvent("beforeload", this, params) !== false){
5977
5978             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5979
5980             var url = this.url;
5981             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5982             if(this.nocache){
5983                 url += "&_dc=" + (new Date().getTime());
5984             }
5985             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5986             var trans = {
5987                 id : transId,
5988                 cb : "stcCallback"+transId,
5989                 scriptId : "stcScript"+transId,
5990                 params : params,
5991                 arg : arg,
5992                 url : url,
5993                 callback : callback,
5994                 scope : scope,
5995                 reader : reader
5996             };
5997             var conn = this;
5998
5999             window[trans.cb] = function(o){
6000                 conn.handleResponse(o, trans);
6001             };
6002
6003             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6004
6005             if(this.autoAbort !== false){
6006                 this.abort();
6007             }
6008
6009             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6010
6011             var script = document.createElement("script");
6012             script.setAttribute("src", url);
6013             script.setAttribute("type", "text/javascript");
6014             script.setAttribute("id", trans.scriptId);
6015             this.head.appendChild(script);
6016
6017             this.trans = trans;
6018         }else{
6019             callback.call(scope||this, null, arg, false);
6020         }
6021     },
6022
6023     // private
6024     isLoading : function(){
6025         return this.trans ? true : false;
6026     },
6027
6028     /**
6029      * Abort the current server request.
6030      */
6031     abort : function(){
6032         if(this.isLoading()){
6033             this.destroyTrans(this.trans);
6034         }
6035     },
6036
6037     // private
6038     destroyTrans : function(trans, isLoaded){
6039         this.head.removeChild(document.getElementById(trans.scriptId));
6040         clearTimeout(trans.timeoutId);
6041         if(isLoaded){
6042             window[trans.cb] = undefined;
6043             try{
6044                 delete window[trans.cb];
6045             }catch(e){}
6046         }else{
6047             // if hasn't been loaded, wait for load to remove it to prevent script error
6048             window[trans.cb] = function(){
6049                 window[trans.cb] = undefined;
6050                 try{
6051                     delete window[trans.cb];
6052                 }catch(e){}
6053             };
6054         }
6055     },
6056
6057     // private
6058     handleResponse : function(o, trans){
6059         this.trans = false;
6060         this.destroyTrans(trans, true);
6061         var result;
6062         try {
6063             result = trans.reader.readRecords(o);
6064         }catch(e){
6065             this.fireEvent("loadexception", this, o, trans.arg, e);
6066             trans.callback.call(trans.scope||window, null, trans.arg, false);
6067             return;
6068         }
6069         this.fireEvent("load", this, o, trans.arg);
6070         trans.callback.call(trans.scope||window, result, trans.arg, true);
6071     },
6072
6073     // private
6074     handleFailure : function(trans){
6075         this.trans = false;
6076         this.destroyTrans(trans, false);
6077         this.fireEvent("loadexception", this, null, trans.arg);
6078         trans.callback.call(trans.scope||window, null, trans.arg, false);
6079     }
6080 });/*
6081  * Based on:
6082  * Ext JS Library 1.1.1
6083  * Copyright(c) 2006-2007, Ext JS, LLC.
6084  *
6085  * Originally Released Under LGPL - original licence link has changed is not relivant.
6086  *
6087  * Fork - LGPL
6088  * <script type="text/javascript">
6089  */
6090
6091 /**
6092  * @class Roo.data.JsonReader
6093  * @extends Roo.data.DataReader
6094  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6095  * based on mappings in a provided Roo.data.Record constructor.
6096  * 
6097  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6098  * in the reply previously. 
6099  * 
6100  * <p>
6101  * Example code:
6102  * <pre><code>
6103 var RecordDef = Roo.data.Record.create([
6104     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6105     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6106 ]);
6107 var myReader = new Roo.data.JsonReader({
6108     totalProperty: "results",    // The property which contains the total dataset size (optional)
6109     root: "rows",                // The property which contains an Array of row objects
6110     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6111 }, RecordDef);
6112 </code></pre>
6113  * <p>
6114  * This would consume a JSON file like this:
6115  * <pre><code>
6116 { 'results': 2, 'rows': [
6117     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6118     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6119 }
6120 </code></pre>
6121  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6122  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6123  * paged from the remote server.
6124  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6125  * @cfg {String} root name of the property which contains the Array of row objects.
6126  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6127  * @constructor
6128  * Create a new JsonReader
6129  * @param {Object} meta Metadata configuration options
6130  * @param {Object} recordType Either an Array of field definition objects,
6131  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6132  */
6133 Roo.data.JsonReader = function(meta, recordType){
6134     
6135     meta = meta || {};
6136     // set some defaults:
6137     Roo.applyIf(meta, {
6138         totalProperty: 'total',
6139         successProperty : 'success',
6140         root : 'data',
6141         id : 'id'
6142     });
6143     
6144     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6145 };
6146 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6147     
6148     /**
6149      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6150      * Used by Store query builder to append _requestMeta to params.
6151      * 
6152      */
6153     metaFromRemote : false,
6154     /**
6155      * This method is only used by a DataProxy which has retrieved data from a remote server.
6156      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6157      * @return {Object} data A data block which is used by an Roo.data.Store object as
6158      * a cache of Roo.data.Records.
6159      */
6160     read : function(response){
6161         var json = response.responseText;
6162        
6163         var o = /* eval:var:o */ eval("("+json+")");
6164         if(!o) {
6165             throw {message: "JsonReader.read: Json object not found"};
6166         }
6167         
6168         if(o.metaData){
6169             
6170             delete this.ef;
6171             this.metaFromRemote = true;
6172             this.meta = o.metaData;
6173             this.recordType = Roo.data.Record.create(o.metaData.fields);
6174             this.onMetaChange(this.meta, this.recordType, o);
6175         }
6176         return this.readRecords(o);
6177     },
6178
6179     // private function a store will implement
6180     onMetaChange : function(meta, recordType, o){
6181
6182     },
6183
6184     /**
6185          * @ignore
6186          */
6187     simpleAccess: function(obj, subsc) {
6188         return obj[subsc];
6189     },
6190
6191         /**
6192          * @ignore
6193          */
6194     getJsonAccessor: function(){
6195         var re = /[\[\.]/;
6196         return function(expr) {
6197             try {
6198                 return(re.test(expr))
6199                     ? new Function("obj", "return obj." + expr)
6200                     : function(obj){
6201                         return obj[expr];
6202                     };
6203             } catch(e){}
6204             return Roo.emptyFn;
6205         };
6206     }(),
6207
6208     /**
6209      * Create a data block containing Roo.data.Records from an XML document.
6210      * @param {Object} o An object which contains an Array of row objects in the property specified
6211      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6212      * which contains the total size of the dataset.
6213      * @return {Object} data A data block which is used by an Roo.data.Store object as
6214      * a cache of Roo.data.Records.
6215      */
6216     readRecords : function(o){
6217         /**
6218          * After any data loads, the raw JSON data is available for further custom processing.
6219          * @type Object
6220          */
6221         this.jsonData = o;
6222         var s = this.meta, Record = this.recordType,
6223             f = Record.prototype.fields, fi = f.items, fl = f.length;
6224
6225 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6226         if (!this.ef) {
6227             if(s.totalProperty) {
6228                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6229                 }
6230                 if(s.successProperty) {
6231                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6232                 }
6233                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6234                 if (s.id) {
6235                         var g = this.getJsonAccessor(s.id);
6236                         this.getId = function(rec) {
6237                                 var r = g(rec);
6238                                 return (r === undefined || r === "") ? null : r;
6239                         };
6240                 } else {
6241                         this.getId = function(){return null;};
6242                 }
6243             this.ef = [];
6244             for(var jj = 0; jj < fl; jj++){
6245                 f = fi[jj];
6246                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6247                 this.ef[jj] = this.getJsonAccessor(map);
6248             }
6249         }
6250
6251         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6252         if(s.totalProperty){
6253             var vt = parseInt(this.getTotal(o), 10);
6254             if(!isNaN(vt)){
6255                 totalRecords = vt;
6256             }
6257         }
6258         if(s.successProperty){
6259             var vs = this.getSuccess(o);
6260             if(vs === false || vs === 'false'){
6261                 success = false;
6262             }
6263         }
6264         var records = [];
6265             for(var i = 0; i < c; i++){
6266                     var n = root[i];
6267                 var values = {};
6268                 var id = this.getId(n);
6269                 for(var j = 0; j < fl; j++){
6270                     f = fi[j];
6271                 var v = this.ef[j](n);
6272                 if (!f.convert) {
6273                     Roo.log('missing convert for ' + f.name);
6274                     Roo.log(f);
6275                     continue;
6276                 }
6277                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6278                 }
6279                 var record = new Record(values, id);
6280                 record.json = n;
6281                 records[i] = record;
6282             }
6283             return {
6284                 success : success,
6285                 records : records,
6286                 totalRecords : totalRecords
6287             };
6288     }
6289 });/*
6290  * Based on:
6291  * Ext JS Library 1.1.1
6292  * Copyright(c) 2006-2007, Ext JS, LLC.
6293  *
6294  * Originally Released Under LGPL - original licence link has changed is not relivant.
6295  *
6296  * Fork - LGPL
6297  * <script type="text/javascript">
6298  */
6299
6300 /**
6301  * @class Roo.data.XmlReader
6302  * @extends Roo.data.DataReader
6303  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6304  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6305  * <p>
6306  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6307  * header in the HTTP response must be set to "text/xml".</em>
6308  * <p>
6309  * Example code:
6310  * <pre><code>
6311 var RecordDef = Roo.data.Record.create([
6312    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6313    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6314 ]);
6315 var myReader = new Roo.data.XmlReader({
6316    totalRecords: "results", // The element which contains the total dataset size (optional)
6317    record: "row",           // The repeated element which contains row information
6318    id: "id"                 // The element within the row that provides an ID for the record (optional)
6319 }, RecordDef);
6320 </code></pre>
6321  * <p>
6322  * This would consume an XML file like this:
6323  * <pre><code>
6324 &lt;?xml?>
6325 &lt;dataset>
6326  &lt;results>2&lt;/results>
6327  &lt;row>
6328    &lt;id>1&lt;/id>
6329    &lt;name>Bill&lt;/name>
6330    &lt;occupation>Gardener&lt;/occupation>
6331  &lt;/row>
6332  &lt;row>
6333    &lt;id>2&lt;/id>
6334    &lt;name>Ben&lt;/name>
6335    &lt;occupation>Horticulturalist&lt;/occupation>
6336  &lt;/row>
6337 &lt;/dataset>
6338 </code></pre>
6339  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6340  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6341  * paged from the remote server.
6342  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6343  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6344  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6345  * a record identifier value.
6346  * @constructor
6347  * Create a new XmlReader
6348  * @param {Object} meta Metadata configuration options
6349  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6350  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6351  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6352  */
6353 Roo.data.XmlReader = function(meta, recordType){
6354     meta = meta || {};
6355     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6356 };
6357 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6358     /**
6359      * This method is only used by a DataProxy which has retrieved data from a remote server.
6360          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6361          * to contain a method called 'responseXML' that returns an XML document object.
6362      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6363      * a cache of Roo.data.Records.
6364      */
6365     read : function(response){
6366         var doc = response.responseXML;
6367         if(!doc) {
6368             throw {message: "XmlReader.read: XML Document not available"};
6369         }
6370         return this.readRecords(doc);
6371     },
6372
6373     /**
6374      * Create a data block containing Roo.data.Records from an XML document.
6375          * @param {Object} doc A parsed XML document.
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     readRecords : function(doc){
6380         /**
6381          * After any data loads/reads, the raw XML Document is available for further custom processing.
6382          * @type XMLDocument
6383          */
6384         this.xmlData = doc;
6385         var root = doc.documentElement || doc;
6386         var q = Roo.DomQuery;
6387         var recordType = this.recordType, fields = recordType.prototype.fields;
6388         var sid = this.meta.id;
6389         var totalRecords = 0, success = true;
6390         if(this.meta.totalRecords){
6391             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6392         }
6393         
6394         if(this.meta.success){
6395             var sv = q.selectValue(this.meta.success, root, true);
6396             success = sv !== false && sv !== 'false';
6397         }
6398         var records = [];
6399         var ns = q.select(this.meta.record, root);
6400         for(var i = 0, len = ns.length; i < len; i++) {
6401                 var n = ns[i];
6402                 var values = {};
6403                 var id = sid ? q.selectValue(sid, n) : undefined;
6404                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6405                     var f = fields.items[j];
6406                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6407                     v = f.convert(v);
6408                     values[f.name] = v;
6409                 }
6410                 var record = new recordType(values, id);
6411                 record.node = n;
6412                 records[records.length] = record;
6413             }
6414
6415             return {
6416                 success : success,
6417                 records : records,
6418                 totalRecords : totalRecords || records.length
6419             };
6420     }
6421 });/*
6422  * Based on:
6423  * Ext JS Library 1.1.1
6424  * Copyright(c) 2006-2007, Ext JS, LLC.
6425  *
6426  * Originally Released Under LGPL - original licence link has changed is not relivant.
6427  *
6428  * Fork - LGPL
6429  * <script type="text/javascript">
6430  */
6431
6432 /**
6433  * @class Roo.data.ArrayReader
6434  * @extends Roo.data.DataReader
6435  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6436  * Each element of that Array represents a row of data fields. The
6437  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6438  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6439  * <p>
6440  * Example code:.
6441  * <pre><code>
6442 var RecordDef = Roo.data.Record.create([
6443     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6444     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6445 ]);
6446 var myReader = new Roo.data.ArrayReader({
6447     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6448 }, RecordDef);
6449 </code></pre>
6450  * <p>
6451  * This would consume an Array like this:
6452  * <pre><code>
6453 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6454   </code></pre>
6455  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6456  * @constructor
6457  * Create a new JsonReader
6458  * @param {Object} meta Metadata configuration options.
6459  * @param {Object} recordType Either an Array of field definition objects
6460  * as specified to {@link Roo.data.Record#create},
6461  * or an {@link Roo.data.Record} object
6462  * created using {@link Roo.data.Record#create}.
6463  */
6464 Roo.data.ArrayReader = function(meta, recordType){
6465     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6466 };
6467
6468 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6469     /**
6470      * Create a data block containing Roo.data.Records from an XML document.
6471      * @param {Object} o An Array of row objects which represents the dataset.
6472      * @return {Object} data A data block which is used by an Roo.data.Store object as
6473      * a cache of Roo.data.Records.
6474      */
6475     readRecords : function(o){
6476         var sid = this.meta ? this.meta.id : null;
6477         var recordType = this.recordType, fields = recordType.prototype.fields;
6478         var records = [];
6479         var root = o;
6480             for(var i = 0; i < root.length; i++){
6481                     var n = root[i];
6482                 var values = {};
6483                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6484                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6485                 var f = fields.items[j];
6486                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6487                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6488                 v = f.convert(v);
6489                 values[f.name] = v;
6490             }
6491                 var record = new recordType(values, id);
6492                 record.json = n;
6493                 records[records.length] = record;
6494             }
6495             return {
6496                 records : records,
6497                 totalRecords : records.length
6498             };
6499     }
6500 });/*
6501  * Based on:
6502  * Ext JS Library 1.1.1
6503  * Copyright(c) 2006-2007, Ext JS, LLC.
6504  *
6505  * Originally Released Under LGPL - original licence link has changed is not relivant.
6506  *
6507  * Fork - LGPL
6508  * <script type="text/javascript">
6509  */
6510
6511
6512 /**
6513  * @class Roo.data.Tree
6514  * @extends Roo.util.Observable
6515  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6516  * in the tree have most standard DOM functionality.
6517  * @constructor
6518  * @param {Node} root (optional) The root node
6519  */
6520 Roo.data.Tree = function(root){
6521    this.nodeHash = {};
6522    /**
6523     * The root node for this tree
6524     * @type Node
6525     */
6526    this.root = null;
6527    if(root){
6528        this.setRootNode(root);
6529    }
6530    this.addEvents({
6531        /**
6532         * @event append
6533         * Fires when a new child node is appended to a node in this tree.
6534         * @param {Tree} tree The owner tree
6535         * @param {Node} parent The parent node
6536         * @param {Node} node The newly appended node
6537         * @param {Number} index The index of the newly appended node
6538         */
6539        "append" : true,
6540        /**
6541         * @event remove
6542         * Fires when a child node is removed from a node in this tree.
6543         * @param {Tree} tree The owner tree
6544         * @param {Node} parent The parent node
6545         * @param {Node} node The child node removed
6546         */
6547        "remove" : true,
6548        /**
6549         * @event move
6550         * Fires when a node is moved to a new location in the tree
6551         * @param {Tree} tree The owner tree
6552         * @param {Node} node The node moved
6553         * @param {Node} oldParent The old parent of this node
6554         * @param {Node} newParent The new parent of this node
6555         * @param {Number} index The index it was moved to
6556         */
6557        "move" : true,
6558        /**
6559         * @event insert
6560         * Fires when a new child node is inserted in a node in this tree.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} parent The parent node
6563         * @param {Node} node The child node inserted
6564         * @param {Node} refNode The child node the node was inserted before
6565         */
6566        "insert" : true,
6567        /**
6568         * @event beforeappend
6569         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6570         * @param {Tree} tree The owner tree
6571         * @param {Node} parent The parent node
6572         * @param {Node} node The child node to be appended
6573         */
6574        "beforeappend" : true,
6575        /**
6576         * @event beforeremove
6577         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6578         * @param {Tree} tree The owner tree
6579         * @param {Node} parent The parent node
6580         * @param {Node} node The child node to be removed
6581         */
6582        "beforeremove" : true,
6583        /**
6584         * @event beforemove
6585         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6586         * @param {Tree} tree The owner tree
6587         * @param {Node} node The node being moved
6588         * @param {Node} oldParent The parent of the node
6589         * @param {Node} newParent The new parent the node is moving to
6590         * @param {Number} index The index it is being moved to
6591         */
6592        "beforemove" : true,
6593        /**
6594         * @event beforeinsert
6595         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} parent The parent node
6598         * @param {Node} node The child node to be inserted
6599         * @param {Node} refNode The child node the node is being inserted before
6600         */
6601        "beforeinsert" : true
6602    });
6603
6604     Roo.data.Tree.superclass.constructor.call(this);
6605 };
6606
6607 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6608     pathSeparator: "/",
6609
6610     proxyNodeEvent : function(){
6611         return this.fireEvent.apply(this, arguments);
6612     },
6613
6614     /**
6615      * Returns the root node for this tree.
6616      * @return {Node}
6617      */
6618     getRootNode : function(){
6619         return this.root;
6620     },
6621
6622     /**
6623      * Sets the root node for this tree.
6624      * @param {Node} node
6625      * @return {Node}
6626      */
6627     setRootNode : function(node){
6628         this.root = node;
6629         node.ownerTree = this;
6630         node.isRoot = true;
6631         this.registerNode(node);
6632         return node;
6633     },
6634
6635     /**
6636      * Gets a node in this tree by its id.
6637      * @param {String} id
6638      * @return {Node}
6639      */
6640     getNodeById : function(id){
6641         return this.nodeHash[id];
6642     },
6643
6644     registerNode : function(node){
6645         this.nodeHash[node.id] = node;
6646     },
6647
6648     unregisterNode : function(node){
6649         delete this.nodeHash[node.id];
6650     },
6651
6652     toString : function(){
6653         return "[Tree"+(this.id?" "+this.id:"")+"]";
6654     }
6655 });
6656
6657 /**
6658  * @class Roo.data.Node
6659  * @extends Roo.util.Observable
6660  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6661  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6662  * @constructor
6663  * @param {Object} attributes The attributes/config for the node
6664  */
6665 Roo.data.Node = function(attributes){
6666     /**
6667      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6668      * @type {Object}
6669      */
6670     this.attributes = attributes || {};
6671     this.leaf = this.attributes.leaf;
6672     /**
6673      * The node id. @type String
6674      */
6675     this.id = this.attributes.id;
6676     if(!this.id){
6677         this.id = Roo.id(null, "ynode-");
6678         this.attributes.id = this.id;
6679     }
6680      
6681     
6682     /**
6683      * All child nodes of this node. @type Array
6684      */
6685     this.childNodes = [];
6686     if(!this.childNodes.indexOf){ // indexOf is a must
6687         this.childNodes.indexOf = function(o){
6688             for(var i = 0, len = this.length; i < len; i++){
6689                 if(this[i] == o) {
6690                     return i;
6691                 }
6692             }
6693             return -1;
6694         };
6695     }
6696     /**
6697      * The parent node for this node. @type Node
6698      */
6699     this.parentNode = null;
6700     /**
6701      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6702      */
6703     this.firstChild = null;
6704     /**
6705      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6706      */
6707     this.lastChild = null;
6708     /**
6709      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6710      */
6711     this.previousSibling = null;
6712     /**
6713      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6714      */
6715     this.nextSibling = null;
6716
6717     this.addEvents({
6718        /**
6719         * @event append
6720         * Fires when a new child node is appended
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The newly appended node
6724         * @param {Number} index The index of the newly appended node
6725         */
6726        "append" : true,
6727        /**
6728         * @event remove
6729         * Fires when a child node is removed
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The removed node
6733         */
6734        "remove" : true,
6735        /**
6736         * @event move
6737         * Fires when this node is moved to a new location in the tree
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} oldParent The old parent of this node
6741         * @param {Node} newParent The new parent of this node
6742         * @param {Number} index The index it was moved to
6743         */
6744        "move" : true,
6745        /**
6746         * @event insert
6747         * Fires when a new child node is inserted.
6748         * @param {Tree} tree The owner tree
6749         * @param {Node} this This node
6750         * @param {Node} node The child node inserted
6751         * @param {Node} refNode The child node the node was inserted before
6752         */
6753        "insert" : true,
6754        /**
6755         * @event beforeappend
6756         * Fires before a new child is appended, return false to cancel the append.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be appended
6760         */
6761        "beforeappend" : true,
6762        /**
6763         * @event beforeremove
6764         * Fires before a child is removed, return false to cancel the remove.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} node The child node to be removed
6768         */
6769        "beforeremove" : true,
6770        /**
6771         * @event beforemove
6772         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6773         * @param {Tree} tree The owner tree
6774         * @param {Node} this This node
6775         * @param {Node} oldParent The parent of this node
6776         * @param {Node} newParent The new parent this node is moving to
6777         * @param {Number} index The index it is being moved to
6778         */
6779        "beforemove" : true,
6780        /**
6781         * @event beforeinsert
6782         * Fires before a new child is inserted, return false to cancel the insert.
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} node The child node to be inserted
6786         * @param {Node} refNode The child node the node is being inserted before
6787         */
6788        "beforeinsert" : true
6789    });
6790     this.listeners = this.attributes.listeners;
6791     Roo.data.Node.superclass.constructor.call(this);
6792 };
6793
6794 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6795     fireEvent : function(evtName){
6796         // first do standard event for this node
6797         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6798             return false;
6799         }
6800         // then bubble it up to the tree if the event wasn't cancelled
6801         var ot = this.getOwnerTree();
6802         if(ot){
6803             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6804                 return false;
6805             }
6806         }
6807         return true;
6808     },
6809
6810     /**
6811      * Returns true if this node is a leaf
6812      * @return {Boolean}
6813      */
6814     isLeaf : function(){
6815         return this.leaf === true;
6816     },
6817
6818     // private
6819     setFirstChild : function(node){
6820         this.firstChild = node;
6821     },
6822
6823     //private
6824     setLastChild : function(node){
6825         this.lastChild = node;
6826     },
6827
6828
6829     /**
6830      * Returns true if this node is the last child of its parent
6831      * @return {Boolean}
6832      */
6833     isLast : function(){
6834        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6835     },
6836
6837     /**
6838      * Returns true if this node is the first child of its parent
6839      * @return {Boolean}
6840      */
6841     isFirst : function(){
6842        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6843     },
6844
6845     hasChildNodes : function(){
6846         return !this.isLeaf() && this.childNodes.length > 0;
6847     },
6848
6849     /**
6850      * Insert node(s) as the last child node of this node.
6851      * @param {Node/Array} node The node or Array of nodes to append
6852      * @return {Node} The appended node if single append, or null if an array was passed
6853      */
6854     appendChild : function(node){
6855         var multi = false;
6856         if(node instanceof Array){
6857             multi = node;
6858         }else if(arguments.length > 1){
6859             multi = arguments;
6860         }
6861         // if passed an array or multiple args do them one by one
6862         if(multi){
6863             for(var i = 0, len = multi.length; i < len; i++) {
6864                 this.appendChild(multi[i]);
6865             }
6866         }else{
6867             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6868                 return false;
6869             }
6870             var index = this.childNodes.length;
6871             var oldParent = node.parentNode;
6872             // it's a move, make sure we move it cleanly
6873             if(oldParent){
6874                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6875                     return false;
6876                 }
6877                 oldParent.removeChild(node);
6878             }
6879             index = this.childNodes.length;
6880             if(index == 0){
6881                 this.setFirstChild(node);
6882             }
6883             this.childNodes.push(node);
6884             node.parentNode = this;
6885             var ps = this.childNodes[index-1];
6886             if(ps){
6887                 node.previousSibling = ps;
6888                 ps.nextSibling = node;
6889             }else{
6890                 node.previousSibling = null;
6891             }
6892             node.nextSibling = null;
6893             this.setLastChild(node);
6894             node.setOwnerTree(this.getOwnerTree());
6895             this.fireEvent("append", this.ownerTree, this, node, index);
6896             if(oldParent){
6897                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6898             }
6899             return node;
6900         }
6901     },
6902
6903     /**
6904      * Removes a child node from this node.
6905      * @param {Node} node The node to remove
6906      * @return {Node} The removed node
6907      */
6908     removeChild : function(node){
6909         var index = this.childNodes.indexOf(node);
6910         if(index == -1){
6911             return false;
6912         }
6913         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6914             return false;
6915         }
6916
6917         // remove it from childNodes collection
6918         this.childNodes.splice(index, 1);
6919
6920         // update siblings
6921         if(node.previousSibling){
6922             node.previousSibling.nextSibling = node.nextSibling;
6923         }
6924         if(node.nextSibling){
6925             node.nextSibling.previousSibling = node.previousSibling;
6926         }
6927
6928         // update child refs
6929         if(this.firstChild == node){
6930             this.setFirstChild(node.nextSibling);
6931         }
6932         if(this.lastChild == node){
6933             this.setLastChild(node.previousSibling);
6934         }
6935
6936         node.setOwnerTree(null);
6937         // clear any references from the node
6938         node.parentNode = null;
6939         node.previousSibling = null;
6940         node.nextSibling = null;
6941         this.fireEvent("remove", this.ownerTree, this, node);
6942         return node;
6943     },
6944
6945     /**
6946      * Inserts the first node before the second node in this nodes childNodes collection.
6947      * @param {Node} node The node to insert
6948      * @param {Node} refNode The node to insert before (if null the node is appended)
6949      * @return {Node} The inserted node
6950      */
6951     insertBefore : function(node, refNode){
6952         if(!refNode){ // like standard Dom, refNode can be null for append
6953             return this.appendChild(node);
6954         }
6955         // nothing to do
6956         if(node == refNode){
6957             return false;
6958         }
6959
6960         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6961             return false;
6962         }
6963         var index = this.childNodes.indexOf(refNode);
6964         var oldParent = node.parentNode;
6965         var refIndex = index;
6966
6967         // when moving internally, indexes will change after remove
6968         if(oldParent == this && this.childNodes.indexOf(node) < index){
6969             refIndex--;
6970         }
6971
6972         // it's a move, make sure we move it cleanly
6973         if(oldParent){
6974             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6975                 return false;
6976             }
6977             oldParent.removeChild(node);
6978         }
6979         if(refIndex == 0){
6980             this.setFirstChild(node);
6981         }
6982         this.childNodes.splice(refIndex, 0, node);
6983         node.parentNode = this;
6984         var ps = this.childNodes[refIndex-1];
6985         if(ps){
6986             node.previousSibling = ps;
6987             ps.nextSibling = node;
6988         }else{
6989             node.previousSibling = null;
6990         }
6991         node.nextSibling = refNode;
6992         refNode.previousSibling = node;
6993         node.setOwnerTree(this.getOwnerTree());
6994         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6995         if(oldParent){
6996             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6997         }
6998         return node;
6999     },
7000
7001     /**
7002      * Returns the child node at the specified index.
7003      * @param {Number} index
7004      * @return {Node}
7005      */
7006     item : function(index){
7007         return this.childNodes[index];
7008     },
7009
7010     /**
7011      * Replaces one child node in this node with another.
7012      * @param {Node} newChild The replacement node
7013      * @param {Node} oldChild The node to replace
7014      * @return {Node} The replaced node
7015      */
7016     replaceChild : function(newChild, oldChild){
7017         this.insertBefore(newChild, oldChild);
7018         this.removeChild(oldChild);
7019         return oldChild;
7020     },
7021
7022     /**
7023      * Returns the index of a child node
7024      * @param {Node} node
7025      * @return {Number} The index of the node or -1 if it was not found
7026      */
7027     indexOf : function(child){
7028         return this.childNodes.indexOf(child);
7029     },
7030
7031     /**
7032      * Returns the tree this node is in.
7033      * @return {Tree}
7034      */
7035     getOwnerTree : function(){
7036         // if it doesn't have one, look for one
7037         if(!this.ownerTree){
7038             var p = this;
7039             while(p){
7040                 if(p.ownerTree){
7041                     this.ownerTree = p.ownerTree;
7042                     break;
7043                 }
7044                 p = p.parentNode;
7045             }
7046         }
7047         return this.ownerTree;
7048     },
7049
7050     /**
7051      * Returns depth of this node (the root node has a depth of 0)
7052      * @return {Number}
7053      */
7054     getDepth : function(){
7055         var depth = 0;
7056         var p = this;
7057         while(p.parentNode){
7058             ++depth;
7059             p = p.parentNode;
7060         }
7061         return depth;
7062     },
7063
7064     // private
7065     setOwnerTree : function(tree){
7066         // if it's move, we need to update everyone
7067         if(tree != this.ownerTree){
7068             if(this.ownerTree){
7069                 this.ownerTree.unregisterNode(this);
7070             }
7071             this.ownerTree = tree;
7072             var cs = this.childNodes;
7073             for(var i = 0, len = cs.length; i < len; i++) {
7074                 cs[i].setOwnerTree(tree);
7075             }
7076             if(tree){
7077                 tree.registerNode(this);
7078             }
7079         }
7080     },
7081
7082     /**
7083      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7084      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7085      * @return {String} The path
7086      */
7087     getPath : function(attr){
7088         attr = attr || "id";
7089         var p = this.parentNode;
7090         var b = [this.attributes[attr]];
7091         while(p){
7092             b.unshift(p.attributes[attr]);
7093             p = p.parentNode;
7094         }
7095         var sep = this.getOwnerTree().pathSeparator;
7096         return sep + b.join(sep);
7097     },
7098
7099     /**
7100      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7101      * function call will be the scope provided or the current node. The arguments to the function
7102      * will be the args provided or the current node. If the function returns false at any point,
7103      * the bubble is stopped.
7104      * @param {Function} fn The function to call
7105      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7106      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7107      */
7108     bubble : function(fn, scope, args){
7109         var p = this;
7110         while(p){
7111             if(fn.call(scope || p, args || p) === false){
7112                 break;
7113             }
7114             p = p.parentNode;
7115         }
7116     },
7117
7118     /**
7119      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7120      * function call will be the scope provided or the current node. The arguments to the function
7121      * will be the args provided or the current node. If the function returns false at any point,
7122      * the cascade is stopped on that branch.
7123      * @param {Function} fn The function to call
7124      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7125      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7126      */
7127     cascade : function(fn, scope, args){
7128         if(fn.call(scope || this, args || this) !== false){
7129             var cs = this.childNodes;
7130             for(var i = 0, len = cs.length; i < len; i++) {
7131                 cs[i].cascade(fn, scope, args);
7132             }
7133         }
7134     },
7135
7136     /**
7137      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7138      * function call will be the scope provided or the current node. The arguments to the function
7139      * will be the args provided or the current node. If the function returns false at any point,
7140      * the iteration stops.
7141      * @param {Function} fn The function to call
7142      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7143      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7144      */
7145     eachChild : function(fn, scope, args){
7146         var cs = this.childNodes;
7147         for(var i = 0, len = cs.length; i < len; i++) {
7148                 if(fn.call(scope || this, args || cs[i]) === false){
7149                     break;
7150                 }
7151         }
7152     },
7153
7154     /**
7155      * Finds the first child that has the attribute with the specified value.
7156      * @param {String} attribute The attribute name
7157      * @param {Mixed} value The value to search for
7158      * @return {Node} The found child or null if none was found
7159      */
7160     findChild : function(attribute, value){
7161         var cs = this.childNodes;
7162         for(var i = 0, len = cs.length; i < len; i++) {
7163                 if(cs[i].attributes[attribute] == value){
7164                     return cs[i];
7165                 }
7166         }
7167         return null;
7168     },
7169
7170     /**
7171      * Finds the first child by a custom function. The child matches if the function passed
7172      * returns true.
7173      * @param {Function} fn
7174      * @param {Object} scope (optional)
7175      * @return {Node} The found child or null if none was found
7176      */
7177     findChildBy : function(fn, scope){
7178         var cs = this.childNodes;
7179         for(var i = 0, len = cs.length; i < len; i++) {
7180                 if(fn.call(scope||cs[i], cs[i]) === true){
7181                     return cs[i];
7182                 }
7183         }
7184         return null;
7185     },
7186
7187     /**
7188      * Sorts this nodes children using the supplied sort function
7189      * @param {Function} fn
7190      * @param {Object} scope (optional)
7191      */
7192     sort : function(fn, scope){
7193         var cs = this.childNodes;
7194         var len = cs.length;
7195         if(len > 0){
7196             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7197             cs.sort(sortFn);
7198             for(var i = 0; i < len; i++){
7199                 var n = cs[i];
7200                 n.previousSibling = cs[i-1];
7201                 n.nextSibling = cs[i+1];
7202                 if(i == 0){
7203                     this.setFirstChild(n);
7204                 }
7205                 if(i == len-1){
7206                     this.setLastChild(n);
7207                 }
7208             }
7209         }
7210     },
7211
7212     /**
7213      * Returns true if this node is an ancestor (at any point) of the passed node.
7214      * @param {Node} node
7215      * @return {Boolean}
7216      */
7217     contains : function(node){
7218         return node.isAncestor(this);
7219     },
7220
7221     /**
7222      * Returns true if the passed node is an ancestor (at any point) of this node.
7223      * @param {Node} node
7224      * @return {Boolean}
7225      */
7226     isAncestor : function(node){
7227         var p = this.parentNode;
7228         while(p){
7229             if(p == node){
7230                 return true;
7231             }
7232             p = p.parentNode;
7233         }
7234         return false;
7235     },
7236
7237     toString : function(){
7238         return "[Node"+(this.id?" "+this.id:"")+"]";
7239     }
7240 });/*
7241  * Based on:
7242  * Ext JS Library 1.1.1
7243  * Copyright(c) 2006-2007, Ext JS, LLC.
7244  *
7245  * Originally Released Under LGPL - original licence link has changed is not relivant.
7246  *
7247  * Fork - LGPL
7248  * <script type="text/javascript">
7249  */
7250  
7251
7252 /**
7253  * @class Roo.ComponentMgr
7254  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7255  * @singleton
7256  */
7257 Roo.ComponentMgr = function(){
7258     var all = new Roo.util.MixedCollection();
7259
7260     return {
7261         /**
7262          * Registers a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         register : function(c){
7266             all.add(c);
7267         },
7268
7269         /**
7270          * Unregisters a component.
7271          * @param {Roo.Component} c The component
7272          */
7273         unregister : function(c){
7274             all.remove(c);
7275         },
7276
7277         /**
7278          * Returns a component by id
7279          * @param {String} id The component id
7280          */
7281         get : function(id){
7282             return all.get(id);
7283         },
7284
7285         /**
7286          * Registers a function that will be called when a specified component is added to ComponentMgr
7287          * @param {String} id The component id
7288          * @param {Funtction} fn The callback function
7289          * @param {Object} scope The scope of the callback
7290          */
7291         onAvailable : function(id, fn, scope){
7292             all.on("add", function(index, o){
7293                 if(o.id == id){
7294                     fn.call(scope || o, o);
7295                     all.un("add", fn, scope);
7296                 }
7297             });
7298         }
7299     };
7300 }();/*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  
7311 /**
7312  * @class Roo.Component
7313  * @extends Roo.util.Observable
7314  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7315  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7316  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7317  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7318  * All visual components (widgets) that require rendering into a layout should subclass Component.
7319  * @constructor
7320  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7321  * 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
7322  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7323  */
7324 Roo.Component = function(config){
7325     config = config || {};
7326     if(config.tagName || config.dom || typeof config == "string"){ // element object
7327         config = {el: config, id: config.id || config};
7328     }
7329     this.initialConfig = config;
7330
7331     Roo.apply(this, config);
7332     this.addEvents({
7333         /**
7334          * @event disable
7335          * Fires after the component is disabled.
7336              * @param {Roo.Component} this
7337              */
7338         disable : true,
7339         /**
7340          * @event enable
7341          * Fires after the component is enabled.
7342              * @param {Roo.Component} this
7343              */
7344         enable : true,
7345         /**
7346          * @event beforeshow
7347          * Fires before the component is shown.  Return false to stop the show.
7348              * @param {Roo.Component} this
7349              */
7350         beforeshow : true,
7351         /**
7352          * @event show
7353          * Fires after the component is shown.
7354              * @param {Roo.Component} this
7355              */
7356         show : true,
7357         /**
7358          * @event beforehide
7359          * Fires before the component is hidden. Return false to stop the hide.
7360              * @param {Roo.Component} this
7361              */
7362         beforehide : true,
7363         /**
7364          * @event hide
7365          * Fires after the component is hidden.
7366              * @param {Roo.Component} this
7367              */
7368         hide : true,
7369         /**
7370          * @event beforerender
7371          * Fires before the component is rendered. Return false to stop the render.
7372              * @param {Roo.Component} this
7373              */
7374         beforerender : true,
7375         /**
7376          * @event render
7377          * Fires after the component is rendered.
7378              * @param {Roo.Component} this
7379              */
7380         render : true,
7381         /**
7382          * @event beforedestroy
7383          * Fires before the component is destroyed. Return false to stop the destroy.
7384              * @param {Roo.Component} this
7385              */
7386         beforedestroy : true,
7387         /**
7388          * @event destroy
7389          * Fires after the component is destroyed.
7390              * @param {Roo.Component} this
7391              */
7392         destroy : true
7393     });
7394     if(!this.id){
7395         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7396     }
7397     Roo.ComponentMgr.register(this);
7398     Roo.Component.superclass.constructor.call(this);
7399     this.initComponent();
7400     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7401         this.render(this.renderTo);
7402         delete this.renderTo;
7403     }
7404 };
7405
7406 /** @private */
7407 Roo.Component.AUTO_ID = 1000;
7408
7409 Roo.extend(Roo.Component, Roo.util.Observable, {
7410     /**
7411      * @scope Roo.Component.prototype
7412      * @type {Boolean}
7413      * true if this component is hidden. Read-only.
7414      */
7415     hidden : false,
7416     /**
7417      * @type {Boolean}
7418      * true if this component is disabled. Read-only.
7419      */
7420     disabled : false,
7421     /**
7422      * @type {Boolean}
7423      * true if this component has been rendered. Read-only.
7424      */
7425     rendered : false,
7426     
7427     /** @cfg {String} disableClass
7428      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7429      */
7430     disabledClass : "x-item-disabled",
7431         /** @cfg {Boolean} allowDomMove
7432          * Whether the component can move the Dom node when rendering (defaults to true).
7433          */
7434     allowDomMove : true,
7435     /** @cfg {String} hideMode
7436      * How this component should hidden. Supported values are
7437      * "visibility" (css visibility), "offsets" (negative offset position) and
7438      * "display" (css display) - defaults to "display".
7439      */
7440     hideMode: 'display',
7441
7442     /** @private */
7443     ctype : "Roo.Component",
7444
7445     /**
7446      * @cfg {String} actionMode 
7447      * which property holds the element that used for  hide() / show() / disable() / enable()
7448      * default is 'el' 
7449      */
7450     actionMode : "el",
7451
7452     /** @private */
7453     getActionEl : function(){
7454         return this[this.actionMode];
7455     },
7456
7457     initComponent : Roo.emptyFn,
7458     /**
7459      * If this is a lazy rendering component, render it to its container element.
7460      * @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.
7461      */
7462     render : function(container, position){
7463         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7464             if(!container && this.el){
7465                 this.el = Roo.get(this.el);
7466                 container = this.el.dom.parentNode;
7467                 this.allowDomMove = false;
7468             }
7469             this.container = Roo.get(container);
7470             this.rendered = true;
7471             if(position !== undefined){
7472                 if(typeof position == 'number'){
7473                     position = this.container.dom.childNodes[position];
7474                 }else{
7475                     position = Roo.getDom(position);
7476                 }
7477             }
7478             this.onRender(this.container, position || null);
7479             if(this.cls){
7480                 this.el.addClass(this.cls);
7481                 delete this.cls;
7482             }
7483             if(this.style){
7484                 this.el.applyStyles(this.style);
7485                 delete this.style;
7486             }
7487             this.fireEvent("render", this);
7488             this.afterRender(this.container);
7489             if(this.hidden){
7490                 this.hide();
7491             }
7492             if(this.disabled){
7493                 this.disable();
7494             }
7495         }
7496         return this;
7497     },
7498
7499     /** @private */
7500     // default function is not really useful
7501     onRender : function(ct, position){
7502         if(this.el){
7503             this.el = Roo.get(this.el);
7504             if(this.allowDomMove !== false){
7505                 ct.dom.insertBefore(this.el.dom, position);
7506             }
7507         }
7508     },
7509
7510     /** @private */
7511     getAutoCreate : function(){
7512         var cfg = typeof this.autoCreate == "object" ?
7513                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7514         if(this.id && !cfg.id){
7515             cfg.id = this.id;
7516         }
7517         return cfg;
7518     },
7519
7520     /** @private */
7521     afterRender : Roo.emptyFn,
7522
7523     /**
7524      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7525      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7526      */
7527     destroy : function(){
7528         if(this.fireEvent("beforedestroy", this) !== false){
7529             this.purgeListeners();
7530             this.beforeDestroy();
7531             if(this.rendered){
7532                 this.el.removeAllListeners();
7533                 this.el.remove();
7534                 if(this.actionMode == "container"){
7535                     this.container.remove();
7536                 }
7537             }
7538             this.onDestroy();
7539             Roo.ComponentMgr.unregister(this);
7540             this.fireEvent("destroy", this);
7541         }
7542     },
7543
7544         /** @private */
7545     beforeDestroy : function(){
7546
7547     },
7548
7549         /** @private */
7550         onDestroy : function(){
7551
7552     },
7553
7554     /**
7555      * Returns the underlying {@link Roo.Element}.
7556      * @return {Roo.Element} The element
7557      */
7558     getEl : function(){
7559         return this.el;
7560     },
7561
7562     /**
7563      * Returns the id of this component.
7564      * @return {String}
7565      */
7566     getId : function(){
7567         return this.id;
7568     },
7569
7570     /**
7571      * Try to focus this component.
7572      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7573      * @return {Roo.Component} this
7574      */
7575     focus : function(selectText){
7576         if(this.rendered){
7577             this.el.focus();
7578             if(selectText === true){
7579                 this.el.dom.select();
7580             }
7581         }
7582         return this;
7583     },
7584
7585     /** @private */
7586     blur : function(){
7587         if(this.rendered){
7588             this.el.blur();
7589         }
7590         return this;
7591     },
7592
7593     /**
7594      * Disable this component.
7595      * @return {Roo.Component} this
7596      */
7597     disable : function(){
7598         if(this.rendered){
7599             this.onDisable();
7600         }
7601         this.disabled = true;
7602         this.fireEvent("disable", this);
7603         return this;
7604     },
7605
7606         // private
7607     onDisable : function(){
7608         this.getActionEl().addClass(this.disabledClass);
7609         this.el.dom.disabled = true;
7610     },
7611
7612     /**
7613      * Enable this component.
7614      * @return {Roo.Component} this
7615      */
7616     enable : function(){
7617         if(this.rendered){
7618             this.onEnable();
7619         }
7620         this.disabled = false;
7621         this.fireEvent("enable", this);
7622         return this;
7623     },
7624
7625         // private
7626     onEnable : function(){
7627         this.getActionEl().removeClass(this.disabledClass);
7628         this.el.dom.disabled = false;
7629     },
7630
7631     /**
7632      * Convenience function for setting disabled/enabled by boolean.
7633      * @param {Boolean} disabled
7634      */
7635     setDisabled : function(disabled){
7636         this[disabled ? "disable" : "enable"]();
7637     },
7638
7639     /**
7640      * Show this component.
7641      * @return {Roo.Component} this
7642      */
7643     show: function(){
7644         if(this.fireEvent("beforeshow", this) !== false){
7645             this.hidden = false;
7646             if(this.rendered){
7647                 this.onShow();
7648             }
7649             this.fireEvent("show", this);
7650         }
7651         return this;
7652     },
7653
7654     // private
7655     onShow : function(){
7656         var ae = this.getActionEl();
7657         if(this.hideMode == 'visibility'){
7658             ae.dom.style.visibility = "visible";
7659         }else if(this.hideMode == 'offsets'){
7660             ae.removeClass('x-hidden');
7661         }else{
7662             ae.dom.style.display = "";
7663         }
7664     },
7665
7666     /**
7667      * Hide this component.
7668      * @return {Roo.Component} this
7669      */
7670     hide: function(){
7671         if(this.fireEvent("beforehide", this) !== false){
7672             this.hidden = true;
7673             if(this.rendered){
7674                 this.onHide();
7675             }
7676             this.fireEvent("hide", this);
7677         }
7678         return this;
7679     },
7680
7681     // private
7682     onHide : function(){
7683         var ae = this.getActionEl();
7684         if(this.hideMode == 'visibility'){
7685             ae.dom.style.visibility = "hidden";
7686         }else if(this.hideMode == 'offsets'){
7687             ae.addClass('x-hidden');
7688         }else{
7689             ae.dom.style.display = "none";
7690         }
7691     },
7692
7693     /**
7694      * Convenience function to hide or show this component by boolean.
7695      * @param {Boolean} visible True to show, false to hide
7696      * @return {Roo.Component} this
7697      */
7698     setVisible: function(visible){
7699         if(visible) {
7700             this.show();
7701         }else{
7702             this.hide();
7703         }
7704         return this;
7705     },
7706
7707     /**
7708      * Returns true if this component is visible.
7709      */
7710     isVisible : function(){
7711         return this.getActionEl().isVisible();
7712     },
7713
7714     cloneConfig : function(overrides){
7715         overrides = overrides || {};
7716         var id = overrides.id || Roo.id();
7717         var cfg = Roo.applyIf(overrides, this.initialConfig);
7718         cfg.id = id; // prevent dup id
7719         return new this.constructor(cfg);
7720     }
7721 });/*
7722  * Based on:
7723  * Ext JS Library 1.1.1
7724  * Copyright(c) 2006-2007, Ext JS, LLC.
7725  *
7726  * Originally Released Under LGPL - original licence link has changed is not relivant.
7727  *
7728  * Fork - LGPL
7729  * <script type="text/javascript">
7730  */
7731  (function(){ 
7732 /**
7733  * @class Roo.Layer
7734  * @extends Roo.Element
7735  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7736  * automatic maintaining of shadow/shim positions.
7737  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7738  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7739  * you can pass a string with a CSS class name. False turns off the shadow.
7740  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7741  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7742  * @cfg {String} cls CSS class to add to the element
7743  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7744  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7745  * @constructor
7746  * @param {Object} config An object with config options.
7747  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7748  */
7749
7750 Roo.Layer = function(config, existingEl){
7751     config = config || {};
7752     var dh = Roo.DomHelper;
7753     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7754     if(existingEl){
7755         this.dom = Roo.getDom(existingEl);
7756     }
7757     if(!this.dom){
7758         var o = config.dh || {tag: "div", cls: "x-layer"};
7759         this.dom = dh.append(pel, o);
7760     }
7761     if(config.cls){
7762         this.addClass(config.cls);
7763     }
7764     this.constrain = config.constrain !== false;
7765     this.visibilityMode = Roo.Element.VISIBILITY;
7766     if(config.id){
7767         this.id = this.dom.id = config.id;
7768     }else{
7769         this.id = Roo.id(this.dom);
7770     }
7771     this.zindex = config.zindex || this.getZIndex();
7772     this.position("absolute", this.zindex);
7773     if(config.shadow){
7774         this.shadowOffset = config.shadowOffset || 4;
7775         this.shadow = new Roo.Shadow({
7776             offset : this.shadowOffset,
7777             mode : config.shadow
7778         });
7779     }else{
7780         this.shadowOffset = 0;
7781     }
7782     this.useShim = config.shim !== false && Roo.useShims;
7783     this.useDisplay = config.useDisplay;
7784     this.hide();
7785 };
7786
7787 var supr = Roo.Element.prototype;
7788
7789 // shims are shared among layer to keep from having 100 iframes
7790 var shims = [];
7791
7792 Roo.extend(Roo.Layer, Roo.Element, {
7793
7794     getZIndex : function(){
7795         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7796     },
7797
7798     getShim : function(){
7799         if(!this.useShim){
7800             return null;
7801         }
7802         if(this.shim){
7803             return this.shim;
7804         }
7805         var shim = shims.shift();
7806         if(!shim){
7807             shim = this.createShim();
7808             shim.enableDisplayMode('block');
7809             shim.dom.style.display = 'none';
7810             shim.dom.style.visibility = 'visible';
7811         }
7812         var pn = this.dom.parentNode;
7813         if(shim.dom.parentNode != pn){
7814             pn.insertBefore(shim.dom, this.dom);
7815         }
7816         shim.setStyle('z-index', this.getZIndex()-2);
7817         this.shim = shim;
7818         return shim;
7819     },
7820
7821     hideShim : function(){
7822         if(this.shim){
7823             this.shim.setDisplayed(false);
7824             shims.push(this.shim);
7825             delete this.shim;
7826         }
7827     },
7828
7829     disableShadow : function(){
7830         if(this.shadow){
7831             this.shadowDisabled = true;
7832             this.shadow.hide();
7833             this.lastShadowOffset = this.shadowOffset;
7834             this.shadowOffset = 0;
7835         }
7836     },
7837
7838     enableShadow : function(show){
7839         if(this.shadow){
7840             this.shadowDisabled = false;
7841             this.shadowOffset = this.lastShadowOffset;
7842             delete this.lastShadowOffset;
7843             if(show){
7844                 this.sync(true);
7845             }
7846         }
7847     },
7848
7849     // private
7850     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7851     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7852     sync : function(doShow){
7853         var sw = this.shadow;
7854         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7855             var sh = this.getShim();
7856
7857             var w = this.getWidth(),
7858                 h = this.getHeight();
7859
7860             var l = this.getLeft(true),
7861                 t = this.getTop(true);
7862
7863             if(sw && !this.shadowDisabled){
7864                 if(doShow && !sw.isVisible()){
7865                     sw.show(this);
7866                 }else{
7867                     sw.realign(l, t, w, h);
7868                 }
7869                 if(sh){
7870                     if(doShow){
7871                        sh.show();
7872                     }
7873                     // fit the shim behind the shadow, so it is shimmed too
7874                     var a = sw.adjusts, s = sh.dom.style;
7875                     s.left = (Math.min(l, l+a.l))+"px";
7876                     s.top = (Math.min(t, t+a.t))+"px";
7877                     s.width = (w+a.w)+"px";
7878                     s.height = (h+a.h)+"px";
7879                 }
7880             }else if(sh){
7881                 if(doShow){
7882                    sh.show();
7883                 }
7884                 sh.setSize(w, h);
7885                 sh.setLeftTop(l, t);
7886             }
7887             
7888         }
7889     },
7890
7891     // private
7892     destroy : function(){
7893         this.hideShim();
7894         if(this.shadow){
7895             this.shadow.hide();
7896         }
7897         this.removeAllListeners();
7898         var pn = this.dom.parentNode;
7899         if(pn){
7900             pn.removeChild(this.dom);
7901         }
7902         Roo.Element.uncache(this.id);
7903     },
7904
7905     remove : function(){
7906         this.destroy();
7907     },
7908
7909     // private
7910     beginUpdate : function(){
7911         this.updating = true;
7912     },
7913
7914     // private
7915     endUpdate : function(){
7916         this.updating = false;
7917         this.sync(true);
7918     },
7919
7920     // private
7921     hideUnders : function(negOffset){
7922         if(this.shadow){
7923             this.shadow.hide();
7924         }
7925         this.hideShim();
7926     },
7927
7928     // private
7929     constrainXY : function(){
7930         if(this.constrain){
7931             var vw = Roo.lib.Dom.getViewWidth(),
7932                 vh = Roo.lib.Dom.getViewHeight();
7933             var s = Roo.get(document).getScroll();
7934
7935             var xy = this.getXY();
7936             var x = xy[0], y = xy[1];   
7937             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7938             // only move it if it needs it
7939             var moved = false;
7940             // first validate right/bottom
7941             if((x + w) > vw+s.left){
7942                 x = vw - w - this.shadowOffset;
7943                 moved = true;
7944             }
7945             if((y + h) > vh+s.top){
7946                 y = vh - h - this.shadowOffset;
7947                 moved = true;
7948             }
7949             // then make sure top/left isn't negative
7950             if(x < s.left){
7951                 x = s.left;
7952                 moved = true;
7953             }
7954             if(y < s.top){
7955                 y = s.top;
7956                 moved = true;
7957             }
7958             if(moved){
7959                 if(this.avoidY){
7960                     var ay = this.avoidY;
7961                     if(y <= ay && (y+h) >= ay){
7962                         y = ay-h-5;   
7963                     }
7964                 }
7965                 xy = [x, y];
7966                 this.storeXY(xy);
7967                 supr.setXY.call(this, xy);
7968                 this.sync();
7969             }
7970         }
7971     },
7972
7973     isVisible : function(){
7974         return this.visible;    
7975     },
7976
7977     // private
7978     showAction : function(){
7979         this.visible = true; // track visibility to prevent getStyle calls
7980         if(this.useDisplay === true){
7981             this.setDisplayed("");
7982         }else if(this.lastXY){
7983             supr.setXY.call(this, this.lastXY);
7984         }else if(this.lastLT){
7985             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7986         }
7987     },
7988
7989     // private
7990     hideAction : function(){
7991         this.visible = false;
7992         if(this.useDisplay === true){
7993             this.setDisplayed(false);
7994         }else{
7995             this.setLeftTop(-10000,-10000);
7996         }
7997     },
7998
7999     // overridden Element method
8000     setVisible : function(v, a, d, c, e){
8001         if(v){
8002             this.showAction();
8003         }
8004         if(a && v){
8005             var cb = function(){
8006                 this.sync(true);
8007                 if(c){
8008                     c();
8009                 }
8010             }.createDelegate(this);
8011             supr.setVisible.call(this, true, true, d, cb, e);
8012         }else{
8013             if(!v){
8014                 this.hideUnders(true);
8015             }
8016             var cb = c;
8017             if(a){
8018                 cb = function(){
8019                     this.hideAction();
8020                     if(c){
8021                         c();
8022                     }
8023                 }.createDelegate(this);
8024             }
8025             supr.setVisible.call(this, v, a, d, cb, e);
8026             if(v){
8027                 this.sync(true);
8028             }else if(!a){
8029                 this.hideAction();
8030             }
8031         }
8032     },
8033
8034     storeXY : function(xy){
8035         delete this.lastLT;
8036         this.lastXY = xy;
8037     },
8038
8039     storeLeftTop : function(left, top){
8040         delete this.lastXY;
8041         this.lastLT = [left, top];
8042     },
8043
8044     // private
8045     beforeFx : function(){
8046         this.beforeAction();
8047         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8048     },
8049
8050     // private
8051     afterFx : function(){
8052         Roo.Layer.superclass.afterFx.apply(this, arguments);
8053         this.sync(this.isVisible());
8054     },
8055
8056     // private
8057     beforeAction : function(){
8058         if(!this.updating && this.shadow){
8059             this.shadow.hide();
8060         }
8061     },
8062
8063     // overridden Element method
8064     setLeft : function(left){
8065         this.storeLeftTop(left, this.getTop(true));
8066         supr.setLeft.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setTop : function(top){
8071         this.storeLeftTop(this.getLeft(true), top);
8072         supr.setTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setLeftTop : function(left, top){
8077         this.storeLeftTop(left, top);
8078         supr.setLeftTop.apply(this, arguments);
8079         this.sync();
8080     },
8081
8082     setXY : function(xy, a, d, c, e){
8083         this.fixDisplay();
8084         this.beforeAction();
8085         this.storeXY(xy);
8086         var cb = this.createCB(c);
8087         supr.setXY.call(this, xy, a, d, cb, e);
8088         if(!a){
8089             cb();
8090         }
8091     },
8092
8093     // private
8094     createCB : function(c){
8095         var el = this;
8096         return function(){
8097             el.constrainXY();
8098             el.sync(true);
8099             if(c){
8100                 c();
8101             }
8102         };
8103     },
8104
8105     // overridden Element method
8106     setX : function(x, a, d, c, e){
8107         this.setXY([x, this.getY()], a, d, c, e);
8108     },
8109
8110     // overridden Element method
8111     setY : function(y, a, d, c, e){
8112         this.setXY([this.getX(), y], a, d, c, e);
8113     },
8114
8115     // overridden Element method
8116     setSize : function(w, h, a, d, c, e){
8117         this.beforeAction();
8118         var cb = this.createCB(c);
8119         supr.setSize.call(this, w, h, a, d, cb, e);
8120         if(!a){
8121             cb();
8122         }
8123     },
8124
8125     // overridden Element method
8126     setWidth : function(w, a, d, c, e){
8127         this.beforeAction();
8128         var cb = this.createCB(c);
8129         supr.setWidth.call(this, w, a, d, cb, e);
8130         if(!a){
8131             cb();
8132         }
8133     },
8134
8135     // overridden Element method
8136     setHeight : function(h, a, d, c, e){
8137         this.beforeAction();
8138         var cb = this.createCB(c);
8139         supr.setHeight.call(this, h, a, d, cb, e);
8140         if(!a){
8141             cb();
8142         }
8143     },
8144
8145     // overridden Element method
8146     setBounds : function(x, y, w, h, a, d, c, e){
8147         this.beforeAction();
8148         var cb = this.createCB(c);
8149         if(!a){
8150             this.storeXY([x, y]);
8151             supr.setXY.call(this, [x, y]);
8152             supr.setSize.call(this, w, h, a, d, cb, e);
8153             cb();
8154         }else{
8155             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8156         }
8157         return this;
8158     },
8159     
8160     /**
8161      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8162      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8163      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8164      * @param {Number} zindex The new z-index to set
8165      * @return {this} The Layer
8166      */
8167     setZIndex : function(zindex){
8168         this.zindex = zindex;
8169         this.setStyle("z-index", zindex + 2);
8170         if(this.shadow){
8171             this.shadow.setZIndex(zindex + 1);
8172         }
8173         if(this.shim){
8174             this.shim.setStyle("z-index", zindex);
8175         }
8176     }
8177 });
8178 })();/*
8179  * Based on:
8180  * Ext JS Library 1.1.1
8181  * Copyright(c) 2006-2007, Ext JS, LLC.
8182  *
8183  * Originally Released Under LGPL - original licence link has changed is not relivant.
8184  *
8185  * Fork - LGPL
8186  * <script type="text/javascript">
8187  */
8188
8189
8190 /**
8191  * @class Roo.Shadow
8192  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8193  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8194  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8195  * @constructor
8196  * Create a new Shadow
8197  * @param {Object} config The config object
8198  */
8199 Roo.Shadow = function(config){
8200     Roo.apply(this, config);
8201     if(typeof this.mode != "string"){
8202         this.mode = this.defaultMode;
8203     }
8204     var o = this.offset, a = {h: 0};
8205     var rad = Math.floor(this.offset/2);
8206     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8207         case "drop":
8208             a.w = 0;
8209             a.l = a.t = o;
8210             a.t -= 1;
8211             if(Roo.isIE){
8212                 a.l -= this.offset + rad;
8213                 a.t -= this.offset + rad;
8214                 a.w -= rad;
8215                 a.h -= rad;
8216                 a.t += 1;
8217             }
8218         break;
8219         case "sides":
8220             a.w = (o*2);
8221             a.l = -o;
8222             a.t = o-1;
8223             if(Roo.isIE){
8224                 a.l -= (this.offset - rad);
8225                 a.t -= this.offset + rad;
8226                 a.l += 1;
8227                 a.w -= (this.offset - rad)*2;
8228                 a.w -= rad + 1;
8229                 a.h -= 1;
8230             }
8231         break;
8232         case "frame":
8233             a.w = a.h = (o*2);
8234             a.l = a.t = -o;
8235             a.t += 1;
8236             a.h -= 2;
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 + 1);
8242                 a.h -= (this.offset + rad);
8243                 a.h += 1;
8244             }
8245         break;
8246     };
8247
8248     this.adjusts = a;
8249 };
8250
8251 Roo.Shadow.prototype = {
8252     /**
8253      * @cfg {String} mode
8254      * The shadow display mode.  Supports the following options:<br />
8255      * sides: Shadow displays on both sides and bottom only<br />
8256      * frame: Shadow displays equally on all four sides<br />
8257      * drop: Traditional bottom-right drop shadow (default)
8258      */
8259     /**
8260      * @cfg {String} offset
8261      * The number of pixels to offset the shadow from the element (defaults to 4)
8262      */
8263     offset: 4,
8264
8265     // private
8266     defaultMode: "drop",
8267
8268     /**
8269      * Displays the shadow under the target element
8270      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8271      */
8272     show : function(target){
8273         target = Roo.get(target);
8274         if(!this.el){
8275             this.el = Roo.Shadow.Pool.pull();
8276             if(this.el.dom.nextSibling != target.dom){
8277                 this.el.insertBefore(target);
8278             }
8279         }
8280         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8281         if(Roo.isIE){
8282             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8283         }
8284         this.realign(
8285             target.getLeft(true),
8286             target.getTop(true),
8287             target.getWidth(),
8288             target.getHeight()
8289         );
8290         this.el.dom.style.display = "block";
8291     },
8292
8293     /**
8294      * Returns true if the shadow is visible, else false
8295      */
8296     isVisible : function(){
8297         return this.el ? true : false;  
8298     },
8299
8300     /**
8301      * Direct alignment when values are already available. Show must be called at least once before
8302      * calling this method to ensure it is initialized.
8303      * @param {Number} left The target element left position
8304      * @param {Number} top The target element top position
8305      * @param {Number} width The target element width
8306      * @param {Number} height The target element height
8307      */
8308     realign : function(l, t, w, h){
8309         if(!this.el){
8310             return;
8311         }
8312         var a = this.adjusts, d = this.el.dom, s = d.style;
8313         var iea = 0;
8314         s.left = (l+a.l)+"px";
8315         s.top = (t+a.t)+"px";
8316         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8317  
8318         if(s.width != sws || s.height != shs){
8319             s.width = sws;
8320             s.height = shs;
8321             if(!Roo.isIE){
8322                 var cn = d.childNodes;
8323                 var sww = Math.max(0, (sw-12))+"px";
8324                 cn[0].childNodes[1].style.width = sww;
8325                 cn[1].childNodes[1].style.width = sww;
8326                 cn[2].childNodes[1].style.width = sww;
8327                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8328             }
8329         }
8330     },
8331
8332     /**
8333      * Hides this shadow
8334      */
8335     hide : function(){
8336         if(this.el){
8337             this.el.dom.style.display = "none";
8338             Roo.Shadow.Pool.push(this.el);
8339             delete this.el;
8340         }
8341     },
8342
8343     /**
8344      * Adjust the z-index of this shadow
8345      * @param {Number} zindex The new z-index
8346      */
8347     setZIndex : function(z){
8348         this.zIndex = z;
8349         if(this.el){
8350             this.el.setStyle("z-index", z);
8351         }
8352     }
8353 };
8354
8355 // Private utility class that manages the internal Shadow cache
8356 Roo.Shadow.Pool = function(){
8357     var p = [];
8358     var markup = Roo.isIE ?
8359                  '<div class="x-ie-shadow"></div>' :
8360                  '<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>';
8361     return {
8362         pull : function(){
8363             var sh = p.shift();
8364             if(!sh){
8365                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8366                 sh.autoBoxAdjust = false;
8367             }
8368             return sh;
8369         },
8370
8371         push : function(sh){
8372             p.push(sh);
8373         }
8374     };
8375 }();/*
8376  * Based on:
8377  * Ext JS Library 1.1.1
8378  * Copyright(c) 2006-2007, Ext JS, LLC.
8379  *
8380  * Originally Released Under LGPL - original licence link has changed is not relivant.
8381  *
8382  * Fork - LGPL
8383  * <script type="text/javascript">
8384  */
8385
8386 /**
8387  * @class Roo.BoxComponent
8388  * @extends Roo.Component
8389  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8390  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8391  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8392  * layout containers.
8393  * @constructor
8394  * @param {Roo.Element/String/Object} config The configuration options.
8395  */
8396 Roo.BoxComponent = function(config){
8397     Roo.Component.call(this, config);
8398     this.addEvents({
8399         /**
8400          * @event resize
8401          * Fires after the component is resized.
8402              * @param {Roo.Component} this
8403              * @param {Number} adjWidth The box-adjusted width that was set
8404              * @param {Number} adjHeight The box-adjusted height that was set
8405              * @param {Number} rawWidth The width that was originally specified
8406              * @param {Number} rawHeight The height that was originally specified
8407              */
8408         resize : true,
8409         /**
8410          * @event move
8411          * Fires after the component is moved.
8412              * @param {Roo.Component} this
8413              * @param {Number} x The new x position
8414              * @param {Number} y The new y position
8415              */
8416         move : true
8417     });
8418 };
8419
8420 Roo.extend(Roo.BoxComponent, Roo.Component, {
8421     // private, set in afterRender to signify that the component has been rendered
8422     boxReady : false,
8423     // private, used to defer height settings to subclasses
8424     deferHeight: false,
8425     /** @cfg {Number} width
8426      * width (optional) size of component
8427      */
8428      /** @cfg {Number} height
8429      * height (optional) size of component
8430      */
8431      
8432     /**
8433      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8434      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8435      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8436      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8437      * @return {Roo.BoxComponent} this
8438      */
8439     setSize : function(w, h){
8440         // support for standard size objects
8441         if(typeof w == 'object'){
8442             h = w.height;
8443             w = w.width;
8444         }
8445         // not rendered
8446         if(!this.boxReady){
8447             this.width = w;
8448             this.height = h;
8449             return this;
8450         }
8451
8452         // prevent recalcs when not needed
8453         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8454             return this;
8455         }
8456         this.lastSize = {width: w, height: h};
8457
8458         var adj = this.adjustSize(w, h);
8459         var aw = adj.width, ah = adj.height;
8460         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8461             var rz = this.getResizeEl();
8462             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8463                 rz.setSize(aw, ah);
8464             }else if(!this.deferHeight && ah !== undefined){
8465                 rz.setHeight(ah);
8466             }else if(aw !== undefined){
8467                 rz.setWidth(aw);
8468             }
8469             this.onResize(aw, ah, w, h);
8470             this.fireEvent('resize', this, aw, ah, w, h);
8471         }
8472         return this;
8473     },
8474
8475     /**
8476      * Gets the current size of the component's underlying element.
8477      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8478      */
8479     getSize : function(){
8480         return this.el.getSize();
8481     },
8482
8483     /**
8484      * Gets the current XY position of the component's underlying element.
8485      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8486      * @return {Array} The XY position of the element (e.g., [100, 200])
8487      */
8488     getPosition : function(local){
8489         if(local === true){
8490             return [this.el.getLeft(true), this.el.getTop(true)];
8491         }
8492         return this.xy || this.el.getXY();
8493     },
8494
8495     /**
8496      * Gets the current box measurements of the component's underlying element.
8497      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8498      * @returns {Object} box An object in the format {x, y, width, height}
8499      */
8500     getBox : function(local){
8501         var s = this.el.getSize();
8502         if(local){
8503             s.x = this.el.getLeft(true);
8504             s.y = this.el.getTop(true);
8505         }else{
8506             var xy = this.xy || this.el.getXY();
8507             s.x = xy[0];
8508             s.y = xy[1];
8509         }
8510         return s;
8511     },
8512
8513     /**
8514      * Sets the current box measurements of the component's underlying element.
8515      * @param {Object} box An object in the format {x, y, width, height}
8516      * @returns {Roo.BoxComponent} this
8517      */
8518     updateBox : function(box){
8519         this.setSize(box.width, box.height);
8520         this.setPagePosition(box.x, box.y);
8521         return this;
8522     },
8523
8524     // protected
8525     getResizeEl : function(){
8526         return this.resizeEl || this.el;
8527     },
8528
8529     // protected
8530     getPositionEl : function(){
8531         return this.positionEl || this.el;
8532     },
8533
8534     /**
8535      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8536      * This method fires the move event.
8537      * @param {Number} left The new left
8538      * @param {Number} top The new top
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPosition : function(x, y){
8542         this.x = x;
8543         this.y = y;
8544         if(!this.boxReady){
8545             return this;
8546         }
8547         var adj = this.adjustPosition(x, y);
8548         var ax = adj.x, ay = adj.y;
8549
8550         var el = this.getPositionEl();
8551         if(ax !== undefined || ay !== undefined){
8552             if(ax !== undefined && ay !== undefined){
8553                 el.setLeftTop(ax, ay);
8554             }else if(ax !== undefined){
8555                 el.setLeft(ax);
8556             }else if(ay !== undefined){
8557                 el.setTop(ay);
8558             }
8559             this.onPosition(ax, ay);
8560             this.fireEvent('move', this, ax, ay);
8561         }
8562         return this;
8563     },
8564
8565     /**
8566      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8567      * This method fires the move event.
8568      * @param {Number} x The new x position
8569      * @param {Number} y The new y position
8570      * @returns {Roo.BoxComponent} this
8571      */
8572     setPagePosition : function(x, y){
8573         this.pageX = x;
8574         this.pageY = y;
8575         if(!this.boxReady){
8576             return;
8577         }
8578         if(x === undefined || y === undefined){ // cannot translate undefined points
8579             return;
8580         }
8581         var p = this.el.translatePoints(x, y);
8582         this.setPosition(p.left, p.top);
8583         return this;
8584     },
8585
8586     // private
8587     onRender : function(ct, position){
8588         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8589         if(this.resizeEl){
8590             this.resizeEl = Roo.get(this.resizeEl);
8591         }
8592         if(this.positionEl){
8593             this.positionEl = Roo.get(this.positionEl);
8594         }
8595     },
8596
8597     // private
8598     afterRender : function(){
8599         Roo.BoxComponent.superclass.afterRender.call(this);
8600         this.boxReady = true;
8601         this.setSize(this.width, this.height);
8602         if(this.x || this.y){
8603             this.setPosition(this.x, this.y);
8604         }
8605         if(this.pageX || this.pageY){
8606             this.setPagePosition(this.pageX, this.pageY);
8607         }
8608     },
8609
8610     /**
8611      * Force the component's size to recalculate based on the underlying element's current height and width.
8612      * @returns {Roo.BoxComponent} this
8613      */
8614     syncSize : function(){
8615         delete this.lastSize;
8616         this.setSize(this.el.getWidth(), this.el.getHeight());
8617         return this;
8618     },
8619
8620     /**
8621      * Called after the component is resized, this method is empty by default but can be implemented by any
8622      * subclass that needs to perform custom logic after a resize occurs.
8623      * @param {Number} adjWidth The box-adjusted width that was set
8624      * @param {Number} adjHeight The box-adjusted height that was set
8625      * @param {Number} rawWidth The width that was originally specified
8626      * @param {Number} rawHeight The height that was originally specified
8627      */
8628     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8629
8630     },
8631
8632     /**
8633      * Called after the component is moved, this method is empty by default but can be implemented by any
8634      * subclass that needs to perform custom logic after a move occurs.
8635      * @param {Number} x The new x position
8636      * @param {Number} y The new y position
8637      */
8638     onPosition : function(x, y){
8639
8640     },
8641
8642     // private
8643     adjustSize : function(w, h){
8644         if(this.autoWidth){
8645             w = 'auto';
8646         }
8647         if(this.autoHeight){
8648             h = 'auto';
8649         }
8650         return {width : w, height: h};
8651     },
8652
8653     // private
8654     adjustPosition : function(x, y){
8655         return {x : x, y: y};
8656     }
8657 });/*
8658  * Based on:
8659  * Ext JS Library 1.1.1
8660  * Copyright(c) 2006-2007, Ext JS, LLC.
8661  *
8662  * Originally Released Under LGPL - original licence link has changed is not relivant.
8663  *
8664  * Fork - LGPL
8665  * <script type="text/javascript">
8666  */
8667
8668
8669 /**
8670  * @class Roo.SplitBar
8671  * @extends Roo.util.Observable
8672  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8673  * <br><br>
8674  * Usage:
8675  * <pre><code>
8676 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8677                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8678 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8679 split.minSize = 100;
8680 split.maxSize = 600;
8681 split.animate = true;
8682 split.on('moved', splitterMoved);
8683 </code></pre>
8684  * @constructor
8685  * Create a new SplitBar
8686  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8687  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8688  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8689  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8690                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8691                         position of the SplitBar).
8692  */
8693 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8694     
8695     /** @private */
8696     this.el = Roo.get(dragElement, true);
8697     this.el.dom.unselectable = "on";
8698     /** @private */
8699     this.resizingEl = Roo.get(resizingElement, true);
8700
8701     /**
8702      * @private
8703      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8704      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8705      * @type Number
8706      */
8707     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8708     
8709     /**
8710      * The minimum size of the resizing element. (Defaults to 0)
8711      * @type Number
8712      */
8713     this.minSize = 0;
8714     
8715     /**
8716      * The maximum size of the resizing element. (Defaults to 2000)
8717      * @type Number
8718      */
8719     this.maxSize = 2000;
8720     
8721     /**
8722      * Whether to animate the transition to the new size
8723      * @type Boolean
8724      */
8725     this.animate = false;
8726     
8727     /**
8728      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8729      * @type Boolean
8730      */
8731     this.useShim = false;
8732     
8733     /** @private */
8734     this.shim = null;
8735     
8736     if(!existingProxy){
8737         /** @private */
8738         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8739     }else{
8740         this.proxy = Roo.get(existingProxy).dom;
8741     }
8742     /** @private */
8743     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8744     
8745     /** @private */
8746     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8747     
8748     /** @private */
8749     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8750     
8751     /** @private */
8752     this.dragSpecs = {};
8753     
8754     /**
8755      * @private The adapter to use to positon and resize elements
8756      */
8757     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8758     this.adapter.init(this);
8759     
8760     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8761         /** @private */
8762         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8763         this.el.addClass("x-splitbar-h");
8764     }else{
8765         /** @private */
8766         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8767         this.el.addClass("x-splitbar-v");
8768     }
8769     
8770     this.addEvents({
8771         /**
8772          * @event resize
8773          * Fires when the splitter is moved (alias for {@link #event-moved})
8774          * @param {Roo.SplitBar} this
8775          * @param {Number} newSize the new width or height
8776          */
8777         "resize" : true,
8778         /**
8779          * @event moved
8780          * Fires when the splitter is moved
8781          * @param {Roo.SplitBar} this
8782          * @param {Number} newSize the new width or height
8783          */
8784         "moved" : true,
8785         /**
8786          * @event beforeresize
8787          * Fires before the splitter is dragged
8788          * @param {Roo.SplitBar} this
8789          */
8790         "beforeresize" : true,
8791
8792         "beforeapply" : true
8793     });
8794
8795     Roo.util.Observable.call(this);
8796 };
8797
8798 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8799     onStartProxyDrag : function(x, y){
8800         this.fireEvent("beforeresize", this);
8801         if(!this.overlay){
8802             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8803             o.unselectable();
8804             o.enableDisplayMode("block");
8805             // all splitbars share the same overlay
8806             Roo.SplitBar.prototype.overlay = o;
8807         }
8808         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8809         this.overlay.show();
8810         Roo.get(this.proxy).setDisplayed("block");
8811         var size = this.adapter.getElementSize(this);
8812         this.activeMinSize = this.getMinimumSize();;
8813         this.activeMaxSize = this.getMaximumSize();;
8814         var c1 = size - this.activeMinSize;
8815         var c2 = Math.max(this.activeMaxSize - size, 0);
8816         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8817             this.dd.resetConstraints();
8818             this.dd.setXConstraint(
8819                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8821             );
8822             this.dd.setYConstraint(0, 0);
8823         }else{
8824             this.dd.resetConstraints();
8825             this.dd.setXConstraint(0, 0);
8826             this.dd.setYConstraint(
8827                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8828                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8829             );
8830          }
8831         this.dragSpecs.startSize = size;
8832         this.dragSpecs.startPoint = [x, y];
8833         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8834     },
8835     
8836     /** 
8837      * @private Called after the drag operation by the DDProxy
8838      */
8839     onEndProxyDrag : function(e){
8840         Roo.get(this.proxy).setDisplayed(false);
8841         var endPoint = Roo.lib.Event.getXY(e);
8842         if(this.overlay){
8843             this.overlay.hide();
8844         }
8845         var newSize;
8846         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.LEFT ?
8849                     endPoint[0] - this.dragSpecs.startPoint[0] :
8850                     this.dragSpecs.startPoint[0] - endPoint[0]
8851                 );
8852         }else{
8853             newSize = this.dragSpecs.startSize + 
8854                 (this.placement == Roo.SplitBar.TOP ?
8855                     endPoint[1] - this.dragSpecs.startPoint[1] :
8856                     this.dragSpecs.startPoint[1] - endPoint[1]
8857                 );
8858         }
8859         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8860         if(newSize != this.dragSpecs.startSize){
8861             if(this.fireEvent('beforeapply', this, newSize) !== false){
8862                 this.adapter.setElementSize(this, newSize);
8863                 this.fireEvent("moved", this, newSize);
8864                 this.fireEvent("resize", this, newSize);
8865             }
8866         }
8867     },
8868     
8869     /**
8870      * Get the adapter this SplitBar uses
8871      * @return The adapter object
8872      */
8873     getAdapter : function(){
8874         return this.adapter;
8875     },
8876     
8877     /**
8878      * Set the adapter this SplitBar uses
8879      * @param {Object} adapter A SplitBar adapter object
8880      */
8881     setAdapter : function(adapter){
8882         this.adapter = adapter;
8883         this.adapter.init(this);
8884     },
8885     
8886     /**
8887      * Gets the minimum size for the resizing element
8888      * @return {Number} The minimum size
8889      */
8890     getMinimumSize : function(){
8891         return this.minSize;
8892     },
8893     
8894     /**
8895      * Sets the minimum size for the resizing element
8896      * @param {Number} minSize The minimum size
8897      */
8898     setMinimumSize : function(minSize){
8899         this.minSize = minSize;
8900     },
8901     
8902     /**
8903      * Gets the maximum size for the resizing element
8904      * @return {Number} The maximum size
8905      */
8906     getMaximumSize : function(){
8907         return this.maxSize;
8908     },
8909     
8910     /**
8911      * Sets the maximum size for the resizing element
8912      * @param {Number} maxSize The maximum size
8913      */
8914     setMaximumSize : function(maxSize){
8915         this.maxSize = maxSize;
8916     },
8917     
8918     /**
8919      * Sets the initialize size for the resizing element
8920      * @param {Number} size The initial size
8921      */
8922     setCurrentSize : function(size){
8923         var oldAnimate = this.animate;
8924         this.animate = false;
8925         this.adapter.setElementSize(this, size);
8926         this.animate = oldAnimate;
8927     },
8928     
8929     /**
8930      * Destroy this splitbar. 
8931      * @param {Boolean} removeEl True to remove the element
8932      */
8933     destroy : function(removeEl){
8934         if(this.shim){
8935             this.shim.remove();
8936         }
8937         this.dd.unreg();
8938         this.proxy.parentNode.removeChild(this.proxy);
8939         if(removeEl){
8940             this.el.remove();
8941         }
8942     }
8943 });
8944
8945 /**
8946  * @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.
8947  */
8948 Roo.SplitBar.createProxy = function(dir){
8949     var proxy = new Roo.Element(document.createElement("div"));
8950     proxy.unselectable();
8951     var cls = 'x-splitbar-proxy';
8952     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8953     document.body.appendChild(proxy.dom);
8954     return proxy.dom;
8955 };
8956
8957 /** 
8958  * @class Roo.SplitBar.BasicLayoutAdapter
8959  * Default Adapter. It assumes the splitter and resizing element are not positioned
8960  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8961  */
8962 Roo.SplitBar.BasicLayoutAdapter = function(){
8963 };
8964
8965 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8966     // do nothing for now
8967     init : function(s){
8968     
8969     },
8970     /**
8971      * Called before drag operations to get the current size of the resizing element. 
8972      * @param {Roo.SplitBar} s The SplitBar using this adapter
8973      */
8974      getElementSize : function(s){
8975         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8976             return s.resizingEl.getWidth();
8977         }else{
8978             return s.resizingEl.getHeight();
8979         }
8980     },
8981     
8982     /**
8983      * Called after drag operations to set the size of the resizing element.
8984      * @param {Roo.SplitBar} s The SplitBar using this adapter
8985      * @param {Number} newSize The new size to set
8986      * @param {Function} onComplete A function to be invoked when resizing is complete
8987      */
8988     setElementSize : function(s, newSize, onComplete){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             if(!s.animate){
8991                 s.resizingEl.setWidth(newSize);
8992                 if(onComplete){
8993                     onComplete(s, newSize);
8994                 }
8995             }else{
8996                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8997             }
8998         }else{
8999             
9000             if(!s.animate){
9001                 s.resizingEl.setHeight(newSize);
9002                 if(onComplete){
9003                     onComplete(s, newSize);
9004                 }
9005             }else{
9006                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9007             }
9008         }
9009     }
9010 };
9011
9012 /** 
9013  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9014  * @extends Roo.SplitBar.BasicLayoutAdapter
9015  * Adapter that  moves the splitter element to align with the resized sizing element. 
9016  * Used with an absolute positioned SplitBar.
9017  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9018  * document.body, make sure you assign an id to the body element.
9019  */
9020 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9021     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9022     this.container = Roo.get(container);
9023 };
9024
9025 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9026     init : function(s){
9027         this.basic.init(s);
9028     },
9029     
9030     getElementSize : function(s){
9031         return this.basic.getElementSize(s);
9032     },
9033     
9034     setElementSize : function(s, newSize, onComplete){
9035         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9036     },
9037     
9038     moveSplitter : function(s){
9039         var yes = Roo.SplitBar;
9040         switch(s.placement){
9041             case yes.LEFT:
9042                 s.el.setX(s.resizingEl.getRight());
9043                 break;
9044             case yes.RIGHT:
9045                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9046                 break;
9047             case yes.TOP:
9048                 s.el.setY(s.resizingEl.getBottom());
9049                 break;
9050             case yes.BOTTOM:
9051                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9052                 break;
9053         }
9054     }
9055 };
9056
9057 /**
9058  * Orientation constant - Create a vertical SplitBar
9059  * @static
9060  * @type Number
9061  */
9062 Roo.SplitBar.VERTICAL = 1;
9063
9064 /**
9065  * Orientation constant - Create a horizontal SplitBar
9066  * @static
9067  * @type Number
9068  */
9069 Roo.SplitBar.HORIZONTAL = 2;
9070
9071 /**
9072  * Placement constant - The resizing element is to the left of the splitter element
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.LEFT = 1;
9077
9078 /**
9079  * Placement constant - The resizing element is to the right of the splitter element
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.RIGHT = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is positioned above the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.TOP = 3;
9091
9092 /**
9093  * Placement constant - The resizing element is positioned under splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.BOTTOM = 4;
9098 /*
9099  * Based on:
9100  * Ext JS Library 1.1.1
9101  * Copyright(c) 2006-2007, Ext JS, LLC.
9102  *
9103  * Originally Released Under LGPL - original licence link has changed is not relivant.
9104  *
9105  * Fork - LGPL
9106  * <script type="text/javascript">
9107  */
9108
9109 /**
9110  * @class Roo.View
9111  * @extends Roo.util.Observable
9112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9113  * This class also supports single and multi selection modes. <br>
9114  * Create a data model bound view:
9115  <pre><code>
9116  var store = new Roo.data.Store(...);
9117
9118  var view = new Roo.View({
9119     el : "my-element",
9120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9121  
9122     singleSelect: true,
9123     selectedClass: "ydataview-selected",
9124     store: store
9125  });
9126
9127  // listen for node click?
9128  view.on("click", function(vw, index, node, e){
9129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9130  });
9131
9132  // load XML data
9133  dataModel.load("foobar.xml");
9134  </code></pre>
9135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9136  * <br><br>
9137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9139  * 
9140  * Note: old style constructor is still suported (container, template, config)
9141  * 
9142  * @constructor
9143  * Create a new View
9144  * @param {Object} config The config object
9145  * 
9146  */
9147 Roo.View = function(config, depreciated_tpl, depreciated_config){
9148     
9149     if (typeof(depreciated_tpl) == 'undefined') {
9150         // new way.. - universal constructor.
9151         Roo.apply(this, config);
9152         this.el  = Roo.get(this.el);
9153     } else {
9154         // old format..
9155         this.el  = Roo.get(config);
9156         this.tpl = depreciated_tpl;
9157         Roo.apply(this, depreciated_config);
9158     }
9159      
9160     
9161     if(typeof(this.tpl) == "string"){
9162         this.tpl = new Roo.Template(this.tpl);
9163     } else {
9164         // support xtype ctors..
9165         this.tpl = new Roo.factory(this.tpl, Roo);
9166     }
9167     
9168     
9169     this.tpl.compile();
9170    
9171
9172      
9173     /** @private */
9174     this.addEvents({
9175         /**
9176          * @event beforeclick
9177          * Fires before a click is processed. Returns false to cancel the default action.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "beforeclick" : true,
9184         /**
9185          * @event click
9186          * Fires when a template node is clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "click" : true,
9193         /**
9194          * @event dblclick
9195          * Fires when a template node is double clicked.
9196          * @param {Roo.View} this
9197          * @param {Number} index The index of the target node
9198          * @param {HTMLElement} node The target node
9199          * @param {Roo.EventObject} e The raw event object
9200          */
9201             "dblclick" : true,
9202         /**
9203          * @event contextmenu
9204          * Fires when a template node is right clicked.
9205          * @param {Roo.View} this
9206          * @param {Number} index The index of the target node
9207          * @param {HTMLElement} node The target node
9208          * @param {Roo.EventObject} e The raw event object
9209          */
9210             "contextmenu" : true,
9211         /**
9212          * @event selectionchange
9213          * Fires when the selected nodes change.
9214          * @param {Roo.View} this
9215          * @param {Array} selections Array of the selected nodes
9216          */
9217             "selectionchange" : true,
9218     
9219         /**
9220          * @event beforeselect
9221          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9222          * @param {Roo.View} this
9223          * @param {HTMLElement} node The node to be selected
9224          * @param {Array} selections Array of currently selected nodes
9225          */
9226             "beforeselect" : true,
9227         /**
9228          * @event preparedata
9229          * Fires on every row to render, to allow you to change the data.
9230          * @param {Roo.View} this
9231          * @param {Object} data to be rendered (change this)
9232          */
9233           "preparedata" : true
9234         });
9235
9236     this.el.on({
9237         "click": this.onClick,
9238         "dblclick": this.onDblClick,
9239         "contextmenu": this.onContextMenu,
9240         scope:this
9241     });
9242
9243     this.selections = [];
9244     this.nodes = [];
9245     this.cmp = new Roo.CompositeElementLite([]);
9246     if(this.store){
9247         this.store = Roo.factory(this.store, Roo.data);
9248         this.setStore(this.store, true);
9249     }
9250     Roo.View.superclass.constructor.call(this);
9251 };
9252
9253 Roo.extend(Roo.View, Roo.util.Observable, {
9254     
9255      /**
9256      * @cfg {Roo.data.Store} store Data store to load data from.
9257      */
9258     store : false,
9259     
9260     /**
9261      * @cfg {String|Roo.Element} el The container element.
9262      */
9263     el : '',
9264     
9265     /**
9266      * @cfg {String|Roo.Template} tpl The template used by this View 
9267      */
9268     tpl : false,
9269     /**
9270      * @cfg {String} dataName the named area of the template to use as the data area
9271      *                          Works with domtemplates roo-name="name"
9272      */
9273     dataName: false,
9274     /**
9275      * @cfg {String} selectedClass The css class to add to selected nodes
9276      */
9277     selectedClass : "x-view-selected",
9278      /**
9279      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9280      */
9281     emptyText : "",
9282     /**
9283      * @cfg {Boolean} multiSelect Allow multiple selection
9284      */
9285     multiSelect : false,
9286     /**
9287      * @cfg {Boolean} singleSelect Allow single selection
9288      */
9289     singleSelect:  false,
9290     
9291     /**
9292      * @cfg {Boolean} toggleSelect - selecting 
9293      */
9294     toggleSelect : false,
9295     
9296     /**
9297      * Returns the element this view is bound to.
9298      * @return {Roo.Element}
9299      */
9300     getEl : function(){
9301         return this.el;
9302     },
9303
9304     /**
9305      * Refreshes the view.
9306      */
9307     refresh : function(){
9308         var t = this.tpl;
9309         
9310         // if we are using something like 'domtemplate', then
9311         // the what gets used is:
9312         // t.applySubtemplate(NAME, data, wrapping data..)
9313         // the outer template then get' applied with
9314         //     the store 'extra data'
9315         // and the body get's added to the
9316         //      roo-name="data" node?
9317         //      <span class='roo-tpl-{name}'></span> ?????
9318         
9319         
9320         
9321         this.clearSelections();
9322         this.el.update("");
9323         var html = [];
9324         var records = this.store.getRange();
9325         if(records.length < 1) {
9326             
9327             // is this valid??  = should it render a template??
9328             
9329             this.el.update(this.emptyText);
9330             return;
9331         }
9332         var el = this.el;
9333         if (this.dataName) {
9334             this.el.update(t.apply(this.store.meta)); //????
9335             el = this.el.child('.roo-tpl-' + this.dataName);
9336         }
9337         
9338         for(var i = 0, len = records.length; i < len; i++){
9339             var data = this.prepareData(records[i].data, i, records[i]);
9340             this.fireEvent("preparedata", this, data, i, records[i]);
9341             html[html.length] = Roo.util.Format.trim(
9342                 this.dataName ?
9343                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9344                     t.apply(data)
9345             );
9346         }
9347         
9348         
9349         
9350         el.update(html.join(""));
9351         this.nodes = el.dom.childNodes;
9352         this.updateIndexes(0);
9353     },
9354
9355     /**
9356      * Function to override to reformat the data that is sent to
9357      * the template for each node.
9358      * DEPRICATED - use the preparedata event handler.
9359      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9360      * a JSON object for an UpdateManager bound view).
9361      */
9362     prepareData : function(data, index, record)
9363     {
9364         this.fireEvent("preparedata", this, data, index, record);
9365         return data;
9366     },
9367
9368     onUpdate : function(ds, record){
9369         this.clearSelections();
9370         var index = this.store.indexOf(record);
9371         var n = this.nodes[index];
9372         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9373         n.parentNode.removeChild(n);
9374         this.updateIndexes(index, index);
9375     },
9376
9377     
9378     
9379 // --------- FIXME     
9380     onAdd : function(ds, records, index)
9381     {
9382         this.clearSelections();
9383         if(this.nodes.length == 0){
9384             this.refresh();
9385             return;
9386         }
9387         var n = this.nodes[index];
9388         for(var i = 0, len = records.length; i < len; i++){
9389             var d = this.prepareData(records[i].data, i, records[i]);
9390             if(n){
9391                 this.tpl.insertBefore(n, d);
9392             }else{
9393                 
9394                 this.tpl.append(this.el, d);
9395             }
9396         }
9397         this.updateIndexes(index);
9398     },
9399
9400     onRemove : function(ds, record, index){
9401         this.clearSelections();
9402         var el = this.dataName  ?
9403             this.el.child('.roo-tpl-' + this.dataName) :
9404             this.el; 
9405         el.dom.removeChild(this.nodes[index]);
9406         this.updateIndexes(index);
9407     },
9408
9409     /**
9410      * Refresh an individual node.
9411      * @param {Number} index
9412      */
9413     refreshNode : function(index){
9414         this.onUpdate(this.store, this.store.getAt(index));
9415     },
9416
9417     updateIndexes : function(startIndex, endIndex){
9418         var ns = this.nodes;
9419         startIndex = startIndex || 0;
9420         endIndex = endIndex || ns.length - 1;
9421         for(var i = startIndex; i <= endIndex; i++){
9422             ns[i].nodeIndex = i;
9423         }
9424     },
9425
9426     /**
9427      * Changes the data store this view uses and refresh the view.
9428      * @param {Store} store
9429      */
9430     setStore : function(store, initial){
9431         if(!initial && this.store){
9432             this.store.un("datachanged", this.refresh);
9433             this.store.un("add", this.onAdd);
9434             this.store.un("remove", this.onRemove);
9435             this.store.un("update", this.onUpdate);
9436             this.store.un("clear", this.refresh);
9437         }
9438         if(store){
9439           
9440             store.on("datachanged", this.refresh, this);
9441             store.on("add", this.onAdd, this);
9442             store.on("remove", this.onRemove, this);
9443             store.on("update", this.onUpdate, this);
9444             store.on("clear", this.refresh, this);
9445         }
9446         
9447         if(store){
9448             this.refresh();
9449         }
9450     },
9451
9452     /**
9453      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9454      * @param {HTMLElement} node
9455      * @return {HTMLElement} The template node
9456      */
9457     findItemFromChild : function(node){
9458         var el = this.dataName  ?
9459             this.el.child('.roo-tpl-' + this.dataName,true) :
9460             this.el.dom; 
9461         
9462         if(!node || node.parentNode == el){
9463                     return node;
9464             }
9465             var p = node.parentNode;
9466             while(p && p != el){
9467             if(p.parentNode == el){
9468                 return p;
9469             }
9470             p = p.parentNode;
9471         }
9472             return null;
9473     },
9474
9475     /** @ignore */
9476     onClick : function(e){
9477         var item = this.findItemFromChild(e.getTarget());
9478         if(item){
9479             var index = this.indexOf(item);
9480             if(this.onItemClick(item, index, e) !== false){
9481                 this.fireEvent("click", this, index, item, e);
9482             }
9483         }else{
9484             this.clearSelections();
9485         }
9486     },
9487
9488     /** @ignore */
9489     onContextMenu : function(e){
9490         var item = this.findItemFromChild(e.getTarget());
9491         if(item){
9492             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9493         }
9494     },
9495
9496     /** @ignore */
9497     onDblClick : function(e){
9498         var item = this.findItemFromChild(e.getTarget());
9499         if(item){
9500             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9501         }
9502     },
9503
9504     onItemClick : function(item, index, e)
9505     {
9506         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9507             return false;
9508         }
9509         if (this.toggleSelect) {
9510             var m = this.isSelected(item) ? 'unselect' : 'select';
9511             Roo.log(m);
9512             var _t = this;
9513             _t[m](item, true, false);
9514             return true;
9515         }
9516         if(this.multiSelect || this.singleSelect){
9517             if(this.multiSelect && e.shiftKey && this.lastSelection){
9518                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9519             }else{
9520                 this.select(item, this.multiSelect && e.ctrlKey);
9521                 this.lastSelection = item;
9522             }
9523             e.preventDefault();
9524         }
9525         return true;
9526     },
9527
9528     /**
9529      * Get the number of selected nodes.
9530      * @return {Number}
9531      */
9532     getSelectionCount : function(){
9533         return this.selections.length;
9534     },
9535
9536     /**
9537      * Get the currently selected nodes.
9538      * @return {Array} An array of HTMLElements
9539      */
9540     getSelectedNodes : function(){
9541         return this.selections;
9542     },
9543
9544     /**
9545      * Get the indexes of the selected nodes.
9546      * @return {Array}
9547      */
9548     getSelectedIndexes : function(){
9549         var indexes = [], s = this.selections;
9550         for(var i = 0, len = s.length; i < len; i++){
9551             indexes.push(s[i].nodeIndex);
9552         }
9553         return indexes;
9554     },
9555
9556     /**
9557      * Clear all selections
9558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9559      */
9560     clearSelections : function(suppressEvent){
9561         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9562             this.cmp.elements = this.selections;
9563             this.cmp.removeClass(this.selectedClass);
9564             this.selections = [];
9565             if(!suppressEvent){
9566                 this.fireEvent("selectionchange", this, this.selections);
9567             }
9568         }
9569     },
9570
9571     /**
9572      * Returns true if the passed node is selected
9573      * @param {HTMLElement/Number} node The node or node index
9574      * @return {Boolean}
9575      */
9576     isSelected : function(node){
9577         var s = this.selections;
9578         if(s.length < 1){
9579             return false;
9580         }
9581         node = this.getNode(node);
9582         return s.indexOf(node) !== -1;
9583     },
9584
9585     /**
9586      * Selects nodes.
9587      * @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
9588      * @param {Boolean} keepExisting (optional) true to keep existing selections
9589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9590      */
9591     select : function(nodeInfo, keepExisting, suppressEvent){
9592         if(nodeInfo instanceof Array){
9593             if(!keepExisting){
9594                 this.clearSelections(true);
9595             }
9596             for(var i = 0, len = nodeInfo.length; i < len; i++){
9597                 this.select(nodeInfo[i], true, true);
9598             }
9599             return;
9600         } 
9601         var node = this.getNode(nodeInfo);
9602         if(!node || this.isSelected(node)){
9603             return; // already selected.
9604         }
9605         if(!keepExisting){
9606             this.clearSelections(true);
9607         }
9608         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9609             Roo.fly(node).addClass(this.selectedClass);
9610             this.selections.push(node);
9611             if(!suppressEvent){
9612                 this.fireEvent("selectionchange", this, this.selections);
9613             }
9614         }
9615         
9616         
9617     },
9618       /**
9619      * Unselects nodes.
9620      * @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
9621      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623      */
9624     unselect : function(nodeInfo, keepExisting, suppressEvent)
9625     {
9626         if(nodeInfo instanceof Array){
9627             Roo.each(this.selections, function(s) {
9628                 this.unselect(s, nodeInfo);
9629             }, this);
9630             return;
9631         }
9632         var node = this.getNode(nodeInfo);
9633         if(!node || !this.isSelected(node)){
9634             Roo.log("not selected");
9635             return; // not selected.
9636         }
9637         // fireevent???
9638         var ns = [];
9639         Roo.each(this.selections, function(s) {
9640             if (s == node ) {
9641                 Roo.fly(node).removeClass(this.selectedClass);
9642
9643                 return;
9644             }
9645             ns.push(s);
9646         },this);
9647         
9648         this.selections= ns;
9649         this.fireEvent("selectionchange", this, this.selections);
9650     },
9651
9652     /**
9653      * Gets a template node.
9654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9655      * @return {HTMLElement} The node or null if it wasn't found
9656      */
9657     getNode : function(nodeInfo){
9658         if(typeof nodeInfo == "string"){
9659             return document.getElementById(nodeInfo);
9660         }else if(typeof nodeInfo == "number"){
9661             return this.nodes[nodeInfo];
9662         }
9663         return nodeInfo;
9664     },
9665
9666     /**
9667      * Gets a range template nodes.
9668      * @param {Number} startIndex
9669      * @param {Number} endIndex
9670      * @return {Array} An array of nodes
9671      */
9672     getNodes : function(start, end){
9673         var ns = this.nodes;
9674         start = start || 0;
9675         end = typeof end == "undefined" ? ns.length - 1 : end;
9676         var nodes = [];
9677         if(start <= end){
9678             for(var i = start; i <= end; i++){
9679                 nodes.push(ns[i]);
9680             }
9681         } else{
9682             for(var i = start; i >= end; i--){
9683                 nodes.push(ns[i]);
9684             }
9685         }
9686         return nodes;
9687     },
9688
9689     /**
9690      * Finds the index of the passed node
9691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9692      * @return {Number} The index of the node or -1
9693      */
9694     indexOf : function(node){
9695         node = this.getNode(node);
9696         if(typeof node.nodeIndex == "number"){
9697             return node.nodeIndex;
9698         }
9699         var ns = this.nodes;
9700         for(var i = 0, len = ns.length; i < len; i++){
9701             if(ns[i] == node){
9702                 return i;
9703             }
9704         }
9705         return -1;
9706     }
9707 });
9708 /*
9709  * Based on:
9710  * Ext JS Library 1.1.1
9711  * Copyright(c) 2006-2007, Ext JS, LLC.
9712  *
9713  * Originally Released Under LGPL - original licence link has changed is not relivant.
9714  *
9715  * Fork - LGPL
9716  * <script type="text/javascript">
9717  */
9718
9719 /**
9720  * @class Roo.JsonView
9721  * @extends Roo.View
9722  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9723 <pre><code>
9724 var view = new Roo.JsonView({
9725     container: "my-element",
9726     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9727     multiSelect: true, 
9728     jsonRoot: "data" 
9729 });
9730
9731 // listen for node click?
9732 view.on("click", function(vw, index, node, e){
9733     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9734 });
9735
9736 // direct load of JSON data
9737 view.load("foobar.php");
9738
9739 // Example from my blog list
9740 var tpl = new Roo.Template(
9741     '&lt;div class="entry"&gt;' +
9742     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9743     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9744     "&lt;/div&gt;&lt;hr /&gt;"
9745 );
9746
9747 var moreView = new Roo.JsonView({
9748     container :  "entry-list", 
9749     template : tpl,
9750     jsonRoot: "posts"
9751 });
9752 moreView.on("beforerender", this.sortEntries, this);
9753 moreView.load({
9754     url: "/blog/get-posts.php",
9755     params: "allposts=true",
9756     text: "Loading Blog Entries..."
9757 });
9758 </code></pre>
9759
9760 * Note: old code is supported with arguments : (container, template, config)
9761
9762
9763  * @constructor
9764  * Create a new JsonView
9765  * 
9766  * @param {Object} config The config object
9767  * 
9768  */
9769 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9770     
9771     
9772     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9773
9774     var um = this.el.getUpdateManager();
9775     um.setRenderer(this);
9776     um.on("update", this.onLoad, this);
9777     um.on("failure", this.onLoadException, this);
9778
9779     /**
9780      * @event beforerender
9781      * Fires before rendering of the downloaded JSON data.
9782      * @param {Roo.JsonView} this
9783      * @param {Object} data The JSON data loaded
9784      */
9785     /**
9786      * @event load
9787      * Fires when data is loaded.
9788      * @param {Roo.JsonView} this
9789      * @param {Object} data The JSON data loaded
9790      * @param {Object} response The raw Connect response object
9791      */
9792     /**
9793      * @event loadexception
9794      * Fires when loading fails.
9795      * @param {Roo.JsonView} this
9796      * @param {Object} response The raw Connect response object
9797      */
9798     this.addEvents({
9799         'beforerender' : true,
9800         'load' : true,
9801         'loadexception' : true
9802     });
9803 };
9804 Roo.extend(Roo.JsonView, Roo.View, {
9805     /**
9806      * @type {String} The root property in the loaded JSON object that contains the data
9807      */
9808     jsonRoot : "",
9809
9810     /**
9811      * Refreshes the view.
9812      */
9813     refresh : function(){
9814         this.clearSelections();
9815         this.el.update("");
9816         var html = [];
9817         var o = this.jsonData;
9818         if(o && o.length > 0){
9819             for(var i = 0, len = o.length; i < len; i++){
9820                 var data = this.prepareData(o[i], i, o);
9821                 html[html.length] = this.tpl.apply(data);
9822             }
9823         }else{
9824             html.push(this.emptyText);
9825         }
9826         this.el.update(html.join(""));
9827         this.nodes = this.el.dom.childNodes;
9828         this.updateIndexes(0);
9829     },
9830
9831     /**
9832      * 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.
9833      * @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:
9834      <pre><code>
9835      view.load({
9836          url: "your-url.php",
9837          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9838          callback: yourFunction,
9839          scope: yourObject, //(optional scope)
9840          discardUrl: false,
9841          nocache: false,
9842          text: "Loading...",
9843          timeout: 30,
9844          scripts: false
9845      });
9846      </code></pre>
9847      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9848      * 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.
9849      * @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}
9850      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9851      * @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.
9852      */
9853     load : function(){
9854         var um = this.el.getUpdateManager();
9855         um.update.apply(um, arguments);
9856     },
9857
9858     render : function(el, response){
9859         this.clearSelections();
9860         this.el.update("");
9861         var o;
9862         try{
9863             o = Roo.util.JSON.decode(response.responseText);
9864             if(this.jsonRoot){
9865                 
9866                 o = o[this.jsonRoot];
9867             }
9868         } catch(e){
9869         }
9870         /**
9871          * The current JSON data or null
9872          */
9873         this.jsonData = o;
9874         this.beforeRender();
9875         this.refresh();
9876     },
9877
9878 /**
9879  * Get the number of records in the current JSON dataset
9880  * @return {Number}
9881  */
9882     getCount : function(){
9883         return this.jsonData ? this.jsonData.length : 0;
9884     },
9885
9886 /**
9887  * Returns the JSON object for the specified node(s)
9888  * @param {HTMLElement/Array} node The node or an array of nodes
9889  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9890  * you get the JSON object for the node
9891  */
9892     getNodeData : function(node){
9893         if(node instanceof Array){
9894             var data = [];
9895             for(var i = 0, len = node.length; i < len; i++){
9896                 data.push(this.getNodeData(node[i]));
9897             }
9898             return data;
9899         }
9900         return this.jsonData[this.indexOf(node)] || null;
9901     },
9902
9903     beforeRender : function(){
9904         this.snapshot = this.jsonData;
9905         if(this.sortInfo){
9906             this.sort.apply(this, this.sortInfo);
9907         }
9908         this.fireEvent("beforerender", this, this.jsonData);
9909     },
9910
9911     onLoad : function(el, o){
9912         this.fireEvent("load", this, this.jsonData, o);
9913     },
9914
9915     onLoadException : function(el, o){
9916         this.fireEvent("loadexception", this, o);
9917     },
9918
9919 /**
9920  * Filter the data by a specific property.
9921  * @param {String} property A property on your JSON objects
9922  * @param {String/RegExp} value Either string that the property values
9923  * should start with, or a RegExp to test against the property
9924  */
9925     filter : function(property, value){
9926         if(this.jsonData){
9927             var data = [];
9928             var ss = this.snapshot;
9929             if(typeof value == "string"){
9930                 var vlen = value.length;
9931                 if(vlen == 0){
9932                     this.clearFilter();
9933                     return;
9934                 }
9935                 value = value.toLowerCase();
9936                 for(var i = 0, len = ss.length; i < len; i++){
9937                     var o = ss[i];
9938                     if(o[property].substr(0, vlen).toLowerCase() == value){
9939                         data.push(o);
9940                     }
9941                 }
9942             } else if(value.exec){ // regex?
9943                 for(var i = 0, len = ss.length; i < len; i++){
9944                     var o = ss[i];
9945                     if(value.test(o[property])){
9946                         data.push(o);
9947                     }
9948                 }
9949             } else{
9950                 return;
9951             }
9952             this.jsonData = data;
9953             this.refresh();
9954         }
9955     },
9956
9957 /**
9958  * Filter by a function. The passed function will be called with each
9959  * object in the current dataset. If the function returns true the value is kept,
9960  * otherwise it is filtered.
9961  * @param {Function} fn
9962  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9963  */
9964     filterBy : function(fn, scope){
9965         if(this.jsonData){
9966             var data = [];
9967             var ss = this.snapshot;
9968             for(var i = 0, len = ss.length; i < len; i++){
9969                 var o = ss[i];
9970                 if(fn.call(scope || this, o)){
9971                     data.push(o);
9972                 }
9973             }
9974             this.jsonData = data;
9975             this.refresh();
9976         }
9977     },
9978
9979 /**
9980  * Clears the current filter.
9981  */
9982     clearFilter : function(){
9983         if(this.snapshot && this.jsonData != this.snapshot){
9984             this.jsonData = this.snapshot;
9985             this.refresh();
9986         }
9987     },
9988
9989
9990 /**
9991  * Sorts the data for this view and refreshes it.
9992  * @param {String} property A property on your JSON objects to sort on
9993  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9994  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9995  */
9996     sort : function(property, dir, sortType){
9997         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9998         if(this.jsonData){
9999             var p = property;
10000             var dsc = dir && dir.toLowerCase() == "desc";
10001             var f = function(o1, o2){
10002                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10003                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10004                 ;
10005                 if(v1 < v2){
10006                     return dsc ? +1 : -1;
10007                 } else if(v1 > v2){
10008                     return dsc ? -1 : +1;
10009                 } else{
10010                     return 0;
10011                 }
10012             };
10013             this.jsonData.sort(f);
10014             this.refresh();
10015             if(this.jsonData != this.snapshot){
10016                 this.snapshot.sort(f);
10017             }
10018         }
10019     }
10020 });/*
10021  * Based on:
10022  * Ext JS Library 1.1.1
10023  * Copyright(c) 2006-2007, Ext JS, LLC.
10024  *
10025  * Originally Released Under LGPL - original licence link has changed is not relivant.
10026  *
10027  * Fork - LGPL
10028  * <script type="text/javascript">
10029  */
10030  
10031
10032 /**
10033  * @class Roo.ColorPalette
10034  * @extends Roo.Component
10035  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10036  * Here's an example of typical usage:
10037  * <pre><code>
10038 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10039 cp.render('my-div');
10040
10041 cp.on('select', function(palette, selColor){
10042     // do something with selColor
10043 });
10044 </code></pre>
10045  * @constructor
10046  * Create a new ColorPalette
10047  * @param {Object} config The config object
10048  */
10049 Roo.ColorPalette = function(config){
10050     Roo.ColorPalette.superclass.constructor.call(this, config);
10051     this.addEvents({
10052         /**
10053              * @event select
10054              * Fires when a color is selected
10055              * @param {ColorPalette} this
10056              * @param {String} color The 6-digit color hex code (without the # symbol)
10057              */
10058         select: true
10059     });
10060
10061     if(this.handler){
10062         this.on("select", this.handler, this.scope, true);
10063     }
10064 };
10065 Roo.extend(Roo.ColorPalette, Roo.Component, {
10066     /**
10067      * @cfg {String} itemCls
10068      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10069      */
10070     itemCls : "x-color-palette",
10071     /**
10072      * @cfg {String} value
10073      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10074      * the hex codes are case-sensitive.
10075      */
10076     value : null,
10077     clickEvent:'click',
10078     // private
10079     ctype: "Roo.ColorPalette",
10080
10081     /**
10082      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10083      */
10084     allowReselect : false,
10085
10086     /**
10087      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10088      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10089      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10090      * of colors with the width setting until the box is symmetrical.</p>
10091      * <p>You can override individual colors if needed:</p>
10092      * <pre><code>
10093 var cp = new Roo.ColorPalette();
10094 cp.colors[0] = "FF0000";  // change the first box to red
10095 </code></pre>
10096
10097 Or you can provide a custom array of your own for complete control:
10098 <pre><code>
10099 var cp = new Roo.ColorPalette();
10100 cp.colors = ["000000", "993300", "333300"];
10101 </code></pre>
10102      * @type Array
10103      */
10104     colors : [
10105         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10106         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10107         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10108         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10109         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10110     ],
10111
10112     // private
10113     onRender : function(container, position){
10114         var t = new Roo.MasterTemplate(
10115             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10116         );
10117         var c = this.colors;
10118         for(var i = 0, len = c.length; i < len; i++){
10119             t.add([c[i]]);
10120         }
10121         var el = document.createElement("div");
10122         el.className = this.itemCls;
10123         t.overwrite(el);
10124         container.dom.insertBefore(el, position);
10125         this.el = Roo.get(el);
10126         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10127         if(this.clickEvent != 'click'){
10128             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10129         }
10130     },
10131
10132     // private
10133     afterRender : function(){
10134         Roo.ColorPalette.superclass.afterRender.call(this);
10135         if(this.value){
10136             var s = this.value;
10137             this.value = null;
10138             this.select(s);
10139         }
10140     },
10141
10142     // private
10143     handleClick : function(e, t){
10144         e.preventDefault();
10145         if(!this.disabled){
10146             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10147             this.select(c.toUpperCase());
10148         }
10149     },
10150
10151     /**
10152      * Selects the specified color in the palette (fires the select event)
10153      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10154      */
10155     select : function(color){
10156         color = color.replace("#", "");
10157         if(color != this.value || this.allowReselect){
10158             var el = this.el;
10159             if(this.value){
10160                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10161             }
10162             el.child("a.color-"+color).addClass("x-color-palette-sel");
10163             this.value = color;
10164             this.fireEvent("select", this, color);
10165         }
10166     }
10167 });/*
10168  * Based on:
10169  * Ext JS Library 1.1.1
10170  * Copyright(c) 2006-2007, Ext JS, LLC.
10171  *
10172  * Originally Released Under LGPL - original licence link has changed is not relivant.
10173  *
10174  * Fork - LGPL
10175  * <script type="text/javascript">
10176  */
10177  
10178 /**
10179  * @class Roo.DatePicker
10180  * @extends Roo.Component
10181  * Simple date picker class.
10182  * @constructor
10183  * Create a new DatePicker
10184  * @param {Object} config The config object
10185  */
10186 Roo.DatePicker = function(config){
10187     Roo.DatePicker.superclass.constructor.call(this, config);
10188
10189     this.value = config && config.value ?
10190                  config.value.clearTime() : new Date().clearTime();
10191
10192     this.addEvents({
10193         /**
10194              * @event select
10195              * Fires when a date is selected
10196              * @param {DatePicker} this
10197              * @param {Date} date The selected date
10198              */
10199         'select': true,
10200         /**
10201              * @event monthchange
10202              * Fires when the displayed month changes 
10203              * @param {DatePicker} this
10204              * @param {Date} date The selected month
10205              */
10206         'monthchange': true
10207     });
10208
10209     if(this.handler){
10210         this.on("select", this.handler,  this.scope || this);
10211     }
10212     // build the disabledDatesRE
10213     if(!this.disabledDatesRE && this.disabledDates){
10214         var dd = this.disabledDates;
10215         var re = "(?:";
10216         for(var i = 0; i < dd.length; i++){
10217             re += dd[i];
10218             if(i != dd.length-1) re += "|";
10219         }
10220         this.disabledDatesRE = new RegExp(re + ")");
10221     }
10222 };
10223
10224 Roo.extend(Roo.DatePicker, Roo.Component, {
10225     /**
10226      * @cfg {String} todayText
10227      * The text to display on the button that selects the current date (defaults to "Today")
10228      */
10229     todayText : "Today",
10230     /**
10231      * @cfg {String} okText
10232      * The text to display on the ok button
10233      */
10234     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10235     /**
10236      * @cfg {String} cancelText
10237      * The text to display on the cancel button
10238      */
10239     cancelText : "Cancel",
10240     /**
10241      * @cfg {String} todayTip
10242      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10243      */
10244     todayTip : "{0} (Spacebar)",
10245     /**
10246      * @cfg {Date} minDate
10247      * Minimum allowable date (JavaScript date object, defaults to null)
10248      */
10249     minDate : null,
10250     /**
10251      * @cfg {Date} maxDate
10252      * Maximum allowable date (JavaScript date object, defaults to null)
10253      */
10254     maxDate : null,
10255     /**
10256      * @cfg {String} minText
10257      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10258      */
10259     minText : "This date is before the minimum date",
10260     /**
10261      * @cfg {String} maxText
10262      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10263      */
10264     maxText : "This date is after the maximum date",
10265     /**
10266      * @cfg {String} format
10267      * The default date format string which can be overriden for localization support.  The format must be
10268      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10269      */
10270     format : "m/d/y",
10271     /**
10272      * @cfg {Array} disabledDays
10273      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10274      */
10275     disabledDays : null,
10276     /**
10277      * @cfg {String} disabledDaysText
10278      * The tooltip to display when the date falls on a disabled day (defaults to "")
10279      */
10280     disabledDaysText : "",
10281     /**
10282      * @cfg {RegExp} disabledDatesRE
10283      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10284      */
10285     disabledDatesRE : null,
10286     /**
10287      * @cfg {String} disabledDatesText
10288      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10289      */
10290     disabledDatesText : "",
10291     /**
10292      * @cfg {Boolean} constrainToViewport
10293      * True to constrain the date picker to the viewport (defaults to true)
10294      */
10295     constrainToViewport : true,
10296     /**
10297      * @cfg {Array} monthNames
10298      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10299      */
10300     monthNames : Date.monthNames,
10301     /**
10302      * @cfg {Array} dayNames
10303      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10304      */
10305     dayNames : Date.dayNames,
10306     /**
10307      * @cfg {String} nextText
10308      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10309      */
10310     nextText: 'Next Month (Control+Right)',
10311     /**
10312      * @cfg {String} prevText
10313      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10314      */
10315     prevText: 'Previous Month (Control+Left)',
10316     /**
10317      * @cfg {String} monthYearText
10318      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10319      */
10320     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10321     /**
10322      * @cfg {Number} startDay
10323      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10324      */
10325     startDay : 0,
10326     /**
10327      * @cfg {Bool} showClear
10328      * Show a clear button (usefull for date form elements that can be blank.)
10329      */
10330     
10331     showClear: false,
10332     
10333     /**
10334      * Sets the value of the date field
10335      * @param {Date} value The date to set
10336      */
10337     setValue : function(value){
10338         var old = this.value;
10339         
10340         if (typeof(value) == 'string') {
10341          
10342             value = Date.parseDate(value, this.format);
10343         }
10344         if (!value) {
10345             value = new Date();
10346         }
10347         
10348         this.value = value.clearTime(true);
10349         if(this.el){
10350             this.update(this.value);
10351         }
10352     },
10353
10354     /**
10355      * Gets the current selected value of the date field
10356      * @return {Date} The selected date
10357      */
10358     getValue : function(){
10359         return this.value;
10360     },
10361
10362     // private
10363     focus : function(){
10364         if(this.el){
10365             this.update(this.activeDate);
10366         }
10367     },
10368
10369     // privateval
10370     onRender : function(container, position){
10371         
10372         var m = [
10373              '<table cellspacing="0">',
10374                 '<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>',
10375                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10376         var dn = this.dayNames;
10377         for(var i = 0; i < 7; i++){
10378             var d = this.startDay+i;
10379             if(d > 6){
10380                 d = d-7;
10381             }
10382             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10383         }
10384         m[m.length] = "</tr></thead><tbody><tr>";
10385         for(var i = 0; i < 42; i++) {
10386             if(i % 7 == 0 && i != 0){
10387                 m[m.length] = "</tr><tr>";
10388             }
10389             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10390         }
10391         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10392             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10393
10394         var el = document.createElement("div");
10395         el.className = "x-date-picker";
10396         el.innerHTML = m.join("");
10397
10398         container.dom.insertBefore(el, position);
10399
10400         this.el = Roo.get(el);
10401         this.eventEl = Roo.get(el.firstChild);
10402
10403         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10404             handler: this.showPrevMonth,
10405             scope: this,
10406             preventDefault:true,
10407             stopDefault:true
10408         });
10409
10410         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10411             handler: this.showNextMonth,
10412             scope: this,
10413             preventDefault:true,
10414             stopDefault:true
10415         });
10416
10417         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10418
10419         this.monthPicker = this.el.down('div.x-date-mp');
10420         this.monthPicker.enableDisplayMode('block');
10421         
10422         var kn = new Roo.KeyNav(this.eventEl, {
10423             "left" : function(e){
10424                 e.ctrlKey ?
10425                     this.showPrevMonth() :
10426                     this.update(this.activeDate.add("d", -1));
10427             },
10428
10429             "right" : function(e){
10430                 e.ctrlKey ?
10431                     this.showNextMonth() :
10432                     this.update(this.activeDate.add("d", 1));
10433             },
10434
10435             "up" : function(e){
10436                 e.ctrlKey ?
10437                     this.showNextYear() :
10438                     this.update(this.activeDate.add("d", -7));
10439             },
10440
10441             "down" : function(e){
10442                 e.ctrlKey ?
10443                     this.showPrevYear() :
10444                     this.update(this.activeDate.add("d", 7));
10445             },
10446
10447             "pageUp" : function(e){
10448                 this.showNextMonth();
10449             },
10450
10451             "pageDown" : function(e){
10452                 this.showPrevMonth();
10453             },
10454
10455             "enter" : function(e){
10456                 e.stopPropagation();
10457                 return true;
10458             },
10459
10460             scope : this
10461         });
10462
10463         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10464
10465         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10466
10467         this.el.unselectable();
10468         
10469         this.cells = this.el.select("table.x-date-inner tbody td");
10470         this.textNodes = this.el.query("table.x-date-inner tbody span");
10471
10472         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10473             text: "&#160;",
10474             tooltip: this.monthYearText
10475         });
10476
10477         this.mbtn.on('click', this.showMonthPicker, this);
10478         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10479
10480
10481         var today = (new Date()).dateFormat(this.format);
10482         
10483         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10484         if (this.showClear) {
10485             baseTb.add( new Roo.Toolbar.Fill());
10486         }
10487         baseTb.add({
10488             text: String.format(this.todayText, today),
10489             tooltip: String.format(this.todayTip, today),
10490             handler: this.selectToday,
10491             scope: this
10492         });
10493         
10494         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10495             
10496         //});
10497         if (this.showClear) {
10498             
10499             baseTb.add( new Roo.Toolbar.Fill());
10500             baseTb.add({
10501                 text: '&#160;',
10502                 cls: 'x-btn-icon x-btn-clear',
10503                 handler: function() {
10504                     //this.value = '';
10505                     this.fireEvent("select", this, '');
10506                 },
10507                 scope: this
10508             });
10509         }
10510         
10511         
10512         if(Roo.isIE){
10513             this.el.repaint();
10514         }
10515         this.update(this.value);
10516     },
10517
10518     createMonthPicker : function(){
10519         if(!this.monthPicker.dom.firstChild){
10520             var buf = ['<table border="0" cellspacing="0">'];
10521             for(var i = 0; i < 6; i++){
10522                 buf.push(
10523                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10524                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10525                     i == 0 ?
10526                     '<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>' :
10527                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10528                 );
10529             }
10530             buf.push(
10531                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10532                     this.okText,
10533                     '</button><button type="button" class="x-date-mp-cancel">',
10534                     this.cancelText,
10535                     '</button></td></tr>',
10536                 '</table>'
10537             );
10538             this.monthPicker.update(buf.join(''));
10539             this.monthPicker.on('click', this.onMonthClick, this);
10540             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10541
10542             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10543             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10544
10545             this.mpMonths.each(function(m, a, i){
10546                 i += 1;
10547                 if((i%2) == 0){
10548                     m.dom.xmonth = 5 + Math.round(i * .5);
10549                 }else{
10550                     m.dom.xmonth = Math.round((i-1) * .5);
10551                 }
10552             });
10553         }
10554     },
10555
10556     showMonthPicker : function(){
10557         this.createMonthPicker();
10558         var size = this.el.getSize();
10559         this.monthPicker.setSize(size);
10560         this.monthPicker.child('table').setSize(size);
10561
10562         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10563         this.updateMPMonth(this.mpSelMonth);
10564         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10565         this.updateMPYear(this.mpSelYear);
10566
10567         this.monthPicker.slideIn('t', {duration:.2});
10568     },
10569
10570     updateMPYear : function(y){
10571         this.mpyear = y;
10572         var ys = this.mpYears.elements;
10573         for(var i = 1; i <= 10; i++){
10574             var td = ys[i-1], y2;
10575             if((i%2) == 0){
10576                 y2 = y + Math.round(i * .5);
10577                 td.firstChild.innerHTML = y2;
10578                 td.xyear = y2;
10579             }else{
10580                 y2 = y - (5-Math.round(i * .5));
10581                 td.firstChild.innerHTML = y2;
10582                 td.xyear = y2;
10583             }
10584             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10585         }
10586     },
10587
10588     updateMPMonth : function(sm){
10589         this.mpMonths.each(function(m, a, i){
10590             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10591         });
10592     },
10593
10594     selectMPMonth: function(m){
10595         
10596     },
10597
10598     onMonthClick : function(e, t){
10599         e.stopEvent();
10600         var el = new Roo.Element(t), pn;
10601         if(el.is('button.x-date-mp-cancel')){
10602             this.hideMonthPicker();
10603         }
10604         else if(el.is('button.x-date-mp-ok')){
10605             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10606             this.hideMonthPicker();
10607         }
10608         else if(pn = el.up('td.x-date-mp-month', 2)){
10609             this.mpMonths.removeClass('x-date-mp-sel');
10610             pn.addClass('x-date-mp-sel');
10611             this.mpSelMonth = pn.dom.xmonth;
10612         }
10613         else if(pn = el.up('td.x-date-mp-year', 2)){
10614             this.mpYears.removeClass('x-date-mp-sel');
10615             pn.addClass('x-date-mp-sel');
10616             this.mpSelYear = pn.dom.xyear;
10617         }
10618         else if(el.is('a.x-date-mp-prev')){
10619             this.updateMPYear(this.mpyear-10);
10620         }
10621         else if(el.is('a.x-date-mp-next')){
10622             this.updateMPYear(this.mpyear+10);
10623         }
10624     },
10625
10626     onMonthDblClick : function(e, t){
10627         e.stopEvent();
10628         var el = new Roo.Element(t), pn;
10629         if(pn = el.up('td.x-date-mp-month', 2)){
10630             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10631             this.hideMonthPicker();
10632         }
10633         else if(pn = el.up('td.x-date-mp-year', 2)){
10634             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10635             this.hideMonthPicker();
10636         }
10637     },
10638
10639     hideMonthPicker : function(disableAnim){
10640         if(this.monthPicker){
10641             if(disableAnim === true){
10642                 this.monthPicker.hide();
10643             }else{
10644                 this.monthPicker.slideOut('t', {duration:.2});
10645             }
10646         }
10647     },
10648
10649     // private
10650     showPrevMonth : function(e){
10651         this.update(this.activeDate.add("mo", -1));
10652     },
10653
10654     // private
10655     showNextMonth : function(e){
10656         this.update(this.activeDate.add("mo", 1));
10657     },
10658
10659     // private
10660     showPrevYear : function(){
10661         this.update(this.activeDate.add("y", -1));
10662     },
10663
10664     // private
10665     showNextYear : function(){
10666         this.update(this.activeDate.add("y", 1));
10667     },
10668
10669     // private
10670     handleMouseWheel : function(e){
10671         var delta = e.getWheelDelta();
10672         if(delta > 0){
10673             this.showPrevMonth();
10674             e.stopEvent();
10675         } else if(delta < 0){
10676             this.showNextMonth();
10677             e.stopEvent();
10678         }
10679     },
10680
10681     // private
10682     handleDateClick : function(e, t){
10683         e.stopEvent();
10684         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10685             this.setValue(new Date(t.dateValue));
10686             this.fireEvent("select", this, this.value);
10687         }
10688     },
10689
10690     // private
10691     selectToday : function(){
10692         this.setValue(new Date().clearTime());
10693         this.fireEvent("select", this, this.value);
10694     },
10695
10696     // private
10697     update : function(date)
10698     {
10699         var vd = this.activeDate;
10700         this.activeDate = date;
10701         if(vd && this.el){
10702             var t = date.getTime();
10703             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10704                 this.cells.removeClass("x-date-selected");
10705                 this.cells.each(function(c){
10706                    if(c.dom.firstChild.dateValue == t){
10707                        c.addClass("x-date-selected");
10708                        setTimeout(function(){
10709                             try{c.dom.firstChild.focus();}catch(e){}
10710                        }, 50);
10711                        return false;
10712                    }
10713                 });
10714                 return;
10715             }
10716         }
10717         
10718         var days = date.getDaysInMonth();
10719         var firstOfMonth = date.getFirstDateOfMonth();
10720         var startingPos = firstOfMonth.getDay()-this.startDay;
10721
10722         if(startingPos <= this.startDay){
10723             startingPos += 7;
10724         }
10725
10726         var pm = date.add("mo", -1);
10727         var prevStart = pm.getDaysInMonth()-startingPos;
10728
10729         var cells = this.cells.elements;
10730         var textEls = this.textNodes;
10731         days += startingPos;
10732
10733         // convert everything to numbers so it's fast
10734         var day = 86400000;
10735         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10736         var today = new Date().clearTime().getTime();
10737         var sel = date.clearTime().getTime();
10738         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10739         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10740         var ddMatch = this.disabledDatesRE;
10741         var ddText = this.disabledDatesText;
10742         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10743         var ddaysText = this.disabledDaysText;
10744         var format = this.format;
10745
10746         var setCellClass = function(cal, cell){
10747             cell.title = "";
10748             var t = d.getTime();
10749             cell.firstChild.dateValue = t;
10750             if(t == today){
10751                 cell.className += " x-date-today";
10752                 cell.title = cal.todayText;
10753             }
10754             if(t == sel){
10755                 cell.className += " x-date-selected";
10756                 setTimeout(function(){
10757                     try{cell.firstChild.focus();}catch(e){}
10758                 }, 50);
10759             }
10760             // disabling
10761             if(t < min) {
10762                 cell.className = " x-date-disabled";
10763                 cell.title = cal.minText;
10764                 return;
10765             }
10766             if(t > max) {
10767                 cell.className = " x-date-disabled";
10768                 cell.title = cal.maxText;
10769                 return;
10770             }
10771             if(ddays){
10772                 if(ddays.indexOf(d.getDay()) != -1){
10773                     cell.title = ddaysText;
10774                     cell.className = " x-date-disabled";
10775                 }
10776             }
10777             if(ddMatch && format){
10778                 var fvalue = d.dateFormat(format);
10779                 if(ddMatch.test(fvalue)){
10780                     cell.title = ddText.replace("%0", fvalue);
10781                     cell.className = " x-date-disabled";
10782                 }
10783             }
10784         };
10785
10786         var i = 0;
10787         for(; i < startingPos; i++) {
10788             textEls[i].innerHTML = (++prevStart);
10789             d.setDate(d.getDate()+1);
10790             cells[i].className = "x-date-prevday";
10791             setCellClass(this, cells[i]);
10792         }
10793         for(; i < days; i++){
10794             intDay = i - startingPos + 1;
10795             textEls[i].innerHTML = (intDay);
10796             d.setDate(d.getDate()+1);
10797             cells[i].className = "x-date-active";
10798             setCellClass(this, cells[i]);
10799         }
10800         var extraDays = 0;
10801         for(; i < 42; i++) {
10802              textEls[i].innerHTML = (++extraDays);
10803              d.setDate(d.getDate()+1);
10804              cells[i].className = "x-date-nextday";
10805              setCellClass(this, cells[i]);
10806         }
10807
10808         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10809         this.fireEvent('monthchange', this, date);
10810         
10811         if(!this.internalRender){
10812             var main = this.el.dom.firstChild;
10813             var w = main.offsetWidth;
10814             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10815             Roo.fly(main).setWidth(w);
10816             this.internalRender = true;
10817             // opera does not respect the auto grow header center column
10818             // then, after it gets a width opera refuses to recalculate
10819             // without a second pass
10820             if(Roo.isOpera && !this.secondPass){
10821                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10822                 this.secondPass = true;
10823                 this.update.defer(10, this, [date]);
10824             }
10825         }
10826         
10827         
10828     }
10829 });        /*
10830  * Based on:
10831  * Ext JS Library 1.1.1
10832  * Copyright(c) 2006-2007, Ext JS, LLC.
10833  *
10834  * Originally Released Under LGPL - original licence link has changed is not relivant.
10835  *
10836  * Fork - LGPL
10837  * <script type="text/javascript">
10838  */
10839 /**
10840  * @class Roo.TabPanel
10841  * @extends Roo.util.Observable
10842  * A lightweight tab container.
10843  * <br><br>
10844  * Usage:
10845  * <pre><code>
10846 // basic tabs 1, built from existing content
10847 var tabs = new Roo.TabPanel("tabs1");
10848 tabs.addTab("script", "View Script");
10849 tabs.addTab("markup", "View Markup");
10850 tabs.activate("script");
10851
10852 // more advanced tabs, built from javascript
10853 var jtabs = new Roo.TabPanel("jtabs");
10854 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10855
10856 // set up the UpdateManager
10857 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10858 var updater = tab2.getUpdateManager();
10859 updater.setDefaultUrl("ajax1.htm");
10860 tab2.on('activate', updater.refresh, updater, true);
10861
10862 // Use setUrl for Ajax loading
10863 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10864 tab3.setUrl("ajax2.htm", null, true);
10865
10866 // Disabled tab
10867 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10868 tab4.disable();
10869
10870 jtabs.activate("jtabs-1");
10871  * </code></pre>
10872  * @constructor
10873  * Create a new TabPanel.
10874  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10875  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10876  */
10877 Roo.TabPanel = function(container, config){
10878     /**
10879     * The container element for this TabPanel.
10880     * @type Roo.Element
10881     */
10882     this.el = Roo.get(container, true);
10883     if(config){
10884         if(typeof config == "boolean"){
10885             this.tabPosition = config ? "bottom" : "top";
10886         }else{
10887             Roo.apply(this, config);
10888         }
10889     }
10890     if(this.tabPosition == "bottom"){
10891         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10892         this.el.addClass("x-tabs-bottom");
10893     }
10894     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10895     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10896     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10897     if(Roo.isIE){
10898         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10899     }
10900     if(this.tabPosition != "bottom"){
10901         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10902          * @type Roo.Element
10903          */
10904         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10905         this.el.addClass("x-tabs-top");
10906     }
10907     this.items = [];
10908
10909     this.bodyEl.setStyle("position", "relative");
10910
10911     this.active = null;
10912     this.activateDelegate = this.activate.createDelegate(this);
10913
10914     this.addEvents({
10915         /**
10916          * @event tabchange
10917          * Fires when the active tab changes
10918          * @param {Roo.TabPanel} this
10919          * @param {Roo.TabPanelItem} activePanel The new active tab
10920          */
10921         "tabchange": true,
10922         /**
10923          * @event beforetabchange
10924          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10925          * @param {Roo.TabPanel} this
10926          * @param {Object} e Set cancel to true on this object to cancel the tab change
10927          * @param {Roo.TabPanelItem} tab The tab being changed to
10928          */
10929         "beforetabchange" : true
10930     });
10931
10932     Roo.EventManager.onWindowResize(this.onResize, this);
10933     this.cpad = this.el.getPadding("lr");
10934     this.hiddenCount = 0;
10935
10936
10937     // toolbar on the tabbar support...
10938     if (this.toolbar) {
10939         var tcfg = this.toolbar;
10940         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10941         this.toolbar = new Roo.Toolbar(tcfg);
10942         if (Roo.isSafari) {
10943             var tbl = tcfg.container.child('table', true);
10944             tbl.setAttribute('width', '100%');
10945         }
10946         
10947     }
10948    
10949
10950
10951     Roo.TabPanel.superclass.constructor.call(this);
10952 };
10953
10954 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10955     /*
10956      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10957      */
10958     tabPosition : "top",
10959     /*
10960      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10961      */
10962     currentTabWidth : 0,
10963     /*
10964      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10965      */
10966     minTabWidth : 40,
10967     /*
10968      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10969      */
10970     maxTabWidth : 250,
10971     /*
10972      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10973      */
10974     preferredTabWidth : 175,
10975     /*
10976      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10977      */
10978     resizeTabs : false,
10979     /*
10980      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10981      */
10982     monitorResize : true,
10983     /*
10984      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10985      */
10986     toolbar : false,
10987
10988     /**
10989      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10990      * @param {String} id The id of the div to use <b>or create</b>
10991      * @param {String} text The text for the tab
10992      * @param {String} content (optional) Content to put in the TabPanelItem body
10993      * @param {Boolean} closable (optional) True to create a close icon on the tab
10994      * @return {Roo.TabPanelItem} The created TabPanelItem
10995      */
10996     addTab : function(id, text, content, closable){
10997         var item = new Roo.TabPanelItem(this, id, text, closable);
10998         this.addTabItem(item);
10999         if(content){
11000             item.setContent(content);
11001         }
11002         return item;
11003     },
11004
11005     /**
11006      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11007      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11008      * @return {Roo.TabPanelItem}
11009      */
11010     getTab : function(id){
11011         return this.items[id];
11012     },
11013
11014     /**
11015      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11016      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11017      */
11018     hideTab : function(id){
11019         var t = this.items[id];
11020         if(!t.isHidden()){
11021            t.setHidden(true);
11022            this.hiddenCount++;
11023            this.autoSizeTabs();
11024         }
11025     },
11026
11027     /**
11028      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11029      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11030      */
11031     unhideTab : function(id){
11032         var t = this.items[id];
11033         if(t.isHidden()){
11034            t.setHidden(false);
11035            this.hiddenCount--;
11036            this.autoSizeTabs();
11037         }
11038     },
11039
11040     /**
11041      * Adds an existing {@link Roo.TabPanelItem}.
11042      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11043      */
11044     addTabItem : function(item){
11045         this.items[item.id] = item;
11046         this.items.push(item);
11047         if(this.resizeTabs){
11048            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11049            this.autoSizeTabs();
11050         }else{
11051             item.autoSize();
11052         }
11053     },
11054
11055     /**
11056      * Removes a {@link Roo.TabPanelItem}.
11057      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11058      */
11059     removeTab : function(id){
11060         var items = this.items;
11061         var tab = items[id];
11062         if(!tab) { return; }
11063         var index = items.indexOf(tab);
11064         if(this.active == tab && items.length > 1){
11065             var newTab = this.getNextAvailable(index);
11066             if(newTab) {
11067                 newTab.activate();
11068             }
11069         }
11070         this.stripEl.dom.removeChild(tab.pnode.dom);
11071         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11072             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11073         }
11074         items.splice(index, 1);
11075         delete this.items[tab.id];
11076         tab.fireEvent("close", tab);
11077         tab.purgeListeners();
11078         this.autoSizeTabs();
11079     },
11080
11081     getNextAvailable : function(start){
11082         var items = this.items;
11083         var index = start;
11084         // look for a next tab that will slide over to
11085         // replace the one being removed
11086         while(index < items.length){
11087             var item = items[++index];
11088             if(item && !item.isHidden()){
11089                 return item;
11090             }
11091         }
11092         // if one isn't found select the previous tab (on the left)
11093         index = start;
11094         while(index >= 0){
11095             var item = items[--index];
11096             if(item && !item.isHidden()){
11097                 return item;
11098             }
11099         }
11100         return null;
11101     },
11102
11103     /**
11104      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11105      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11106      */
11107     disableTab : function(id){
11108         var tab = this.items[id];
11109         if(tab && this.active != tab){
11110             tab.disable();
11111         }
11112     },
11113
11114     /**
11115      * Enables a {@link Roo.TabPanelItem} that is disabled.
11116      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11117      */
11118     enableTab : function(id){
11119         var tab = this.items[id];
11120         tab.enable();
11121     },
11122
11123     /**
11124      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11125      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11126      * @return {Roo.TabPanelItem} The TabPanelItem.
11127      */
11128     activate : function(id){
11129         var tab = this.items[id];
11130         if(!tab){
11131             return null;
11132         }
11133         if(tab == this.active || tab.disabled){
11134             return tab;
11135         }
11136         var e = {};
11137         this.fireEvent("beforetabchange", this, e, tab);
11138         if(e.cancel !== true && !tab.disabled){
11139             if(this.active){
11140                 this.active.hide();
11141             }
11142             this.active = this.items[id];
11143             this.active.show();
11144             this.fireEvent("tabchange", this, this.active);
11145         }
11146         return tab;
11147     },
11148
11149     /**
11150      * Gets the active {@link Roo.TabPanelItem}.
11151      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11152      */
11153     getActiveTab : function(){
11154         return this.active;
11155     },
11156
11157     /**
11158      * Updates the tab body element to fit the height of the container element
11159      * for overflow scrolling
11160      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11161      */
11162     syncHeight : function(targetHeight){
11163         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11164         var bm = this.bodyEl.getMargins();
11165         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11166         this.bodyEl.setHeight(newHeight);
11167         return newHeight;
11168     },
11169
11170     onResize : function(){
11171         if(this.monitorResize){
11172             this.autoSizeTabs();
11173         }
11174     },
11175
11176     /**
11177      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11178      */
11179     beginUpdate : function(){
11180         this.updating = true;
11181     },
11182
11183     /**
11184      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11185      */
11186     endUpdate : function(){
11187         this.updating = false;
11188         this.autoSizeTabs();
11189     },
11190
11191     /**
11192      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11193      */
11194     autoSizeTabs : function(){
11195         var count = this.items.length;
11196         var vcount = count - this.hiddenCount;
11197         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11198         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11199         var availWidth = Math.floor(w / vcount);
11200         var b = this.stripBody;
11201         if(b.getWidth() > w){
11202             var tabs = this.items;
11203             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11204             if(availWidth < this.minTabWidth){
11205                 /*if(!this.sleft){    // incomplete scrolling code
11206                     this.createScrollButtons();
11207                 }
11208                 this.showScroll();
11209                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11210             }
11211         }else{
11212             if(this.currentTabWidth < this.preferredTabWidth){
11213                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11214             }
11215         }
11216     },
11217
11218     /**
11219      * Returns the number of tabs in this TabPanel.
11220      * @return {Number}
11221      */
11222      getCount : function(){
11223          return this.items.length;
11224      },
11225
11226     /**
11227      * Resizes all the tabs to the passed width
11228      * @param {Number} The new width
11229      */
11230     setTabWidth : function(width){
11231         this.currentTabWidth = width;
11232         for(var i = 0, len = this.items.length; i < len; i++) {
11233                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11234         }
11235     },
11236
11237     /**
11238      * Destroys this TabPanel
11239      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11240      */
11241     destroy : function(removeEl){
11242         Roo.EventManager.removeResizeListener(this.onResize, this);
11243         for(var i = 0, len = this.items.length; i < len; i++){
11244             this.items[i].purgeListeners();
11245         }
11246         if(removeEl === true){
11247             this.el.update("");
11248             this.el.remove();
11249         }
11250     }
11251 });
11252
11253 /**
11254  * @class Roo.TabPanelItem
11255  * @extends Roo.util.Observable
11256  * Represents an individual item (tab plus body) in a TabPanel.
11257  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11258  * @param {String} id The id of this TabPanelItem
11259  * @param {String} text The text for the tab of this TabPanelItem
11260  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11261  */
11262 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11263     /**
11264      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11265      * @type Roo.TabPanel
11266      */
11267     this.tabPanel = tabPanel;
11268     /**
11269      * The id for this TabPanelItem
11270      * @type String
11271      */
11272     this.id = id;
11273     /** @private */
11274     this.disabled = false;
11275     /** @private */
11276     this.text = text;
11277     /** @private */
11278     this.loaded = false;
11279     this.closable = closable;
11280
11281     /**
11282      * The body element for this TabPanelItem.
11283      * @type Roo.Element
11284      */
11285     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11286     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11287     this.bodyEl.setStyle("display", "block");
11288     this.bodyEl.setStyle("zoom", "1");
11289     this.hideAction();
11290
11291     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11292     /** @private */
11293     this.el = Roo.get(els.el, true);
11294     this.inner = Roo.get(els.inner, true);
11295     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11296     this.pnode = Roo.get(els.el.parentNode, true);
11297     this.el.on("mousedown", this.onTabMouseDown, this);
11298     this.el.on("click", this.onTabClick, this);
11299     /** @private */
11300     if(closable){
11301         var c = Roo.get(els.close, true);
11302         c.dom.title = this.closeText;
11303         c.addClassOnOver("close-over");
11304         c.on("click", this.closeClick, this);
11305      }
11306
11307     this.addEvents({
11308          /**
11309          * @event activate
11310          * Fires when this tab becomes the active tab.
11311          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11312          * @param {Roo.TabPanelItem} this
11313          */
11314         "activate": true,
11315         /**
11316          * @event beforeclose
11317          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11318          * @param {Roo.TabPanelItem} this
11319          * @param {Object} e Set cancel to true on this object to cancel the close.
11320          */
11321         "beforeclose": true,
11322         /**
11323          * @event close
11324          * Fires when this tab is closed.
11325          * @param {Roo.TabPanelItem} this
11326          */
11327          "close": true,
11328         /**
11329          * @event deactivate
11330          * Fires when this tab is no longer the active tab.
11331          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11332          * @param {Roo.TabPanelItem} this
11333          */
11334          "deactivate" : true
11335     });
11336     this.hidden = false;
11337
11338     Roo.TabPanelItem.superclass.constructor.call(this);
11339 };
11340
11341 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11342     purgeListeners : function(){
11343        Roo.util.Observable.prototype.purgeListeners.call(this);
11344        this.el.removeAllListeners();
11345     },
11346     /**
11347      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11348      */
11349     show : function(){
11350         this.pnode.addClass("on");
11351         this.showAction();
11352         if(Roo.isOpera){
11353             this.tabPanel.stripWrap.repaint();
11354         }
11355         this.fireEvent("activate", this.tabPanel, this);
11356     },
11357
11358     /**
11359      * Returns true if this tab is the active tab.
11360      * @return {Boolean}
11361      */
11362     isActive : function(){
11363         return this.tabPanel.getActiveTab() == this;
11364     },
11365
11366     /**
11367      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11368      */
11369     hide : function(){
11370         this.pnode.removeClass("on");
11371         this.hideAction();
11372         this.fireEvent("deactivate", this.tabPanel, this);
11373     },
11374
11375     hideAction : function(){
11376         this.bodyEl.hide();
11377         this.bodyEl.setStyle("position", "absolute");
11378         this.bodyEl.setLeft("-20000px");
11379         this.bodyEl.setTop("-20000px");
11380     },
11381
11382     showAction : function(){
11383         this.bodyEl.setStyle("position", "relative");
11384         this.bodyEl.setTop("");
11385         this.bodyEl.setLeft("");
11386         this.bodyEl.show();
11387     },
11388
11389     /**
11390      * Set the tooltip for the tab.
11391      * @param {String} tooltip The tab's tooltip
11392      */
11393     setTooltip : function(text){
11394         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11395             this.textEl.dom.qtip = text;
11396             this.textEl.dom.removeAttribute('title');
11397         }else{
11398             this.textEl.dom.title = text;
11399         }
11400     },
11401
11402     onTabClick : function(e){
11403         e.preventDefault();
11404         this.tabPanel.activate(this.id);
11405     },
11406
11407     onTabMouseDown : function(e){
11408         e.preventDefault();
11409         this.tabPanel.activate(this.id);
11410     },
11411
11412     getWidth : function(){
11413         return this.inner.getWidth();
11414     },
11415
11416     setWidth : function(width){
11417         var iwidth = width - this.pnode.getPadding("lr");
11418         this.inner.setWidth(iwidth);
11419         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11420         this.pnode.setWidth(width);
11421     },
11422
11423     /**
11424      * Show or hide the tab
11425      * @param {Boolean} hidden True to hide or false to show.
11426      */
11427     setHidden : function(hidden){
11428         this.hidden = hidden;
11429         this.pnode.setStyle("display", hidden ? "none" : "");
11430     },
11431
11432     /**
11433      * Returns true if this tab is "hidden"
11434      * @return {Boolean}
11435      */
11436     isHidden : function(){
11437         return this.hidden;
11438     },
11439
11440     /**
11441      * Returns the text for this tab
11442      * @return {String}
11443      */
11444     getText : function(){
11445         return this.text;
11446     },
11447
11448     autoSize : function(){
11449         //this.el.beginMeasure();
11450         this.textEl.setWidth(1);
11451         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11452         //this.el.endMeasure();
11453     },
11454
11455     /**
11456      * Sets the text for the tab (Note: this also sets the tooltip text)
11457      * @param {String} text The tab's text and tooltip
11458      */
11459     setText : function(text){
11460         this.text = text;
11461         this.textEl.update(text);
11462         this.setTooltip(text);
11463         if(!this.tabPanel.resizeTabs){
11464             this.autoSize();
11465         }
11466     },
11467     /**
11468      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11469      */
11470     activate : function(){
11471         this.tabPanel.activate(this.id);
11472     },
11473
11474     /**
11475      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11476      */
11477     disable : function(){
11478         if(this.tabPanel.active != this){
11479             this.disabled = true;
11480             this.pnode.addClass("disabled");
11481         }
11482     },
11483
11484     /**
11485      * Enables this TabPanelItem if it was previously disabled.
11486      */
11487     enable : function(){
11488         this.disabled = false;
11489         this.pnode.removeClass("disabled");
11490     },
11491
11492     /**
11493      * Sets the content for this TabPanelItem.
11494      * @param {String} content The content
11495      * @param {Boolean} loadScripts true to look for and load scripts
11496      */
11497     setContent : function(content, loadScripts){
11498         this.bodyEl.update(content, loadScripts);
11499     },
11500
11501     /**
11502      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11503      * @return {Roo.UpdateManager} The UpdateManager
11504      */
11505     getUpdateManager : function(){
11506         return this.bodyEl.getUpdateManager();
11507     },
11508
11509     /**
11510      * Set a URL to be used to load the content for this TabPanelItem.
11511      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11512      * @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)
11513      * @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)
11514      * @return {Roo.UpdateManager} The UpdateManager
11515      */
11516     setUrl : function(url, params, loadOnce){
11517         if(this.refreshDelegate){
11518             this.un('activate', this.refreshDelegate);
11519         }
11520         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11521         this.on("activate", this.refreshDelegate);
11522         return this.bodyEl.getUpdateManager();
11523     },
11524
11525     /** @private */
11526     _handleRefresh : function(url, params, loadOnce){
11527         if(!loadOnce || !this.loaded){
11528             var updater = this.bodyEl.getUpdateManager();
11529             updater.update(url, params, this._setLoaded.createDelegate(this));
11530         }
11531     },
11532
11533     /**
11534      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11535      *   Will fail silently if the setUrl method has not been called.
11536      *   This does not activate the panel, just updates its content.
11537      */
11538     refresh : function(){
11539         if(this.refreshDelegate){
11540            this.loaded = false;
11541            this.refreshDelegate();
11542         }
11543     },
11544
11545     /** @private */
11546     _setLoaded : function(){
11547         this.loaded = true;
11548     },
11549
11550     /** @private */
11551     closeClick : function(e){
11552         var o = {};
11553         e.stopEvent();
11554         this.fireEvent("beforeclose", this, o);
11555         if(o.cancel !== true){
11556             this.tabPanel.removeTab(this.id);
11557         }
11558     },
11559     /**
11560      * The text displayed in the tooltip for the close icon.
11561      * @type String
11562      */
11563     closeText : "Close this tab"
11564 });
11565
11566 /** @private */
11567 Roo.TabPanel.prototype.createStrip = function(container){
11568     var strip = document.createElement("div");
11569     strip.className = "x-tabs-wrap";
11570     container.appendChild(strip);
11571     return strip;
11572 };
11573 /** @private */
11574 Roo.TabPanel.prototype.createStripList = function(strip){
11575     // div wrapper for retard IE
11576     // returns the "tr" element.
11577     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11578         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11579         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11580     return strip.firstChild.firstChild.firstChild.firstChild;
11581 };
11582 /** @private */
11583 Roo.TabPanel.prototype.createBody = function(container){
11584     var body = document.createElement("div");
11585     Roo.id(body, "tab-body");
11586     Roo.fly(body).addClass("x-tabs-body");
11587     container.appendChild(body);
11588     return body;
11589 };
11590 /** @private */
11591 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11592     var body = Roo.getDom(id);
11593     if(!body){
11594         body = document.createElement("div");
11595         body.id = id;
11596     }
11597     Roo.fly(body).addClass("x-tabs-item-body");
11598     bodyEl.insertBefore(body, bodyEl.firstChild);
11599     return body;
11600 };
11601 /** @private */
11602 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11603     var td = document.createElement("td");
11604     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11605     //stripEl.appendChild(td);
11606     if(closable){
11607         td.className = "x-tabs-closable";
11608         if(!this.closeTpl){
11609             this.closeTpl = new Roo.Template(
11610                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11611                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11612                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11613             );
11614         }
11615         var el = this.closeTpl.overwrite(td, {"text": text});
11616         var close = el.getElementsByTagName("div")[0];
11617         var inner = el.getElementsByTagName("em")[0];
11618         return {"el": el, "close": close, "inner": inner};
11619     } else {
11620         if(!this.tabTpl){
11621             this.tabTpl = new Roo.Template(
11622                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11623                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11624             );
11625         }
11626         var el = this.tabTpl.overwrite(td, {"text": text});
11627         var inner = el.getElementsByTagName("em")[0];
11628         return {"el": el, "inner": inner};
11629     }
11630 };/*
11631  * Based on:
11632  * Ext JS Library 1.1.1
11633  * Copyright(c) 2006-2007, Ext JS, LLC.
11634  *
11635  * Originally Released Under LGPL - original licence link has changed is not relivant.
11636  *
11637  * Fork - LGPL
11638  * <script type="text/javascript">
11639  */
11640
11641 /**
11642  * @class Roo.Button
11643  * @extends Roo.util.Observable
11644  * Simple Button class
11645  * @cfg {String} text The button text
11646  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11647  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11648  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11649  * @cfg {Object} scope The scope of the handler
11650  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11651  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11652  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11653  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11654  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11655  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11656    applies if enableToggle = true)
11657  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11658  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11659   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11660  * @constructor
11661  * Create a new button
11662  * @param {Object} config The config object
11663  */
11664 Roo.Button = function(renderTo, config)
11665 {
11666     if (!config) {
11667         config = renderTo;
11668         renderTo = config.renderTo || false;
11669     }
11670     
11671     Roo.apply(this, config);
11672     this.addEvents({
11673         /**
11674              * @event click
11675              * Fires when this button is clicked
11676              * @param {Button} this
11677              * @param {EventObject} e The click event
11678              */
11679             "click" : true,
11680         /**
11681              * @event toggle
11682              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11683              * @param {Button} this
11684              * @param {Boolean} pressed
11685              */
11686             "toggle" : true,
11687         /**
11688              * @event mouseover
11689              * Fires when the mouse hovers over the button
11690              * @param {Button} this
11691              * @param {Event} e The event object
11692              */
11693         'mouseover' : true,
11694         /**
11695              * @event mouseout
11696              * Fires when the mouse exits the button
11697              * @param {Button} this
11698              * @param {Event} e The event object
11699              */
11700         'mouseout': true,
11701          /**
11702              * @event render
11703              * Fires when the button is rendered
11704              * @param {Button} this
11705              */
11706         'render': true
11707     });
11708     if(this.menu){
11709         this.menu = Roo.menu.MenuMgr.get(this.menu);
11710     }
11711     // register listeners first!!  - so render can be captured..
11712     Roo.util.Observable.call(this);
11713     if(renderTo){
11714         this.render(renderTo);
11715     }
11716     
11717   
11718 };
11719
11720 Roo.extend(Roo.Button, Roo.util.Observable, {
11721     /**
11722      * 
11723      */
11724     
11725     /**
11726      * Read-only. True if this button is hidden
11727      * @type Boolean
11728      */
11729     hidden : false,
11730     /**
11731      * Read-only. True if this button is disabled
11732      * @type Boolean
11733      */
11734     disabled : false,
11735     /**
11736      * Read-only. True if this button is pressed (only if enableToggle = true)
11737      * @type Boolean
11738      */
11739     pressed : false,
11740
11741     /**
11742      * @cfg {Number} tabIndex 
11743      * The DOM tabIndex for this button (defaults to undefined)
11744      */
11745     tabIndex : undefined,
11746
11747     /**
11748      * @cfg {Boolean} enableToggle
11749      * True to enable pressed/not pressed toggling (defaults to false)
11750      */
11751     enableToggle: false,
11752     /**
11753      * @cfg {Mixed} menu
11754      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11755      */
11756     menu : undefined,
11757     /**
11758      * @cfg {String} menuAlign
11759      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11760      */
11761     menuAlign : "tl-bl?",
11762
11763     /**
11764      * @cfg {String} iconCls
11765      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11766      */
11767     iconCls : undefined,
11768     /**
11769      * @cfg {String} type
11770      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11771      */
11772     type : 'button',
11773
11774     // private
11775     menuClassTarget: 'tr',
11776
11777     /**
11778      * @cfg {String} clickEvent
11779      * The type of event to map to the button's event handler (defaults to 'click')
11780      */
11781     clickEvent : 'click',
11782
11783     /**
11784      * @cfg {Boolean} handleMouseEvents
11785      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11786      */
11787     handleMouseEvents : true,
11788
11789     /**
11790      * @cfg {String} tooltipType
11791      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11792      */
11793     tooltipType : 'qtip',
11794
11795     /**
11796      * @cfg {String} cls
11797      * A CSS class to apply to the button's main element.
11798      */
11799     
11800     /**
11801      * @cfg {Roo.Template} template (Optional)
11802      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11803      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11804      * require code modifications if required elements (e.g. a button) aren't present.
11805      */
11806
11807     // private
11808     render : function(renderTo){
11809         var btn;
11810         if(this.hideParent){
11811             this.parentEl = Roo.get(renderTo);
11812         }
11813         if(!this.dhconfig){
11814             if(!this.template){
11815                 if(!Roo.Button.buttonTemplate){
11816                     // hideous table template
11817                     Roo.Button.buttonTemplate = new Roo.Template(
11818                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11819                         '<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>',
11820                         "</tr></tbody></table>");
11821                 }
11822                 this.template = Roo.Button.buttonTemplate;
11823             }
11824             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11825             var btnEl = btn.child("button:first");
11826             btnEl.on('focus', this.onFocus, this);
11827             btnEl.on('blur', this.onBlur, this);
11828             if(this.cls){
11829                 btn.addClass(this.cls);
11830             }
11831             if(this.icon){
11832                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11833             }
11834             if(this.iconCls){
11835                 btnEl.addClass(this.iconCls);
11836                 if(!this.cls){
11837                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11838                 }
11839             }
11840             if(this.tabIndex !== undefined){
11841                 btnEl.dom.tabIndex = this.tabIndex;
11842             }
11843             if(this.tooltip){
11844                 if(typeof this.tooltip == 'object'){
11845                     Roo.QuickTips.tips(Roo.apply({
11846                           target: btnEl.id
11847                     }, this.tooltip));
11848                 } else {
11849                     btnEl.dom[this.tooltipType] = this.tooltip;
11850                 }
11851             }
11852         }else{
11853             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11854         }
11855         this.el = btn;
11856         if(this.id){
11857             this.el.dom.id = this.el.id = this.id;
11858         }
11859         if(this.menu){
11860             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11861             this.menu.on("show", this.onMenuShow, this);
11862             this.menu.on("hide", this.onMenuHide, this);
11863         }
11864         btn.addClass("x-btn");
11865         if(Roo.isIE && !Roo.isIE7){
11866             this.autoWidth.defer(1, this);
11867         }else{
11868             this.autoWidth();
11869         }
11870         if(this.handleMouseEvents){
11871             btn.on("mouseover", this.onMouseOver, this);
11872             btn.on("mouseout", this.onMouseOut, this);
11873             btn.on("mousedown", this.onMouseDown, this);
11874         }
11875         btn.on(this.clickEvent, this.onClick, this);
11876         //btn.on("mouseup", this.onMouseUp, this);
11877         if(this.hidden){
11878             this.hide();
11879         }
11880         if(this.disabled){
11881             this.disable();
11882         }
11883         Roo.ButtonToggleMgr.register(this);
11884         if(this.pressed){
11885             this.el.addClass("x-btn-pressed");
11886         }
11887         if(this.repeat){
11888             var repeater = new Roo.util.ClickRepeater(btn,
11889                 typeof this.repeat == "object" ? this.repeat : {}
11890             );
11891             repeater.on("click", this.onClick,  this);
11892         }
11893         
11894         this.fireEvent('render', this);
11895         
11896     },
11897     /**
11898      * Returns the button's underlying element
11899      * @return {Roo.Element} The element
11900      */
11901     getEl : function(){
11902         return this.el;  
11903     },
11904     
11905     /**
11906      * Destroys this Button and removes any listeners.
11907      */
11908     destroy : function(){
11909         Roo.ButtonToggleMgr.unregister(this);
11910         this.el.removeAllListeners();
11911         this.purgeListeners();
11912         this.el.remove();
11913     },
11914
11915     // private
11916     autoWidth : function(){
11917         if(this.el){
11918             this.el.setWidth("auto");
11919             if(Roo.isIE7 && Roo.isStrict){
11920                 var ib = this.el.child('button');
11921                 if(ib && ib.getWidth() > 20){
11922                     ib.clip();
11923                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11924                 }
11925             }
11926             if(this.minWidth){
11927                 if(this.hidden){
11928                     this.el.beginMeasure();
11929                 }
11930                 if(this.el.getWidth() < this.minWidth){
11931                     this.el.setWidth(this.minWidth);
11932                 }
11933                 if(this.hidden){
11934                     this.el.endMeasure();
11935                 }
11936             }
11937         }
11938     },
11939
11940     /**
11941      * Assigns this button's click handler
11942      * @param {Function} handler The function to call when the button is clicked
11943      * @param {Object} scope (optional) Scope for the function passed in
11944      */
11945     setHandler : function(handler, scope){
11946         this.handler = handler;
11947         this.scope = scope;  
11948     },
11949     
11950     /**
11951      * Sets this button's text
11952      * @param {String} text The button text
11953      */
11954     setText : function(text){
11955         this.text = text;
11956         if(this.el){
11957             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11958         }
11959         this.autoWidth();
11960     },
11961     
11962     /**
11963      * Gets the text for this button
11964      * @return {String} The button text
11965      */
11966     getText : function(){
11967         return this.text;  
11968     },
11969     
11970     /**
11971      * Show this button
11972      */
11973     show: function(){
11974         this.hidden = false;
11975         if(this.el){
11976             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11977         }
11978     },
11979     
11980     /**
11981      * Hide this button
11982      */
11983     hide: function(){
11984         this.hidden = true;
11985         if(this.el){
11986             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11987         }
11988     },
11989     
11990     /**
11991      * Convenience function for boolean show/hide
11992      * @param {Boolean} visible True to show, false to hide
11993      */
11994     setVisible: function(visible){
11995         if(visible) {
11996             this.show();
11997         }else{
11998             this.hide();
11999         }
12000     },
12001     
12002     /**
12003      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12004      * @param {Boolean} state (optional) Force a particular state
12005      */
12006     toggle : function(state){
12007         state = state === undefined ? !this.pressed : state;
12008         if(state != this.pressed){
12009             if(state){
12010                 this.el.addClass("x-btn-pressed");
12011                 this.pressed = true;
12012                 this.fireEvent("toggle", this, true);
12013             }else{
12014                 this.el.removeClass("x-btn-pressed");
12015                 this.pressed = false;
12016                 this.fireEvent("toggle", this, false);
12017             }
12018             if(this.toggleHandler){
12019                 this.toggleHandler.call(this.scope || this, this, state);
12020             }
12021         }
12022     },
12023     
12024     /**
12025      * Focus the button
12026      */
12027     focus : function(){
12028         this.el.child('button:first').focus();
12029     },
12030     
12031     /**
12032      * Disable this button
12033      */
12034     disable : function(){
12035         if(this.el){
12036             this.el.addClass("x-btn-disabled");
12037         }
12038         this.disabled = true;
12039     },
12040     
12041     /**
12042      * Enable this button
12043      */
12044     enable : function(){
12045         if(this.el){
12046             this.el.removeClass("x-btn-disabled");
12047         }
12048         this.disabled = false;
12049     },
12050
12051     /**
12052      * Convenience function for boolean enable/disable
12053      * @param {Boolean} enabled True to enable, false to disable
12054      */
12055     setDisabled : function(v){
12056         this[v !== true ? "enable" : "disable"]();
12057     },
12058
12059     // private
12060     onClick : function(e){
12061         if(e){
12062             e.preventDefault();
12063         }
12064         if(e.button != 0){
12065             return;
12066         }
12067         if(!this.disabled){
12068             if(this.enableToggle){
12069                 this.toggle();
12070             }
12071             if(this.menu && !this.menu.isVisible()){
12072                 this.menu.show(this.el, this.menuAlign);
12073             }
12074             this.fireEvent("click", this, e);
12075             if(this.handler){
12076                 this.el.removeClass("x-btn-over");
12077                 this.handler.call(this.scope || this, this, e);
12078             }
12079         }
12080     },
12081     // private
12082     onMouseOver : function(e){
12083         if(!this.disabled){
12084             this.el.addClass("x-btn-over");
12085             this.fireEvent('mouseover', this, e);
12086         }
12087     },
12088     // private
12089     onMouseOut : function(e){
12090         if(!e.within(this.el,  true)){
12091             this.el.removeClass("x-btn-over");
12092             this.fireEvent('mouseout', this, e);
12093         }
12094     },
12095     // private
12096     onFocus : function(e){
12097         if(!this.disabled){
12098             this.el.addClass("x-btn-focus");
12099         }
12100     },
12101     // private
12102     onBlur : function(e){
12103         this.el.removeClass("x-btn-focus");
12104     },
12105     // private
12106     onMouseDown : function(e){
12107         if(!this.disabled && e.button == 0){
12108             this.el.addClass("x-btn-click");
12109             Roo.get(document).on('mouseup', this.onMouseUp, this);
12110         }
12111     },
12112     // private
12113     onMouseUp : function(e){
12114         if(e.button == 0){
12115             this.el.removeClass("x-btn-click");
12116             Roo.get(document).un('mouseup', this.onMouseUp, this);
12117         }
12118     },
12119     // private
12120     onMenuShow : function(e){
12121         this.el.addClass("x-btn-menu-active");
12122     },
12123     // private
12124     onMenuHide : function(e){
12125         this.el.removeClass("x-btn-menu-active");
12126     }   
12127 });
12128
12129 // Private utility class used by Button
12130 Roo.ButtonToggleMgr = function(){
12131    var groups = {};
12132    
12133    function toggleGroup(btn, state){
12134        if(state){
12135            var g = groups[btn.toggleGroup];
12136            for(var i = 0, l = g.length; i < l; i++){
12137                if(g[i] != btn){
12138                    g[i].toggle(false);
12139                }
12140            }
12141        }
12142    }
12143    
12144    return {
12145        register : function(btn){
12146            if(!btn.toggleGroup){
12147                return;
12148            }
12149            var g = groups[btn.toggleGroup];
12150            if(!g){
12151                g = groups[btn.toggleGroup] = [];
12152            }
12153            g.push(btn);
12154            btn.on("toggle", toggleGroup);
12155        },
12156        
12157        unregister : function(btn){
12158            if(!btn.toggleGroup){
12159                return;
12160            }
12161            var g = groups[btn.toggleGroup];
12162            if(g){
12163                g.remove(btn);
12164                btn.un("toggle", toggleGroup);
12165            }
12166        }
12167    };
12168 }();/*
12169  * Based on:
12170  * Ext JS Library 1.1.1
12171  * Copyright(c) 2006-2007, Ext JS, LLC.
12172  *
12173  * Originally Released Under LGPL - original licence link has changed is not relivant.
12174  *
12175  * Fork - LGPL
12176  * <script type="text/javascript">
12177  */
12178  
12179 /**
12180  * @class Roo.SplitButton
12181  * @extends Roo.Button
12182  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12183  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12184  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12185  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12186  * @cfg {String} arrowTooltip The title attribute of the arrow
12187  * @constructor
12188  * Create a new menu button
12189  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12190  * @param {Object} config The config object
12191  */
12192 Roo.SplitButton = function(renderTo, config){
12193     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12194     /**
12195      * @event arrowclick
12196      * Fires when this button's arrow is clicked
12197      * @param {SplitButton} this
12198      * @param {EventObject} e The click event
12199      */
12200     this.addEvents({"arrowclick":true});
12201 };
12202
12203 Roo.extend(Roo.SplitButton, Roo.Button, {
12204     render : function(renderTo){
12205         // this is one sweet looking template!
12206         var tpl = new Roo.Template(
12207             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12208             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12209             '<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>',
12210             "</tbody></table></td><td>",
12211             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12212             '<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>',
12213             "</tbody></table></td></tr></table>"
12214         );
12215         var btn = tpl.append(renderTo, [this.text, this.type], true);
12216         var btnEl = btn.child("button");
12217         if(this.cls){
12218             btn.addClass(this.cls);
12219         }
12220         if(this.icon){
12221             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12222         }
12223         if(this.iconCls){
12224             btnEl.addClass(this.iconCls);
12225             if(!this.cls){
12226                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12227             }
12228         }
12229         this.el = btn;
12230         if(this.handleMouseEvents){
12231             btn.on("mouseover", this.onMouseOver, this);
12232             btn.on("mouseout", this.onMouseOut, this);
12233             btn.on("mousedown", this.onMouseDown, this);
12234             btn.on("mouseup", this.onMouseUp, this);
12235         }
12236         btn.on(this.clickEvent, this.onClick, this);
12237         if(this.tooltip){
12238             if(typeof this.tooltip == 'object'){
12239                 Roo.QuickTips.tips(Roo.apply({
12240                       target: btnEl.id
12241                 }, this.tooltip));
12242             } else {
12243                 btnEl.dom[this.tooltipType] = this.tooltip;
12244             }
12245         }
12246         if(this.arrowTooltip){
12247             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12248         }
12249         if(this.hidden){
12250             this.hide();
12251         }
12252         if(this.disabled){
12253             this.disable();
12254         }
12255         if(this.pressed){
12256             this.el.addClass("x-btn-pressed");
12257         }
12258         if(Roo.isIE && !Roo.isIE7){
12259             this.autoWidth.defer(1, this);
12260         }else{
12261             this.autoWidth();
12262         }
12263         if(this.menu){
12264             this.menu.on("show", this.onMenuShow, this);
12265             this.menu.on("hide", this.onMenuHide, this);
12266         }
12267         this.fireEvent('render', this);
12268     },
12269
12270     // private
12271     autoWidth : function(){
12272         if(this.el){
12273             var tbl = this.el.child("table:first");
12274             var tbl2 = this.el.child("table:last");
12275             this.el.setWidth("auto");
12276             tbl.setWidth("auto");
12277             if(Roo.isIE7 && Roo.isStrict){
12278                 var ib = this.el.child('button:first');
12279                 if(ib && ib.getWidth() > 20){
12280                     ib.clip();
12281                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12282                 }
12283             }
12284             if(this.minWidth){
12285                 if(this.hidden){
12286                     this.el.beginMeasure();
12287                 }
12288                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12289                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12290                 }
12291                 if(this.hidden){
12292                     this.el.endMeasure();
12293                 }
12294             }
12295             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12296         } 
12297     },
12298     /**
12299      * Sets this button's click handler
12300      * @param {Function} handler The function to call when the button is clicked
12301      * @param {Object} scope (optional) Scope for the function passed above
12302      */
12303     setHandler : function(handler, scope){
12304         this.handler = handler;
12305         this.scope = scope;  
12306     },
12307     
12308     /**
12309      * Sets this button's arrow click handler
12310      * @param {Function} handler The function to call when the arrow is clicked
12311      * @param {Object} scope (optional) Scope for the function passed above
12312      */
12313     setArrowHandler : function(handler, scope){
12314         this.arrowHandler = handler;
12315         this.scope = scope;  
12316     },
12317     
12318     /**
12319      * Focus the button
12320      */
12321     focus : function(){
12322         if(this.el){
12323             this.el.child("button:first").focus();
12324         }
12325     },
12326
12327     // private
12328     onClick : function(e){
12329         e.preventDefault();
12330         if(!this.disabled){
12331             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12332                 if(this.menu && !this.menu.isVisible()){
12333                     this.menu.show(this.el, this.menuAlign);
12334                 }
12335                 this.fireEvent("arrowclick", this, e);
12336                 if(this.arrowHandler){
12337                     this.arrowHandler.call(this.scope || this, this, e);
12338                 }
12339             }else{
12340                 this.fireEvent("click", this, e);
12341                 if(this.handler){
12342                     this.handler.call(this.scope || this, this, e);
12343                 }
12344             }
12345         }
12346     },
12347     // private
12348     onMouseDown : function(e){
12349         if(!this.disabled){
12350             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12351         }
12352     },
12353     // private
12354     onMouseUp : function(e){
12355         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12356     }   
12357 });
12358
12359
12360 // backwards compat
12361 Roo.MenuButton = Roo.SplitButton;/*
12362  * Based on:
12363  * Ext JS Library 1.1.1
12364  * Copyright(c) 2006-2007, Ext JS, LLC.
12365  *
12366  * Originally Released Under LGPL - original licence link has changed is not relivant.
12367  *
12368  * Fork - LGPL
12369  * <script type="text/javascript">
12370  */
12371
12372 /**
12373  * @class Roo.Toolbar
12374  * Basic Toolbar class.
12375  * @constructor
12376  * Creates a new Toolbar
12377  * @param {Object} container The config object
12378  */ 
12379 Roo.Toolbar = function(container, buttons, config)
12380 {
12381     /// old consturctor format still supported..
12382     if(container instanceof Array){ // omit the container for later rendering
12383         buttons = container;
12384         config = buttons;
12385         container = null;
12386     }
12387     if (typeof(container) == 'object' && container.xtype) {
12388         config = container;
12389         container = config.container;
12390         buttons = config.buttons || []; // not really - use items!!
12391     }
12392     var xitems = [];
12393     if (config && config.items) {
12394         xitems = config.items;
12395         delete config.items;
12396     }
12397     Roo.apply(this, config);
12398     this.buttons = buttons;
12399     
12400     if(container){
12401         this.render(container);
12402     }
12403     this.xitems = xitems;
12404     Roo.each(xitems, function(b) {
12405         this.add(b);
12406     }, this);
12407     
12408 };
12409
12410 Roo.Toolbar.prototype = {
12411     /**
12412      * @cfg {Array} items
12413      * array of button configs or elements to add (will be converted to a MixedCollection)
12414      */
12415     
12416     /**
12417      * @cfg {String/HTMLElement/Element} container
12418      * The id or element that will contain the toolbar
12419      */
12420     // private
12421     render : function(ct){
12422         this.el = Roo.get(ct);
12423         if(this.cls){
12424             this.el.addClass(this.cls);
12425         }
12426         // using a table allows for vertical alignment
12427         // 100% width is needed by Safari...
12428         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12429         this.tr = this.el.child("tr", true);
12430         var autoId = 0;
12431         this.items = new Roo.util.MixedCollection(false, function(o){
12432             return o.id || ("item" + (++autoId));
12433         });
12434         if(this.buttons){
12435             this.add.apply(this, this.buttons);
12436             delete this.buttons;
12437         }
12438     },
12439
12440     /**
12441      * Adds element(s) to the toolbar -- this function takes a variable number of 
12442      * arguments of mixed type and adds them to the toolbar.
12443      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12444      * <ul>
12445      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12446      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12447      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12448      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12449      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12450      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12451      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12452      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12453      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12454      * </ul>
12455      * @param {Mixed} arg2
12456      * @param {Mixed} etc.
12457      */
12458     add : function(){
12459         var a = arguments, l = a.length;
12460         for(var i = 0; i < l; i++){
12461             this._add(a[i]);
12462         }
12463     },
12464     // private..
12465     _add : function(el) {
12466         
12467         if (el.xtype) {
12468             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12469         }
12470         
12471         if (el.applyTo){ // some kind of form field
12472             return this.addField(el);
12473         } 
12474         if (el.render){ // some kind of Toolbar.Item
12475             return this.addItem(el);
12476         }
12477         if (typeof el == "string"){ // string
12478             if(el == "separator" || el == "-"){
12479                 return this.addSeparator();
12480             }
12481             if (el == " "){
12482                 return this.addSpacer();
12483             }
12484             if(el == "->"){
12485                 return this.addFill();
12486             }
12487             return this.addText(el);
12488             
12489         }
12490         if(el.tagName){ // element
12491             return this.addElement(el);
12492         }
12493         if(typeof el == "object"){ // must be button config?
12494             return this.addButton(el);
12495         }
12496         // and now what?!?!
12497         return false;
12498         
12499     },
12500     
12501     /**
12502      * Add an Xtype element
12503      * @param {Object} xtype Xtype Object
12504      * @return {Object} created Object
12505      */
12506     addxtype : function(e){
12507         return this.add(e);  
12508     },
12509     
12510     /**
12511      * Returns the Element for this toolbar.
12512      * @return {Roo.Element}
12513      */
12514     getEl : function(){
12515         return this.el;  
12516     },
12517     
12518     /**
12519      * Adds a separator
12520      * @return {Roo.Toolbar.Item} The separator item
12521      */
12522     addSeparator : function(){
12523         return this.addItem(new Roo.Toolbar.Separator());
12524     },
12525
12526     /**
12527      * Adds a spacer element
12528      * @return {Roo.Toolbar.Spacer} The spacer item
12529      */
12530     addSpacer : function(){
12531         return this.addItem(new Roo.Toolbar.Spacer());
12532     },
12533
12534     /**
12535      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12536      * @return {Roo.Toolbar.Fill} The fill item
12537      */
12538     addFill : function(){
12539         return this.addItem(new Roo.Toolbar.Fill());
12540     },
12541
12542     /**
12543      * Adds any standard HTML element to the toolbar
12544      * @param {String/HTMLElement/Element} el The element or id of the element to add
12545      * @return {Roo.Toolbar.Item} The element's item
12546      */
12547     addElement : function(el){
12548         return this.addItem(new Roo.Toolbar.Item(el));
12549     },
12550     /**
12551      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12552      * @type Roo.util.MixedCollection  
12553      */
12554     items : false,
12555      
12556     /**
12557      * Adds any Toolbar.Item or subclass
12558      * @param {Roo.Toolbar.Item} item
12559      * @return {Roo.Toolbar.Item} The item
12560      */
12561     addItem : function(item){
12562         var td = this.nextBlock();
12563         item.render(td);
12564         this.items.add(item);
12565         return item;
12566     },
12567     
12568     /**
12569      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12570      * @param {Object/Array} config A button config or array of configs
12571      * @return {Roo.Toolbar.Button/Array}
12572      */
12573     addButton : function(config){
12574         if(config instanceof Array){
12575             var buttons = [];
12576             for(var i = 0, len = config.length; i < len; i++) {
12577                 buttons.push(this.addButton(config[i]));
12578             }
12579             return buttons;
12580         }
12581         var b = config;
12582         if(!(config instanceof Roo.Toolbar.Button)){
12583             b = config.split ?
12584                 new Roo.Toolbar.SplitButton(config) :
12585                 new Roo.Toolbar.Button(config);
12586         }
12587         var td = this.nextBlock();
12588         b.render(td);
12589         this.items.add(b);
12590         return b;
12591     },
12592     
12593     /**
12594      * Adds text to the toolbar
12595      * @param {String} text The text to add
12596      * @return {Roo.Toolbar.Item} The element's item
12597      */
12598     addText : function(text){
12599         return this.addItem(new Roo.Toolbar.TextItem(text));
12600     },
12601     
12602     /**
12603      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12604      * @param {Number} index The index where the item is to be inserted
12605      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12606      * @return {Roo.Toolbar.Button/Item}
12607      */
12608     insertButton : function(index, item){
12609         if(item instanceof Array){
12610             var buttons = [];
12611             for(var i = 0, len = item.length; i < len; i++) {
12612                buttons.push(this.insertButton(index + i, item[i]));
12613             }
12614             return buttons;
12615         }
12616         if (!(item instanceof Roo.Toolbar.Button)){
12617            item = new Roo.Toolbar.Button(item);
12618         }
12619         var td = document.createElement("td");
12620         this.tr.insertBefore(td, this.tr.childNodes[index]);
12621         item.render(td);
12622         this.items.insert(index, item);
12623         return item;
12624     },
12625     
12626     /**
12627      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12628      * @param {Object} config
12629      * @return {Roo.Toolbar.Item} The element's item
12630      */
12631     addDom : function(config, returnEl){
12632         var td = this.nextBlock();
12633         Roo.DomHelper.overwrite(td, config);
12634         var ti = new Roo.Toolbar.Item(td.firstChild);
12635         ti.render(td);
12636         this.items.add(ti);
12637         return ti;
12638     },
12639
12640     /**
12641      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12642      * @type Roo.util.MixedCollection  
12643      */
12644     fields : false,
12645     
12646     /**
12647      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12648      * Note: the field should not have been rendered yet. For a field that has already been
12649      * rendered, use {@link #addElement}.
12650      * @param {Roo.form.Field} field
12651      * @return {Roo.ToolbarItem}
12652      */
12653      
12654       
12655     addField : function(field) {
12656         if (!this.fields) {
12657             var autoId = 0;
12658             this.fields = new Roo.util.MixedCollection(false, function(o){
12659                 return o.id || ("item" + (++autoId));
12660             });
12661
12662         }
12663         
12664         var td = this.nextBlock();
12665         field.render(td);
12666         var ti = new Roo.Toolbar.Item(td.firstChild);
12667         ti.render(td);
12668         this.items.add(ti);
12669         this.fields.add(field);
12670         return ti;
12671     },
12672     /**
12673      * Hide the toolbar
12674      * @method hide
12675      */
12676      
12677       
12678     hide : function()
12679     {
12680         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12681         this.el.child('div').hide();
12682     },
12683     /**
12684      * Show the toolbar
12685      * @method show
12686      */
12687     show : function()
12688     {
12689         this.el.child('div').show();
12690     },
12691       
12692     // private
12693     nextBlock : function(){
12694         var td = document.createElement("td");
12695         this.tr.appendChild(td);
12696         return td;
12697     },
12698
12699     // private
12700     destroy : function(){
12701         if(this.items){ // rendered?
12702             Roo.destroy.apply(Roo, this.items.items);
12703         }
12704         if(this.fields){ // rendered?
12705             Roo.destroy.apply(Roo, this.fields.items);
12706         }
12707         Roo.Element.uncache(this.el, this.tr);
12708     }
12709 };
12710
12711 /**
12712  * @class Roo.Toolbar.Item
12713  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12714  * @constructor
12715  * Creates a new Item
12716  * @param {HTMLElement} el 
12717  */
12718 Roo.Toolbar.Item = function(el){
12719     this.el = Roo.getDom(el);
12720     this.id = Roo.id(this.el);
12721     this.hidden = false;
12722 };
12723
12724 Roo.Toolbar.Item.prototype = {
12725     
12726     /**
12727      * Get this item's HTML Element
12728      * @return {HTMLElement}
12729      */
12730     getEl : function(){
12731        return this.el;  
12732     },
12733
12734     // private
12735     render : function(td){
12736         this.td = td;
12737         td.appendChild(this.el);
12738     },
12739     
12740     /**
12741      * Removes and destroys this item.
12742      */
12743     destroy : function(){
12744         this.td.parentNode.removeChild(this.td);
12745     },
12746     
12747     /**
12748      * Shows this item.
12749      */
12750     show: function(){
12751         this.hidden = false;
12752         this.td.style.display = "";
12753     },
12754     
12755     /**
12756      * Hides this item.
12757      */
12758     hide: function(){
12759         this.hidden = true;
12760         this.td.style.display = "none";
12761     },
12762     
12763     /**
12764      * Convenience function for boolean show/hide.
12765      * @param {Boolean} visible true to show/false to hide
12766      */
12767     setVisible: function(visible){
12768         if(visible) {
12769             this.show();
12770         }else{
12771             this.hide();
12772         }
12773     },
12774     
12775     /**
12776      * Try to focus this item.
12777      */
12778     focus : function(){
12779         Roo.fly(this.el).focus();
12780     },
12781     
12782     /**
12783      * Disables this item.
12784      */
12785     disable : function(){
12786         Roo.fly(this.td).addClass("x-item-disabled");
12787         this.disabled = true;
12788         this.el.disabled = true;
12789     },
12790     
12791     /**
12792      * Enables this item.
12793      */
12794     enable : function(){
12795         Roo.fly(this.td).removeClass("x-item-disabled");
12796         this.disabled = false;
12797         this.el.disabled = false;
12798     }
12799 };
12800
12801
12802 /**
12803  * @class Roo.Toolbar.Separator
12804  * @extends Roo.Toolbar.Item
12805  * A simple toolbar separator class
12806  * @constructor
12807  * Creates a new Separator
12808  */
12809 Roo.Toolbar.Separator = function(){
12810     var s = document.createElement("span");
12811     s.className = "ytb-sep";
12812     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12813 };
12814 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12815     enable:Roo.emptyFn,
12816     disable:Roo.emptyFn,
12817     focus:Roo.emptyFn
12818 });
12819
12820 /**
12821  * @class Roo.Toolbar.Spacer
12822  * @extends Roo.Toolbar.Item
12823  * A simple element that adds extra horizontal space to a toolbar.
12824  * @constructor
12825  * Creates a new Spacer
12826  */
12827 Roo.Toolbar.Spacer = function(){
12828     var s = document.createElement("div");
12829     s.className = "ytb-spacer";
12830     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12831 };
12832 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12833     enable:Roo.emptyFn,
12834     disable:Roo.emptyFn,
12835     focus:Roo.emptyFn
12836 });
12837
12838 /**
12839  * @class Roo.Toolbar.Fill
12840  * @extends Roo.Toolbar.Spacer
12841  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12842  * @constructor
12843  * Creates a new Spacer
12844  */
12845 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12846     // private
12847     render : function(td){
12848         td.style.width = '100%';
12849         Roo.Toolbar.Fill.superclass.render.call(this, td);
12850     }
12851 });
12852
12853 /**
12854  * @class Roo.Toolbar.TextItem
12855  * @extends Roo.Toolbar.Item
12856  * A simple class that renders text directly into a toolbar.
12857  * @constructor
12858  * Creates a new TextItem
12859  * @param {String} text
12860  */
12861 Roo.Toolbar.TextItem = function(text){
12862     if (typeof(text) == 'object') {
12863         text = text.text;
12864     }
12865     var s = document.createElement("span");
12866     s.className = "ytb-text";
12867     s.innerHTML = text;
12868     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12869 };
12870 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12871     enable:Roo.emptyFn,
12872     disable:Roo.emptyFn,
12873     focus:Roo.emptyFn
12874 });
12875
12876 /**
12877  * @class Roo.Toolbar.Button
12878  * @extends Roo.Button
12879  * A button that renders into a toolbar.
12880  * @constructor
12881  * Creates a new Button
12882  * @param {Object} config A standard {@link Roo.Button} config object
12883  */
12884 Roo.Toolbar.Button = function(config){
12885     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12886 };
12887 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12888     render : function(td){
12889         this.td = td;
12890         Roo.Toolbar.Button.superclass.render.call(this, td);
12891     },
12892     
12893     /**
12894      * Removes and destroys this button
12895      */
12896     destroy : function(){
12897         Roo.Toolbar.Button.superclass.destroy.call(this);
12898         this.td.parentNode.removeChild(this.td);
12899     },
12900     
12901     /**
12902      * Shows this button
12903      */
12904     show: function(){
12905         this.hidden = false;
12906         this.td.style.display = "";
12907     },
12908     
12909     /**
12910      * Hides this button
12911      */
12912     hide: function(){
12913         this.hidden = true;
12914         this.td.style.display = "none";
12915     },
12916
12917     /**
12918      * Disables this item
12919      */
12920     disable : function(){
12921         Roo.fly(this.td).addClass("x-item-disabled");
12922         this.disabled = true;
12923     },
12924
12925     /**
12926      * Enables this item
12927      */
12928     enable : function(){
12929         Roo.fly(this.td).removeClass("x-item-disabled");
12930         this.disabled = false;
12931     }
12932 });
12933 // backwards compat
12934 Roo.ToolbarButton = Roo.Toolbar.Button;
12935
12936 /**
12937  * @class Roo.Toolbar.SplitButton
12938  * @extends Roo.SplitButton
12939  * A menu button that renders into a toolbar.
12940  * @constructor
12941  * Creates a new SplitButton
12942  * @param {Object} config A standard {@link Roo.SplitButton} config object
12943  */
12944 Roo.Toolbar.SplitButton = function(config){
12945     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12946 };
12947 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12948     render : function(td){
12949         this.td = td;
12950         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12951     },
12952     
12953     /**
12954      * Removes and destroys this button
12955      */
12956     destroy : function(){
12957         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12958         this.td.parentNode.removeChild(this.td);
12959     },
12960     
12961     /**
12962      * Shows this button
12963      */
12964     show: function(){
12965         this.hidden = false;
12966         this.td.style.display = "";
12967     },
12968     
12969     /**
12970      * Hides this button
12971      */
12972     hide: function(){
12973         this.hidden = true;
12974         this.td.style.display = "none";
12975     }
12976 });
12977
12978 // backwards compat
12979 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12980  * Based on:
12981  * Ext JS Library 1.1.1
12982  * Copyright(c) 2006-2007, Ext JS, LLC.
12983  *
12984  * Originally Released Under LGPL - original licence link has changed is not relivant.
12985  *
12986  * Fork - LGPL
12987  * <script type="text/javascript">
12988  */
12989  
12990 /**
12991  * @class Roo.PagingToolbar
12992  * @extends Roo.Toolbar
12993  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12994  * @constructor
12995  * Create a new PagingToolbar
12996  * @param {Object} config The config object
12997  */
12998 Roo.PagingToolbar = function(el, ds, config)
12999 {
13000     // old args format still supported... - xtype is prefered..
13001     if (typeof(el) == 'object' && el.xtype) {
13002         // created from xtype...
13003         config = el;
13004         ds = el.dataSource;
13005         el = config.container;
13006     }
13007     var items = [];
13008     if (config.items) {
13009         items = config.items;
13010         config.items = [];
13011     }
13012     
13013     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13014     this.ds = ds;
13015     this.cursor = 0;
13016     this.renderButtons(this.el);
13017     this.bind(ds);
13018     
13019     // supprot items array.
13020    
13021     Roo.each(items, function(e) {
13022         this.add(Roo.factory(e));
13023     },this);
13024     
13025 };
13026
13027 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13028     /**
13029      * @cfg {Roo.data.Store} dataSource
13030      * The underlying data store providing the paged data
13031      */
13032     /**
13033      * @cfg {String/HTMLElement/Element} container
13034      * container The id or element that will contain the toolbar
13035      */
13036     /**
13037      * @cfg {Boolean} displayInfo
13038      * True to display the displayMsg (defaults to false)
13039      */
13040     /**
13041      * @cfg {Number} pageSize
13042      * The number of records to display per page (defaults to 20)
13043      */
13044     pageSize: 20,
13045     /**
13046      * @cfg {String} displayMsg
13047      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13048      */
13049     displayMsg : 'Displaying {0} - {1} of {2}',
13050     /**
13051      * @cfg {String} emptyMsg
13052      * The message to display when no records are found (defaults to "No data to display")
13053      */
13054     emptyMsg : 'No data to display',
13055     /**
13056      * Customizable piece of the default paging text (defaults to "Page")
13057      * @type String
13058      */
13059     beforePageText : "Page",
13060     /**
13061      * Customizable piece of the default paging text (defaults to "of %0")
13062      * @type String
13063      */
13064     afterPageText : "of {0}",
13065     /**
13066      * Customizable piece of the default paging text (defaults to "First Page")
13067      * @type String
13068      */
13069     firstText : "First Page",
13070     /**
13071      * Customizable piece of the default paging text (defaults to "Previous Page")
13072      * @type String
13073      */
13074     prevText : "Previous Page",
13075     /**
13076      * Customizable piece of the default paging text (defaults to "Next Page")
13077      * @type String
13078      */
13079     nextText : "Next Page",
13080     /**
13081      * Customizable piece of the default paging text (defaults to "Last Page")
13082      * @type String
13083      */
13084     lastText : "Last Page",
13085     /**
13086      * Customizable piece of the default paging text (defaults to "Refresh")
13087      * @type String
13088      */
13089     refreshText : "Refresh",
13090
13091     // private
13092     renderButtons : function(el){
13093         Roo.PagingToolbar.superclass.render.call(this, el);
13094         this.first = this.addButton({
13095             tooltip: this.firstText,
13096             cls: "x-btn-icon x-grid-page-first",
13097             disabled: true,
13098             handler: this.onClick.createDelegate(this, ["first"])
13099         });
13100         this.prev = this.addButton({
13101             tooltip: this.prevText,
13102             cls: "x-btn-icon x-grid-page-prev",
13103             disabled: true,
13104             handler: this.onClick.createDelegate(this, ["prev"])
13105         });
13106         //this.addSeparator();
13107         this.add(this.beforePageText);
13108         this.field = Roo.get(this.addDom({
13109            tag: "input",
13110            type: "text",
13111            size: "3",
13112            value: "1",
13113            cls: "x-grid-page-number"
13114         }).el);
13115         this.field.on("keydown", this.onPagingKeydown, this);
13116         this.field.on("focus", function(){this.dom.select();});
13117         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13118         this.field.setHeight(18);
13119         //this.addSeparator();
13120         this.next = this.addButton({
13121             tooltip: this.nextText,
13122             cls: "x-btn-icon x-grid-page-next",
13123             disabled: true,
13124             handler: this.onClick.createDelegate(this, ["next"])
13125         });
13126         this.last = this.addButton({
13127             tooltip: this.lastText,
13128             cls: "x-btn-icon x-grid-page-last",
13129             disabled: true,
13130             handler: this.onClick.createDelegate(this, ["last"])
13131         });
13132         //this.addSeparator();
13133         this.loading = this.addButton({
13134             tooltip: this.refreshText,
13135             cls: "x-btn-icon x-grid-loading",
13136             handler: this.onClick.createDelegate(this, ["refresh"])
13137         });
13138
13139         if(this.displayInfo){
13140             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13141         }
13142     },
13143
13144     // private
13145     updateInfo : function(){
13146         if(this.displayEl){
13147             var count = this.ds.getCount();
13148             var msg = count == 0 ?
13149                 this.emptyMsg :
13150                 String.format(
13151                     this.displayMsg,
13152                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13153                 );
13154             this.displayEl.update(msg);
13155         }
13156     },
13157
13158     // private
13159     onLoad : function(ds, r, o){
13160        this.cursor = o.params ? o.params.start : 0;
13161        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13162
13163        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13164        this.field.dom.value = ap;
13165        this.first.setDisabled(ap == 1);
13166        this.prev.setDisabled(ap == 1);
13167        this.next.setDisabled(ap == ps);
13168        this.last.setDisabled(ap == ps);
13169        this.loading.enable();
13170        this.updateInfo();
13171     },
13172
13173     // private
13174     getPageData : function(){
13175         var total = this.ds.getTotalCount();
13176         return {
13177             total : total,
13178             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13179             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13180         };
13181     },
13182
13183     // private
13184     onLoadError : function(){
13185         this.loading.enable();
13186     },
13187
13188     // private
13189     onPagingKeydown : function(e){
13190         var k = e.getKey();
13191         var d = this.getPageData();
13192         if(k == e.RETURN){
13193             var v = this.field.dom.value, pageNum;
13194             if(!v || isNaN(pageNum = parseInt(v, 10))){
13195                 this.field.dom.value = d.activePage;
13196                 return;
13197             }
13198             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13199             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13200             e.stopEvent();
13201         }
13202         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))
13203         {
13204           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13205           this.field.dom.value = pageNum;
13206           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13207           e.stopEvent();
13208         }
13209         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13210         {
13211           var v = this.field.dom.value, pageNum; 
13212           var increment = (e.shiftKey) ? 10 : 1;
13213           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13214             increment *= -1;
13215           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13216             this.field.dom.value = d.activePage;
13217             return;
13218           }
13219           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13220           {
13221             this.field.dom.value = parseInt(v, 10) + increment;
13222             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13223             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13224           }
13225           e.stopEvent();
13226         }
13227     },
13228
13229     // private
13230     beforeLoad : function(){
13231         if(this.loading){
13232             this.loading.disable();
13233         }
13234     },
13235
13236     // private
13237     onClick : function(which){
13238         var ds = this.ds;
13239         switch(which){
13240             case "first":
13241                 ds.load({params:{start: 0, limit: this.pageSize}});
13242             break;
13243             case "prev":
13244                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13245             break;
13246             case "next":
13247                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13248             break;
13249             case "last":
13250                 var total = ds.getTotalCount();
13251                 var extra = total % this.pageSize;
13252                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13253                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13254             break;
13255             case "refresh":
13256                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13257             break;
13258         }
13259     },
13260
13261     /**
13262      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13263      * @param {Roo.data.Store} store The data store to unbind
13264      */
13265     unbind : function(ds){
13266         ds.un("beforeload", this.beforeLoad, this);
13267         ds.un("load", this.onLoad, this);
13268         ds.un("loadexception", this.onLoadError, this);
13269         ds.un("remove", this.updateInfo, this);
13270         ds.un("add", this.updateInfo, this);
13271         this.ds = undefined;
13272     },
13273
13274     /**
13275      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13276      * @param {Roo.data.Store} store The data store to bind
13277      */
13278     bind : function(ds){
13279         ds.on("beforeload", this.beforeLoad, this);
13280         ds.on("load", this.onLoad, this);
13281         ds.on("loadexception", this.onLoadError, this);
13282         ds.on("remove", this.updateInfo, this);
13283         ds.on("add", this.updateInfo, this);
13284         this.ds = ds;
13285     }
13286 });/*
13287  * Based on:
13288  * Ext JS Library 1.1.1
13289  * Copyright(c) 2006-2007, Ext JS, LLC.
13290  *
13291  * Originally Released Under LGPL - original licence link has changed is not relivant.
13292  *
13293  * Fork - LGPL
13294  * <script type="text/javascript">
13295  */
13296
13297 /**
13298  * @class Roo.Resizable
13299  * @extends Roo.util.Observable
13300  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13301  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13302  * 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
13303  * the element will be wrapped for you automatically.</p>
13304  * <p>Here is the list of valid resize handles:</p>
13305  * <pre>
13306 Value   Description
13307 ------  -------------------
13308  'n'     north
13309  's'     south
13310  'e'     east
13311  'w'     west
13312  'nw'    northwest
13313  'sw'    southwest
13314  'se'    southeast
13315  'ne'    northeast
13316  'hd'    horizontal drag
13317  'all'   all
13318 </pre>
13319  * <p>Here's an example showing the creation of a typical Resizable:</p>
13320  * <pre><code>
13321 var resizer = new Roo.Resizable("element-id", {
13322     handles: 'all',
13323     minWidth: 200,
13324     minHeight: 100,
13325     maxWidth: 500,
13326     maxHeight: 400,
13327     pinned: true
13328 });
13329 resizer.on("resize", myHandler);
13330 </code></pre>
13331  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13332  * resizer.east.setDisplayed(false);</p>
13333  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13334  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13335  * resize operation's new size (defaults to [0, 0])
13336  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13337  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13338  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13339  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13340  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13341  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13342  * @cfg {Number} width The width of the element in pixels (defaults to null)
13343  * @cfg {Number} height The height of the element in pixels (defaults to null)
13344  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13345  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13346  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13347  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13348  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13349  * in favor of the handles config option (defaults to false)
13350  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13351  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13352  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13353  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13354  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13355  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13356  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13357  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13358  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13359  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13360  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13361  * @constructor
13362  * Create a new resizable component
13363  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13364  * @param {Object} config configuration options
13365   */
13366 Roo.Resizable = function(el, config)
13367 {
13368     this.el = Roo.get(el);
13369
13370     if(config && config.wrap){
13371         config.resizeChild = this.el;
13372         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13373         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13374         this.el.setStyle("overflow", "hidden");
13375         this.el.setPositioning(config.resizeChild.getPositioning());
13376         config.resizeChild.clearPositioning();
13377         if(!config.width || !config.height){
13378             var csize = config.resizeChild.getSize();
13379             this.el.setSize(csize.width, csize.height);
13380         }
13381         if(config.pinned && !config.adjustments){
13382             config.adjustments = "auto";
13383         }
13384     }
13385
13386     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13387     this.proxy.unselectable();
13388     this.proxy.enableDisplayMode('block');
13389
13390     Roo.apply(this, config);
13391
13392     if(this.pinned){
13393         this.disableTrackOver = true;
13394         this.el.addClass("x-resizable-pinned");
13395     }
13396     // if the element isn't positioned, make it relative
13397     var position = this.el.getStyle("position");
13398     if(position != "absolute" && position != "fixed"){
13399         this.el.setStyle("position", "relative");
13400     }
13401     if(!this.handles){ // no handles passed, must be legacy style
13402         this.handles = 's,e,se';
13403         if(this.multiDirectional){
13404             this.handles += ',n,w';
13405         }
13406     }
13407     if(this.handles == "all"){
13408         this.handles = "n s e w ne nw se sw";
13409     }
13410     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13411     var ps = Roo.Resizable.positions;
13412     for(var i = 0, len = hs.length; i < len; i++){
13413         if(hs[i] && ps[hs[i]]){
13414             var pos = ps[hs[i]];
13415             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13416         }
13417     }
13418     // legacy
13419     this.corner = this.southeast;
13420     
13421     // updateBox = the box can move..
13422     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13423         this.updateBox = true;
13424     }
13425
13426     this.activeHandle = null;
13427
13428     if(this.resizeChild){
13429         if(typeof this.resizeChild == "boolean"){
13430             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13431         }else{
13432             this.resizeChild = Roo.get(this.resizeChild, true);
13433         }
13434     }
13435     
13436     if(this.adjustments == "auto"){
13437         var rc = this.resizeChild;
13438         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13439         if(rc && (hw || hn)){
13440             rc.position("relative");
13441             rc.setLeft(hw ? hw.el.getWidth() : 0);
13442             rc.setTop(hn ? hn.el.getHeight() : 0);
13443         }
13444         this.adjustments = [
13445             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13446             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13447         ];
13448     }
13449
13450     if(this.draggable){
13451         this.dd = this.dynamic ?
13452             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13453         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13454     }
13455
13456     // public events
13457     this.addEvents({
13458         /**
13459          * @event beforeresize
13460          * Fired before resize is allowed. Set enabled to false to cancel resize.
13461          * @param {Roo.Resizable} this
13462          * @param {Roo.EventObject} e The mousedown event
13463          */
13464         "beforeresize" : true,
13465         /**
13466          * @event resize
13467          * Fired after a resize.
13468          * @param {Roo.Resizable} this
13469          * @param {Number} width The new width
13470          * @param {Number} height The new height
13471          * @param {Roo.EventObject} e The mouseup event
13472          */
13473         "resize" : true
13474     });
13475
13476     if(this.width !== null && this.height !== null){
13477         this.resizeTo(this.width, this.height);
13478     }else{
13479         this.updateChildSize();
13480     }
13481     if(Roo.isIE){
13482         this.el.dom.style.zoom = 1;
13483     }
13484     Roo.Resizable.superclass.constructor.call(this);
13485 };
13486
13487 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13488         resizeChild : false,
13489         adjustments : [0, 0],
13490         minWidth : 5,
13491         minHeight : 5,
13492         maxWidth : 10000,
13493         maxHeight : 10000,
13494         enabled : true,
13495         animate : false,
13496         duration : .35,
13497         dynamic : false,
13498         handles : false,
13499         multiDirectional : false,
13500         disableTrackOver : false,
13501         easing : 'easeOutStrong',
13502         widthIncrement : 0,
13503         heightIncrement : 0,
13504         pinned : false,
13505         width : null,
13506         height : null,
13507         preserveRatio : false,
13508         transparent: false,
13509         minX: 0,
13510         minY: 0,
13511         draggable: false,
13512
13513         /**
13514          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13515          */
13516         constrainTo: undefined,
13517         /**
13518          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13519          */
13520         resizeRegion: undefined,
13521
13522
13523     /**
13524      * Perform a manual resize
13525      * @param {Number} width
13526      * @param {Number} height
13527      */
13528     resizeTo : function(width, height){
13529         this.el.setSize(width, height);
13530         this.updateChildSize();
13531         this.fireEvent("resize", this, width, height, null);
13532     },
13533
13534     // private
13535     startSizing : function(e, handle){
13536         this.fireEvent("beforeresize", this, e);
13537         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13538
13539             if(!this.overlay){
13540                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13541                 this.overlay.unselectable();
13542                 this.overlay.enableDisplayMode("block");
13543                 this.overlay.on("mousemove", this.onMouseMove, this);
13544                 this.overlay.on("mouseup", this.onMouseUp, this);
13545             }
13546             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13547
13548             this.resizing = true;
13549             this.startBox = this.el.getBox();
13550             this.startPoint = e.getXY();
13551             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13552                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13553
13554             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13555             this.overlay.show();
13556
13557             if(this.constrainTo) {
13558                 var ct = Roo.get(this.constrainTo);
13559                 this.resizeRegion = ct.getRegion().adjust(
13560                     ct.getFrameWidth('t'),
13561                     ct.getFrameWidth('l'),
13562                     -ct.getFrameWidth('b'),
13563                     -ct.getFrameWidth('r')
13564                 );
13565             }
13566
13567             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13568             this.proxy.show();
13569             this.proxy.setBox(this.startBox);
13570             if(!this.dynamic){
13571                 this.proxy.setStyle('visibility', 'visible');
13572             }
13573         }
13574     },
13575
13576     // private
13577     onMouseDown : function(handle, e){
13578         if(this.enabled){
13579             e.stopEvent();
13580             this.activeHandle = handle;
13581             this.startSizing(e, handle);
13582         }
13583     },
13584
13585     // private
13586     onMouseUp : function(e){
13587         var size = this.resizeElement();
13588         this.resizing = false;
13589         this.handleOut();
13590         this.overlay.hide();
13591         this.proxy.hide();
13592         this.fireEvent("resize", this, size.width, size.height, e);
13593     },
13594
13595     // private
13596     updateChildSize : function(){
13597         if(this.resizeChild){
13598             var el = this.el;
13599             var child = this.resizeChild;
13600             var adj = this.adjustments;
13601             if(el.dom.offsetWidth){
13602                 var b = el.getSize(true);
13603                 child.setSize(b.width+adj[0], b.height+adj[1]);
13604             }
13605             // Second call here for IE
13606             // The first call enables instant resizing and
13607             // the second call corrects scroll bars if they
13608             // exist
13609             if(Roo.isIE){
13610                 setTimeout(function(){
13611                     if(el.dom.offsetWidth){
13612                         var b = el.getSize(true);
13613                         child.setSize(b.width+adj[0], b.height+adj[1]);
13614                     }
13615                 }, 10);
13616             }
13617         }
13618     },
13619
13620     // private
13621     snap : function(value, inc, min){
13622         if(!inc || !value) return value;
13623         var newValue = value;
13624         var m = value % inc;
13625         if(m > 0){
13626             if(m > (inc/2)){
13627                 newValue = value + (inc-m);
13628             }else{
13629                 newValue = value - m;
13630             }
13631         }
13632         return Math.max(min, newValue);
13633     },
13634
13635     // private
13636     resizeElement : function(){
13637         var box = this.proxy.getBox();
13638         if(this.updateBox){
13639             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13640         }else{
13641             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13642         }
13643         this.updateChildSize();
13644         if(!this.dynamic){
13645             this.proxy.hide();
13646         }
13647         return box;
13648     },
13649
13650     // private
13651     constrain : function(v, diff, m, mx){
13652         if(v - diff < m){
13653             diff = v - m;
13654         }else if(v - diff > mx){
13655             diff = mx - v;
13656         }
13657         return diff;
13658     },
13659
13660     // private
13661     onMouseMove : function(e){
13662         if(this.enabled){
13663             try{// try catch so if something goes wrong the user doesn't get hung
13664
13665             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13666                 return;
13667             }
13668
13669             //var curXY = this.startPoint;
13670             var curSize = this.curSize || this.startBox;
13671             var x = this.startBox.x, y = this.startBox.y;
13672             var ox = x, oy = y;
13673             var w = curSize.width, h = curSize.height;
13674             var ow = w, oh = h;
13675             var mw = this.minWidth, mh = this.minHeight;
13676             var mxw = this.maxWidth, mxh = this.maxHeight;
13677             var wi = this.widthIncrement;
13678             var hi = this.heightIncrement;
13679
13680             var eventXY = e.getXY();
13681             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13682             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13683
13684             var pos = this.activeHandle.position;
13685
13686             switch(pos){
13687                 case "east":
13688                     w += diffX;
13689                     w = Math.min(Math.max(mw, w), mxw);
13690                     break;
13691              
13692                 case "south":
13693                     h += diffY;
13694                     h = Math.min(Math.max(mh, h), mxh);
13695                     break;
13696                 case "southeast":
13697                     w += diffX;
13698                     h += diffY;
13699                     w = Math.min(Math.max(mw, w), mxw);
13700                     h = Math.min(Math.max(mh, h), mxh);
13701                     break;
13702                 case "north":
13703                     diffY = this.constrain(h, diffY, mh, mxh);
13704                     y += diffY;
13705                     h -= diffY;
13706                     break;
13707                 case "hdrag":
13708                     
13709                     if (wi) {
13710                         var adiffX = Math.abs(diffX);
13711                         var sub = (adiffX % wi); // how much 
13712                         if (sub > (wi/2)) { // far enough to snap
13713                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13714                         } else {
13715                             // remove difference.. 
13716                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13717                         }
13718                     }
13719                     x += diffX;
13720                     x = Math.max(this.minX, x);
13721                     break;
13722                 case "west":
13723                     diffX = this.constrain(w, diffX, mw, mxw);
13724                     x += diffX;
13725                     w -= diffX;
13726                     break;
13727                 case "northeast":
13728                     w += diffX;
13729                     w = Math.min(Math.max(mw, w), mxw);
13730                     diffY = this.constrain(h, diffY, mh, mxh);
13731                     y += diffY;
13732                     h -= diffY;
13733                     break;
13734                 case "northwest":
13735                     diffX = this.constrain(w, diffX, mw, mxw);
13736                     diffY = this.constrain(h, diffY, mh, mxh);
13737                     y += diffY;
13738                     h -= diffY;
13739                     x += diffX;
13740                     w -= diffX;
13741                     break;
13742                case "southwest":
13743                     diffX = this.constrain(w, diffX, mw, mxw);
13744                     h += diffY;
13745                     h = Math.min(Math.max(mh, h), mxh);
13746                     x += diffX;
13747                     w -= diffX;
13748                     break;
13749             }
13750
13751             var sw = this.snap(w, wi, mw);
13752             var sh = this.snap(h, hi, mh);
13753             if(sw != w || sh != h){
13754                 switch(pos){
13755                     case "northeast":
13756                         y -= sh - h;
13757                     break;
13758                     case "north":
13759                         y -= sh - h;
13760                         break;
13761                     case "southwest":
13762                         x -= sw - w;
13763                     break;
13764                     case "west":
13765                         x -= sw - w;
13766                         break;
13767                     case "northwest":
13768                         x -= sw - w;
13769                         y -= sh - h;
13770                     break;
13771                 }
13772                 w = sw;
13773                 h = sh;
13774             }
13775
13776             if(this.preserveRatio){
13777                 switch(pos){
13778                     case "southeast":
13779                     case "east":
13780                         h = oh * (w/ow);
13781                         h = Math.min(Math.max(mh, h), mxh);
13782                         w = ow * (h/oh);
13783                        break;
13784                     case "south":
13785                         w = ow * (h/oh);
13786                         w = Math.min(Math.max(mw, w), mxw);
13787                         h = oh * (w/ow);
13788                         break;
13789                     case "northeast":
13790                         w = ow * (h/oh);
13791                         w = Math.min(Math.max(mw, w), mxw);
13792                         h = oh * (w/ow);
13793                     break;
13794                     case "north":
13795                         var tw = w;
13796                         w = ow * (h/oh);
13797                         w = Math.min(Math.max(mw, w), mxw);
13798                         h = oh * (w/ow);
13799                         x += (tw - w) / 2;
13800                         break;
13801                     case "southwest":
13802                         h = oh * (w/ow);
13803                         h = Math.min(Math.max(mh, h), mxh);
13804                         var tw = w;
13805                         w = ow * (h/oh);
13806                         x += tw - w;
13807                         break;
13808                     case "west":
13809                         var th = h;
13810                         h = oh * (w/ow);
13811                         h = Math.min(Math.max(mh, h), mxh);
13812                         y += (th - h) / 2;
13813                         var tw = w;
13814                         w = ow * (h/oh);
13815                         x += tw - w;
13816                        break;
13817                     case "northwest":
13818                         var tw = w;
13819                         var th = h;
13820                         h = oh * (w/ow);
13821                         h = Math.min(Math.max(mh, h), mxh);
13822                         w = ow * (h/oh);
13823                         y += th - h;
13824                         x += tw - w;
13825                        break;
13826
13827                 }
13828             }
13829             if (pos == 'hdrag') {
13830                 w = ow;
13831             }
13832             this.proxy.setBounds(x, y, w, h);
13833             if(this.dynamic){
13834                 this.resizeElement();
13835             }
13836             }catch(e){}
13837         }
13838     },
13839
13840     // private
13841     handleOver : function(){
13842         if(this.enabled){
13843             this.el.addClass("x-resizable-over");
13844         }
13845     },
13846
13847     // private
13848     handleOut : function(){
13849         if(!this.resizing){
13850             this.el.removeClass("x-resizable-over");
13851         }
13852     },
13853
13854     /**
13855      * Returns the element this component is bound to.
13856      * @return {Roo.Element}
13857      */
13858     getEl : function(){
13859         return this.el;
13860     },
13861
13862     /**
13863      * Returns the resizeChild element (or null).
13864      * @return {Roo.Element}
13865      */
13866     getResizeChild : function(){
13867         return this.resizeChild;
13868     },
13869
13870     /**
13871      * Destroys this resizable. If the element was wrapped and
13872      * removeEl is not true then the element remains.
13873      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13874      */
13875     destroy : function(removeEl){
13876         this.proxy.remove();
13877         if(this.overlay){
13878             this.overlay.removeAllListeners();
13879             this.overlay.remove();
13880         }
13881         var ps = Roo.Resizable.positions;
13882         for(var k in ps){
13883             if(typeof ps[k] != "function" && this[ps[k]]){
13884                 var h = this[ps[k]];
13885                 h.el.removeAllListeners();
13886                 h.el.remove();
13887             }
13888         }
13889         if(removeEl){
13890             this.el.update("");
13891             this.el.remove();
13892         }
13893     }
13894 });
13895
13896 // private
13897 // hash to map config positions to true positions
13898 Roo.Resizable.positions = {
13899     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13900     hd: "hdrag"
13901 };
13902
13903 // private
13904 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13905     if(!this.tpl){
13906         // only initialize the template if resizable is used
13907         var tpl = Roo.DomHelper.createTemplate(
13908             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13909         );
13910         tpl.compile();
13911         Roo.Resizable.Handle.prototype.tpl = tpl;
13912     }
13913     this.position = pos;
13914     this.rz = rz;
13915     // show north drag fro topdra
13916     var handlepos = pos == 'hdrag' ? 'north' : pos;
13917     
13918     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13919     if (pos == 'hdrag') {
13920         this.el.setStyle('cursor', 'pointer');
13921     }
13922     this.el.unselectable();
13923     if(transparent){
13924         this.el.setOpacity(0);
13925     }
13926     this.el.on("mousedown", this.onMouseDown, this);
13927     if(!disableTrackOver){
13928         this.el.on("mouseover", this.onMouseOver, this);
13929         this.el.on("mouseout", this.onMouseOut, this);
13930     }
13931 };
13932
13933 // private
13934 Roo.Resizable.Handle.prototype = {
13935     afterResize : function(rz){
13936         // do nothing
13937     },
13938     // private
13939     onMouseDown : function(e){
13940         this.rz.onMouseDown(this, e);
13941     },
13942     // private
13943     onMouseOver : function(e){
13944         this.rz.handleOver(this, e);
13945     },
13946     // private
13947     onMouseOut : function(e){
13948         this.rz.handleOut(this, e);
13949     }
13950 };/*
13951  * Based on:
13952  * Ext JS Library 1.1.1
13953  * Copyright(c) 2006-2007, Ext JS, LLC.
13954  *
13955  * Originally Released Under LGPL - original licence link has changed is not relivant.
13956  *
13957  * Fork - LGPL
13958  * <script type="text/javascript">
13959  */
13960
13961 /**
13962  * @class Roo.Editor
13963  * @extends Roo.Component
13964  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13965  * @constructor
13966  * Create a new Editor
13967  * @param {Roo.form.Field} field The Field object (or descendant)
13968  * @param {Object} config The config object
13969  */
13970 Roo.Editor = function(field, config){
13971     Roo.Editor.superclass.constructor.call(this, config);
13972     this.field = field;
13973     this.addEvents({
13974         /**
13975              * @event beforestartedit
13976              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13977              * false from the handler of this event.
13978              * @param {Editor} this
13979              * @param {Roo.Element} boundEl The underlying element bound to this editor
13980              * @param {Mixed} value The field value being set
13981              */
13982         "beforestartedit" : true,
13983         /**
13984              * @event startedit
13985              * Fires when this editor is displayed
13986              * @param {Roo.Element} boundEl The underlying element bound to this editor
13987              * @param {Mixed} value The starting field value
13988              */
13989         "startedit" : true,
13990         /**
13991              * @event beforecomplete
13992              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13993              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13994              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13995              * event will not fire since no edit actually occurred.
13996              * @param {Editor} this
13997              * @param {Mixed} value The current field value
13998              * @param {Mixed} startValue The original field value
13999              */
14000         "beforecomplete" : true,
14001         /**
14002              * @event complete
14003              * Fires after editing is complete and any changed value has been written to the underlying field.
14004              * @param {Editor} this
14005              * @param {Mixed} value The current field value
14006              * @param {Mixed} startValue The original field value
14007              */
14008         "complete" : true,
14009         /**
14010          * @event specialkey
14011          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14012          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14013          * @param {Roo.form.Field} this
14014          * @param {Roo.EventObject} e The event object
14015          */
14016         "specialkey" : true
14017     });
14018 };
14019
14020 Roo.extend(Roo.Editor, Roo.Component, {
14021     /**
14022      * @cfg {Boolean/String} autosize
14023      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14024      * or "height" to adopt the height only (defaults to false)
14025      */
14026     /**
14027      * @cfg {Boolean} revertInvalid
14028      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14029      * validation fails (defaults to true)
14030      */
14031     /**
14032      * @cfg {Boolean} ignoreNoChange
14033      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14034      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14035      * will never be ignored.
14036      */
14037     /**
14038      * @cfg {Boolean} hideEl
14039      * False to keep the bound element visible while the editor is displayed (defaults to true)
14040      */
14041     /**
14042      * @cfg {Mixed} value
14043      * The data value of the underlying field (defaults to "")
14044      */
14045     value : "",
14046     /**
14047      * @cfg {String} alignment
14048      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14049      */
14050     alignment: "c-c?",
14051     /**
14052      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14053      * for bottom-right shadow (defaults to "frame")
14054      */
14055     shadow : "frame",
14056     /**
14057      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14058      */
14059     constrain : false,
14060     /**
14061      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14062      */
14063     completeOnEnter : false,
14064     /**
14065      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14066      */
14067     cancelOnEsc : false,
14068     /**
14069      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14070      */
14071     updateEl : false,
14072
14073     // private
14074     onRender : function(ct, position){
14075         this.el = new Roo.Layer({
14076             shadow: this.shadow,
14077             cls: "x-editor",
14078             parentEl : ct,
14079             shim : this.shim,
14080             shadowOffset:4,
14081             id: this.id,
14082             constrain: this.constrain
14083         });
14084         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14085         if(this.field.msgTarget != 'title'){
14086             this.field.msgTarget = 'qtip';
14087         }
14088         this.field.render(this.el);
14089         if(Roo.isGecko){
14090             this.field.el.dom.setAttribute('autocomplete', 'off');
14091         }
14092         this.field.on("specialkey", this.onSpecialKey, this);
14093         if(this.swallowKeys){
14094             this.field.el.swallowEvent(['keydown','keypress']);
14095         }
14096         this.field.show();
14097         this.field.on("blur", this.onBlur, this);
14098         if(this.field.grow){
14099             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14100         }
14101     },
14102
14103     onSpecialKey : function(field, e)
14104     {
14105         //Roo.log('editor onSpecialKey');
14106         if(this.completeOnEnter && e.getKey() == e.ENTER){
14107             e.stopEvent();
14108             this.completeEdit();
14109             return;
14110         }
14111         // do not fire special key otherwise it might hide close the editor...
14112         if(e.getKey() == e.ENTER){    
14113             return;
14114         }
14115         if(this.cancelOnEsc && e.getKey() == e.ESC){
14116             this.cancelEdit();
14117             return;
14118         } 
14119         this.fireEvent('specialkey', field, e);
14120     
14121     },
14122
14123     /**
14124      * Starts the editing process and shows the editor.
14125      * @param {String/HTMLElement/Element} el The element to edit
14126      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14127       * to the innerHTML of el.
14128      */
14129     startEdit : function(el, value){
14130         if(this.editing){
14131             this.completeEdit();
14132         }
14133         this.boundEl = Roo.get(el);
14134         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14135         if(!this.rendered){
14136             this.render(this.parentEl || document.body);
14137         }
14138         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14139             return;
14140         }
14141         this.startValue = v;
14142         this.field.setValue(v);
14143         if(this.autoSize){
14144             var sz = this.boundEl.getSize();
14145             switch(this.autoSize){
14146                 case "width":
14147                 this.setSize(sz.width,  "");
14148                 break;
14149                 case "height":
14150                 this.setSize("",  sz.height);
14151                 break;
14152                 default:
14153                 this.setSize(sz.width,  sz.height);
14154             }
14155         }
14156         this.el.alignTo(this.boundEl, this.alignment);
14157         this.editing = true;
14158         if(Roo.QuickTips){
14159             Roo.QuickTips.disable();
14160         }
14161         this.show();
14162     },
14163
14164     /**
14165      * Sets the height and width of this editor.
14166      * @param {Number} width The new width
14167      * @param {Number} height The new height
14168      */
14169     setSize : function(w, h){
14170         this.field.setSize(w, h);
14171         if(this.el){
14172             this.el.sync();
14173         }
14174     },
14175
14176     /**
14177      * Realigns the editor to the bound field based on the current alignment config value.
14178      */
14179     realign : function(){
14180         this.el.alignTo(this.boundEl, this.alignment);
14181     },
14182
14183     /**
14184      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14185      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14186      */
14187     completeEdit : function(remainVisible){
14188         if(!this.editing){
14189             return;
14190         }
14191         var v = this.getValue();
14192         if(this.revertInvalid !== false && !this.field.isValid()){
14193             v = this.startValue;
14194             this.cancelEdit(true);
14195         }
14196         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14197             this.editing = false;
14198             this.hide();
14199             return;
14200         }
14201         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14202             this.editing = false;
14203             if(this.updateEl && this.boundEl){
14204                 this.boundEl.update(v);
14205             }
14206             if(remainVisible !== true){
14207                 this.hide();
14208             }
14209             this.fireEvent("complete", this, v, this.startValue);
14210         }
14211     },
14212
14213     // private
14214     onShow : function(){
14215         this.el.show();
14216         if(this.hideEl !== false){
14217             this.boundEl.hide();
14218         }
14219         this.field.show();
14220         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14221             this.fixIEFocus = true;
14222             this.deferredFocus.defer(50, this);
14223         }else{
14224             this.field.focus();
14225         }
14226         this.fireEvent("startedit", this.boundEl, this.startValue);
14227     },
14228
14229     deferredFocus : function(){
14230         if(this.editing){
14231             this.field.focus();
14232         }
14233     },
14234
14235     /**
14236      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14237      * reverted to the original starting value.
14238      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14239      * cancel (defaults to false)
14240      */
14241     cancelEdit : function(remainVisible){
14242         if(this.editing){
14243             this.setValue(this.startValue);
14244             if(remainVisible !== true){
14245                 this.hide();
14246             }
14247         }
14248     },
14249
14250     // private
14251     onBlur : function(){
14252         if(this.allowBlur !== true && this.editing){
14253             this.completeEdit();
14254         }
14255     },
14256
14257     // private
14258     onHide : function(){
14259         if(this.editing){
14260             this.completeEdit();
14261             return;
14262         }
14263         this.field.blur();
14264         if(this.field.collapse){
14265             this.field.collapse();
14266         }
14267         this.el.hide();
14268         if(this.hideEl !== false){
14269             this.boundEl.show();
14270         }
14271         if(Roo.QuickTips){
14272             Roo.QuickTips.enable();
14273         }
14274     },
14275
14276     /**
14277      * Sets the data value of the editor
14278      * @param {Mixed} value Any valid value supported by the underlying field
14279      */
14280     setValue : function(v){
14281         this.field.setValue(v);
14282     },
14283
14284     /**
14285      * Gets the data value of the editor
14286      * @return {Mixed} The data value
14287      */
14288     getValue : function(){
14289         return this.field.getValue();
14290     }
14291 });/*
14292  * Based on:
14293  * Ext JS Library 1.1.1
14294  * Copyright(c) 2006-2007, Ext JS, LLC.
14295  *
14296  * Originally Released Under LGPL - original licence link has changed is not relivant.
14297  *
14298  * Fork - LGPL
14299  * <script type="text/javascript">
14300  */
14301  
14302 /**
14303  * @class Roo.BasicDialog
14304  * @extends Roo.util.Observable
14305  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14306  * <pre><code>
14307 var dlg = new Roo.BasicDialog("my-dlg", {
14308     height: 200,
14309     width: 300,
14310     minHeight: 100,
14311     minWidth: 150,
14312     modal: true,
14313     proxyDrag: true,
14314     shadow: true
14315 });
14316 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14317 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14318 dlg.addButton('Cancel', dlg.hide, dlg);
14319 dlg.show();
14320 </code></pre>
14321   <b>A Dialog should always be a direct child of the body element.</b>
14322  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14323  * @cfg {String} title Default text to display in the title bar (defaults to null)
14324  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14325  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14326  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14327  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14328  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14329  * (defaults to null with no animation)
14330  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14331  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14332  * property for valid values (defaults to 'all')
14333  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14334  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14335  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14336  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14337  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14338  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14339  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14340  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14341  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14342  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14343  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14344  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14345  * draggable = true (defaults to false)
14346  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14347  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14348  * shadow (defaults to false)
14349  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14350  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14351  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14352  * @cfg {Array} buttons Array of buttons
14353  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14354  * @constructor
14355  * Create a new BasicDialog.
14356  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14357  * @param {Object} config Configuration options
14358  */
14359 Roo.BasicDialog = function(el, config){
14360     this.el = Roo.get(el);
14361     var dh = Roo.DomHelper;
14362     if(!this.el && config && config.autoCreate){
14363         if(typeof config.autoCreate == "object"){
14364             if(!config.autoCreate.id){
14365                 config.autoCreate.id = el;
14366             }
14367             this.el = dh.append(document.body,
14368                         config.autoCreate, true);
14369         }else{
14370             this.el = dh.append(document.body,
14371                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14372         }
14373     }
14374     el = this.el;
14375     el.setDisplayed(true);
14376     el.hide = this.hideAction;
14377     this.id = el.id;
14378     el.addClass("x-dlg");
14379
14380     Roo.apply(this, config);
14381
14382     this.proxy = el.createProxy("x-dlg-proxy");
14383     this.proxy.hide = this.hideAction;
14384     this.proxy.setOpacity(.5);
14385     this.proxy.hide();
14386
14387     if(config.width){
14388         el.setWidth(config.width);
14389     }
14390     if(config.height){
14391         el.setHeight(config.height);
14392     }
14393     this.size = el.getSize();
14394     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14395         this.xy = [config.x,config.y];
14396     }else{
14397         this.xy = el.getCenterXY(true);
14398     }
14399     /** The header element @type Roo.Element */
14400     this.header = el.child("> .x-dlg-hd");
14401     /** The body element @type Roo.Element */
14402     this.body = el.child("> .x-dlg-bd");
14403     /** The footer element @type Roo.Element */
14404     this.footer = el.child("> .x-dlg-ft");
14405
14406     if(!this.header){
14407         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14408     }
14409     if(!this.body){
14410         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14411     }
14412
14413     this.header.unselectable();
14414     if(this.title){
14415         this.header.update(this.title);
14416     }
14417     // this element allows the dialog to be focused for keyboard event
14418     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14419     this.focusEl.swallowEvent("click", true);
14420
14421     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14422
14423     // wrap the body and footer for special rendering
14424     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14425     if(this.footer){
14426         this.bwrap.dom.appendChild(this.footer.dom);
14427     }
14428
14429     this.bg = this.el.createChild({
14430         tag: "div", cls:"x-dlg-bg",
14431         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14432     });
14433     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14434
14435
14436     if(this.autoScroll !== false && !this.autoTabs){
14437         this.body.setStyle("overflow", "auto");
14438     }
14439
14440     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14441
14442     if(this.closable !== false){
14443         this.el.addClass("x-dlg-closable");
14444         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14445         this.close.on("click", this.closeClick, this);
14446         this.close.addClassOnOver("x-dlg-close-over");
14447     }
14448     if(this.collapsible !== false){
14449         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14450         this.collapseBtn.on("click", this.collapseClick, this);
14451         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14452         this.header.on("dblclick", this.collapseClick, this);
14453     }
14454     if(this.resizable !== false){
14455         this.el.addClass("x-dlg-resizable");
14456         this.resizer = new Roo.Resizable(el, {
14457             minWidth: this.minWidth || 80,
14458             minHeight:this.minHeight || 80,
14459             handles: this.resizeHandles || "all",
14460             pinned: true
14461         });
14462         this.resizer.on("beforeresize", this.beforeResize, this);
14463         this.resizer.on("resize", this.onResize, this);
14464     }
14465     if(this.draggable !== false){
14466         el.addClass("x-dlg-draggable");
14467         if (!this.proxyDrag) {
14468             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14469         }
14470         else {
14471             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14472         }
14473         dd.setHandleElId(this.header.id);
14474         dd.endDrag = this.endMove.createDelegate(this);
14475         dd.startDrag = this.startMove.createDelegate(this);
14476         dd.onDrag = this.onDrag.createDelegate(this);
14477         dd.scroll = false;
14478         this.dd = dd;
14479     }
14480     if(this.modal){
14481         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14482         this.mask.enableDisplayMode("block");
14483         this.mask.hide();
14484         this.el.addClass("x-dlg-modal");
14485     }
14486     if(this.shadow){
14487         this.shadow = new Roo.Shadow({
14488             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14489             offset : this.shadowOffset
14490         });
14491     }else{
14492         this.shadowOffset = 0;
14493     }
14494     if(Roo.useShims && this.shim !== false){
14495         this.shim = this.el.createShim();
14496         this.shim.hide = this.hideAction;
14497         this.shim.hide();
14498     }else{
14499         this.shim = false;
14500     }
14501     if(this.autoTabs){
14502         this.initTabs();
14503     }
14504     if (this.buttons) { 
14505         var bts= this.buttons;
14506         this.buttons = [];
14507         Roo.each(bts, function(b) {
14508             this.addButton(b);
14509         }, this);
14510     }
14511     
14512     
14513     this.addEvents({
14514         /**
14515          * @event keydown
14516          * Fires when a key is pressed
14517          * @param {Roo.BasicDialog} this
14518          * @param {Roo.EventObject} e
14519          */
14520         "keydown" : true,
14521         /**
14522          * @event move
14523          * Fires when this dialog is moved by the user.
14524          * @param {Roo.BasicDialog} this
14525          * @param {Number} x The new page X
14526          * @param {Number} y The new page Y
14527          */
14528         "move" : true,
14529         /**
14530          * @event resize
14531          * Fires when this dialog is resized by the user.
14532          * @param {Roo.BasicDialog} this
14533          * @param {Number} width The new width
14534          * @param {Number} height The new height
14535          */
14536         "resize" : true,
14537         /**
14538          * @event beforehide
14539          * Fires before this dialog is hidden.
14540          * @param {Roo.BasicDialog} this
14541          */
14542         "beforehide" : true,
14543         /**
14544          * @event hide
14545          * Fires when this dialog is hidden.
14546          * @param {Roo.BasicDialog} this
14547          */
14548         "hide" : true,
14549         /**
14550          * @event beforeshow
14551          * Fires before this dialog is shown.
14552          * @param {Roo.BasicDialog} this
14553          */
14554         "beforeshow" : true,
14555         /**
14556          * @event show
14557          * Fires when this dialog is shown.
14558          * @param {Roo.BasicDialog} this
14559          */
14560         "show" : true
14561     });
14562     el.on("keydown", this.onKeyDown, this);
14563     el.on("mousedown", this.toFront, this);
14564     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14565     this.el.hide();
14566     Roo.DialogManager.register(this);
14567     Roo.BasicDialog.superclass.constructor.call(this);
14568 };
14569
14570 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14571     shadowOffset: Roo.isIE ? 6 : 5,
14572     minHeight: 80,
14573     minWidth: 200,
14574     minButtonWidth: 75,
14575     defaultButton: null,
14576     buttonAlign: "right",
14577     tabTag: 'div',
14578     firstShow: true,
14579
14580     /**
14581      * Sets the dialog title text
14582      * @param {String} text The title text to display
14583      * @return {Roo.BasicDialog} this
14584      */
14585     setTitle : function(text){
14586         this.header.update(text);
14587         return this;
14588     },
14589
14590     // private
14591     closeClick : function(){
14592         this.hide();
14593     },
14594
14595     // private
14596     collapseClick : function(){
14597         this[this.collapsed ? "expand" : "collapse"]();
14598     },
14599
14600     /**
14601      * Collapses the dialog to its minimized state (only the title bar is visible).
14602      * Equivalent to the user clicking the collapse dialog button.
14603      */
14604     collapse : function(){
14605         if(!this.collapsed){
14606             this.collapsed = true;
14607             this.el.addClass("x-dlg-collapsed");
14608             this.restoreHeight = this.el.getHeight();
14609             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14610         }
14611     },
14612
14613     /**
14614      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14615      * clicking the expand dialog button.
14616      */
14617     expand : function(){
14618         if(this.collapsed){
14619             this.collapsed = false;
14620             this.el.removeClass("x-dlg-collapsed");
14621             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14622         }
14623     },
14624
14625     /**
14626      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14627      * @return {Roo.TabPanel} The tabs component
14628      */
14629     initTabs : function(){
14630         var tabs = this.getTabs();
14631         while(tabs.getTab(0)){
14632             tabs.removeTab(0);
14633         }
14634         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14635             var dom = el.dom;
14636             tabs.addTab(Roo.id(dom), dom.title);
14637             dom.title = "";
14638         });
14639         tabs.activate(0);
14640         return tabs;
14641     },
14642
14643     // private
14644     beforeResize : function(){
14645         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14646     },
14647
14648     // private
14649     onResize : function(){
14650         this.refreshSize();
14651         this.syncBodyHeight();
14652         this.adjustAssets();
14653         this.focus();
14654         this.fireEvent("resize", this, this.size.width, this.size.height);
14655     },
14656
14657     // private
14658     onKeyDown : function(e){
14659         if(this.isVisible()){
14660             this.fireEvent("keydown", this, e);
14661         }
14662     },
14663
14664     /**
14665      * Resizes the dialog.
14666      * @param {Number} width
14667      * @param {Number} height
14668      * @return {Roo.BasicDialog} this
14669      */
14670     resizeTo : function(width, height){
14671         this.el.setSize(width, height);
14672         this.size = {width: width, height: height};
14673         this.syncBodyHeight();
14674         if(this.fixedcenter){
14675             this.center();
14676         }
14677         if(this.isVisible()){
14678             this.constrainXY();
14679             this.adjustAssets();
14680         }
14681         this.fireEvent("resize", this, width, height);
14682         return this;
14683     },
14684
14685
14686     /**
14687      * Resizes the dialog to fit the specified content size.
14688      * @param {Number} width
14689      * @param {Number} height
14690      * @return {Roo.BasicDialog} this
14691      */
14692     setContentSize : function(w, h){
14693         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14694         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14695         //if(!this.el.isBorderBox()){
14696             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14697             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14698         //}
14699         if(this.tabs){
14700             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14701             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14702         }
14703         this.resizeTo(w, h);
14704         return this;
14705     },
14706
14707     /**
14708      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14709      * executed in response to a particular key being pressed while the dialog is active.
14710      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14711      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14712      * @param {Function} fn The function to call
14713      * @param {Object} scope (optional) The scope of the function
14714      * @return {Roo.BasicDialog} this
14715      */
14716     addKeyListener : function(key, fn, scope){
14717         var keyCode, shift, ctrl, alt;
14718         if(typeof key == "object" && !(key instanceof Array)){
14719             keyCode = key["key"];
14720             shift = key["shift"];
14721             ctrl = key["ctrl"];
14722             alt = key["alt"];
14723         }else{
14724             keyCode = key;
14725         }
14726         var handler = function(dlg, e){
14727             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14728                 var k = e.getKey();
14729                 if(keyCode instanceof Array){
14730                     for(var i = 0, len = keyCode.length; i < len; i++){
14731                         if(keyCode[i] == k){
14732                           fn.call(scope || window, dlg, k, e);
14733                           return;
14734                         }
14735                     }
14736                 }else{
14737                     if(k == keyCode){
14738                         fn.call(scope || window, dlg, k, e);
14739                     }
14740                 }
14741             }
14742         };
14743         this.on("keydown", handler);
14744         return this;
14745     },
14746
14747     /**
14748      * Returns the TabPanel component (creates it if it doesn't exist).
14749      * Note: If you wish to simply check for the existence of tabs without creating them,
14750      * check for a null 'tabs' property.
14751      * @return {Roo.TabPanel} The tabs component
14752      */
14753     getTabs : function(){
14754         if(!this.tabs){
14755             this.el.addClass("x-dlg-auto-tabs");
14756             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14757             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14758         }
14759         return this.tabs;
14760     },
14761
14762     /**
14763      * Adds a button to the footer section of the dialog.
14764      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14765      * object or a valid Roo.DomHelper element config
14766      * @param {Function} handler The function called when the button is clicked
14767      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14768      * @return {Roo.Button} The new button
14769      */
14770     addButton : function(config, handler, scope){
14771         var dh = Roo.DomHelper;
14772         if(!this.footer){
14773             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14774         }
14775         if(!this.btnContainer){
14776             var tb = this.footer.createChild({
14777
14778                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14779                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14780             }, null, true);
14781             this.btnContainer = tb.firstChild.firstChild.firstChild;
14782         }
14783         var bconfig = {
14784             handler: handler,
14785             scope: scope,
14786             minWidth: this.minButtonWidth,
14787             hideParent:true
14788         };
14789         if(typeof config == "string"){
14790             bconfig.text = config;
14791         }else{
14792             if(config.tag){
14793                 bconfig.dhconfig = config;
14794             }else{
14795                 Roo.apply(bconfig, config);
14796             }
14797         }
14798         var fc = false;
14799         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14800             bconfig.position = Math.max(0, bconfig.position);
14801             fc = this.btnContainer.childNodes[bconfig.position];
14802         }
14803          
14804         var btn = new Roo.Button(
14805             fc ? 
14806                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14807                 : this.btnContainer.appendChild(document.createElement("td")),
14808             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14809             bconfig
14810         );
14811         this.syncBodyHeight();
14812         if(!this.buttons){
14813             /**
14814              * Array of all the buttons that have been added to this dialog via addButton
14815              * @type Array
14816              */
14817             this.buttons = [];
14818         }
14819         this.buttons.push(btn);
14820         return btn;
14821     },
14822
14823     /**
14824      * Sets the default button to be focused when the dialog is displayed.
14825      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14826      * @return {Roo.BasicDialog} this
14827      */
14828     setDefaultButton : function(btn){
14829         this.defaultButton = btn;
14830         return this;
14831     },
14832
14833     // private
14834     getHeaderFooterHeight : function(safe){
14835         var height = 0;
14836         if(this.header){
14837            height += this.header.getHeight();
14838         }
14839         if(this.footer){
14840            var fm = this.footer.getMargins();
14841             height += (this.footer.getHeight()+fm.top+fm.bottom);
14842         }
14843         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14844         height += this.centerBg.getPadding("tb");
14845         return height;
14846     },
14847
14848     // private
14849     syncBodyHeight : function(){
14850         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14851         var height = this.size.height - this.getHeaderFooterHeight(false);
14852         bd.setHeight(height-bd.getMargins("tb"));
14853         var hh = this.header.getHeight();
14854         var h = this.size.height-hh;
14855         cb.setHeight(h);
14856         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14857         bw.setHeight(h-cb.getPadding("tb"));
14858         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14859         bd.setWidth(bw.getWidth(true));
14860         if(this.tabs){
14861             this.tabs.syncHeight();
14862             if(Roo.isIE){
14863                 this.tabs.el.repaint();
14864             }
14865         }
14866     },
14867
14868     /**
14869      * Restores the previous state of the dialog if Roo.state is configured.
14870      * @return {Roo.BasicDialog} this
14871      */
14872     restoreState : function(){
14873         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14874         if(box && box.width){
14875             this.xy = [box.x, box.y];
14876             this.resizeTo(box.width, box.height);
14877         }
14878         return this;
14879     },
14880
14881     // private
14882     beforeShow : function(){
14883         this.expand();
14884         if(this.fixedcenter){
14885             this.xy = this.el.getCenterXY(true);
14886         }
14887         if(this.modal){
14888             Roo.get(document.body).addClass("x-body-masked");
14889             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14890             this.mask.show();
14891         }
14892         this.constrainXY();
14893     },
14894
14895     // private
14896     animShow : function(){
14897         var b = Roo.get(this.animateTarget).getBox();
14898         this.proxy.setSize(b.width, b.height);
14899         this.proxy.setLocation(b.x, b.y);
14900         this.proxy.show();
14901         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14902                     true, .35, this.showEl.createDelegate(this));
14903     },
14904
14905     /**
14906      * Shows the dialog.
14907      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14908      * @return {Roo.BasicDialog} this
14909      */
14910     show : function(animateTarget){
14911         if (this.fireEvent("beforeshow", this) === false){
14912             return;
14913         }
14914         if(this.syncHeightBeforeShow){
14915             this.syncBodyHeight();
14916         }else if(this.firstShow){
14917             this.firstShow = false;
14918             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14919         }
14920         this.animateTarget = animateTarget || this.animateTarget;
14921         if(!this.el.isVisible()){
14922             this.beforeShow();
14923             if(this.animateTarget && Roo.get(this.animateTarget)){
14924                 this.animShow();
14925             }else{
14926                 this.showEl();
14927             }
14928         }
14929         return this;
14930     },
14931
14932     // private
14933     showEl : function(){
14934         this.proxy.hide();
14935         this.el.setXY(this.xy);
14936         this.el.show();
14937         this.adjustAssets(true);
14938         this.toFront();
14939         this.focus();
14940         // IE peekaboo bug - fix found by Dave Fenwick
14941         if(Roo.isIE){
14942             this.el.repaint();
14943         }
14944         this.fireEvent("show", this);
14945     },
14946
14947     /**
14948      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14949      * dialog itself will receive focus.
14950      */
14951     focus : function(){
14952         if(this.defaultButton){
14953             this.defaultButton.focus();
14954         }else{
14955             this.focusEl.focus();
14956         }
14957     },
14958
14959     // private
14960     constrainXY : function(){
14961         if(this.constraintoviewport !== false){
14962             if(!this.viewSize){
14963                 if(this.container){
14964                     var s = this.container.getSize();
14965                     this.viewSize = [s.width, s.height];
14966                 }else{
14967                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14968                 }
14969             }
14970             var s = Roo.get(this.container||document).getScroll();
14971
14972             var x = this.xy[0], y = this.xy[1];
14973             var w = this.size.width, h = this.size.height;
14974             var vw = this.viewSize[0], vh = this.viewSize[1];
14975             // only move it if it needs it
14976             var moved = false;
14977             // first validate right/bottom
14978             if(x + w > vw+s.left){
14979                 x = vw - w;
14980                 moved = true;
14981             }
14982             if(y + h > vh+s.top){
14983                 y = vh - h;
14984                 moved = true;
14985             }
14986             // then make sure top/left isn't negative
14987             if(x < s.left){
14988                 x = s.left;
14989                 moved = true;
14990             }
14991             if(y < s.top){
14992                 y = s.top;
14993                 moved = true;
14994             }
14995             if(moved){
14996                 // cache xy
14997                 this.xy = [x, y];
14998                 if(this.isVisible()){
14999                     this.el.setLocation(x, y);
15000                     this.adjustAssets();
15001                 }
15002             }
15003         }
15004     },
15005
15006     // private
15007     onDrag : function(){
15008         if(!this.proxyDrag){
15009             this.xy = this.el.getXY();
15010             this.adjustAssets();
15011         }
15012     },
15013
15014     // private
15015     adjustAssets : function(doShow){
15016         var x = this.xy[0], y = this.xy[1];
15017         var w = this.size.width, h = this.size.height;
15018         if(doShow === true){
15019             if(this.shadow){
15020                 this.shadow.show(this.el);
15021             }
15022             if(this.shim){
15023                 this.shim.show();
15024             }
15025         }
15026         if(this.shadow && this.shadow.isVisible()){
15027             this.shadow.show(this.el);
15028         }
15029         if(this.shim && this.shim.isVisible()){
15030             this.shim.setBounds(x, y, w, h);
15031         }
15032     },
15033
15034     // private
15035     adjustViewport : function(w, h){
15036         if(!w || !h){
15037             w = Roo.lib.Dom.getViewWidth();
15038             h = Roo.lib.Dom.getViewHeight();
15039         }
15040         // cache the size
15041         this.viewSize = [w, h];
15042         if(this.modal && this.mask.isVisible()){
15043             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15044             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15045         }
15046         if(this.isVisible()){
15047             this.constrainXY();
15048         }
15049     },
15050
15051     /**
15052      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15053      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15054      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15055      */
15056     destroy : function(removeEl){
15057         if(this.isVisible()){
15058             this.animateTarget = null;
15059             this.hide();
15060         }
15061         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15062         if(this.tabs){
15063             this.tabs.destroy(removeEl);
15064         }
15065         Roo.destroy(
15066              this.shim,
15067              this.proxy,
15068              this.resizer,
15069              this.close,
15070              this.mask
15071         );
15072         if(this.dd){
15073             this.dd.unreg();
15074         }
15075         if(this.buttons){
15076            for(var i = 0, len = this.buttons.length; i < len; i++){
15077                this.buttons[i].destroy();
15078            }
15079         }
15080         this.el.removeAllListeners();
15081         if(removeEl === true){
15082             this.el.update("");
15083             this.el.remove();
15084         }
15085         Roo.DialogManager.unregister(this);
15086     },
15087
15088     // private
15089     startMove : function(){
15090         if(this.proxyDrag){
15091             this.proxy.show();
15092         }
15093         if(this.constraintoviewport !== false){
15094             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15095         }
15096     },
15097
15098     // private
15099     endMove : function(){
15100         if(!this.proxyDrag){
15101             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15102         }else{
15103             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15104             this.proxy.hide();
15105         }
15106         this.refreshSize();
15107         this.adjustAssets();
15108         this.focus();
15109         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15110     },
15111
15112     /**
15113      * Brings this dialog to the front of any other visible dialogs
15114      * @return {Roo.BasicDialog} this
15115      */
15116     toFront : function(){
15117         Roo.DialogManager.bringToFront(this);
15118         return this;
15119     },
15120
15121     /**
15122      * Sends this dialog to the back (under) of any other visible dialogs
15123      * @return {Roo.BasicDialog} this
15124      */
15125     toBack : function(){
15126         Roo.DialogManager.sendToBack(this);
15127         return this;
15128     },
15129
15130     /**
15131      * Centers this dialog in the viewport
15132      * @return {Roo.BasicDialog} this
15133      */
15134     center : function(){
15135         var xy = this.el.getCenterXY(true);
15136         this.moveTo(xy[0], xy[1]);
15137         return this;
15138     },
15139
15140     /**
15141      * Moves the dialog's top-left corner to the specified point
15142      * @param {Number} x
15143      * @param {Number} y
15144      * @return {Roo.BasicDialog} this
15145      */
15146     moveTo : function(x, y){
15147         this.xy = [x,y];
15148         if(this.isVisible()){
15149             this.el.setXY(this.xy);
15150             this.adjustAssets();
15151         }
15152         return this;
15153     },
15154
15155     /**
15156      * Aligns the dialog to the specified element
15157      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15158      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15159      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15160      * @return {Roo.BasicDialog} this
15161      */
15162     alignTo : function(element, position, offsets){
15163         this.xy = this.el.getAlignToXY(element, position, offsets);
15164         if(this.isVisible()){
15165             this.el.setXY(this.xy);
15166             this.adjustAssets();
15167         }
15168         return this;
15169     },
15170
15171     /**
15172      * Anchors an element to another element and realigns it when the window is resized.
15173      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15174      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15175      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15176      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15177      * is a number, it is used as the buffer delay (defaults to 50ms).
15178      * @return {Roo.BasicDialog} this
15179      */
15180     anchorTo : function(el, alignment, offsets, monitorScroll){
15181         var action = function(){
15182             this.alignTo(el, alignment, offsets);
15183         };
15184         Roo.EventManager.onWindowResize(action, this);
15185         var tm = typeof monitorScroll;
15186         if(tm != 'undefined'){
15187             Roo.EventManager.on(window, 'scroll', action, this,
15188                 {buffer: tm == 'number' ? monitorScroll : 50});
15189         }
15190         action.call(this);
15191         return this;
15192     },
15193
15194     /**
15195      * Returns true if the dialog is visible
15196      * @return {Boolean}
15197      */
15198     isVisible : function(){
15199         return this.el.isVisible();
15200     },
15201
15202     // private
15203     animHide : function(callback){
15204         var b = Roo.get(this.animateTarget).getBox();
15205         this.proxy.show();
15206         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15207         this.el.hide();
15208         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15209                     this.hideEl.createDelegate(this, [callback]));
15210     },
15211
15212     /**
15213      * Hides the dialog.
15214      * @param {Function} callback (optional) Function to call when the dialog is hidden
15215      * @return {Roo.BasicDialog} this
15216      */
15217     hide : function(callback){
15218         if (this.fireEvent("beforehide", this) === false){
15219             return;
15220         }
15221         if(this.shadow){
15222             this.shadow.hide();
15223         }
15224         if(this.shim) {
15225           this.shim.hide();
15226         }
15227         // sometimes animateTarget seems to get set.. causing problems...
15228         // this just double checks..
15229         if(this.animateTarget && Roo.get(this.animateTarget)) {
15230            this.animHide(callback);
15231         }else{
15232             this.el.hide();
15233             this.hideEl(callback);
15234         }
15235         return this;
15236     },
15237
15238     // private
15239     hideEl : function(callback){
15240         this.proxy.hide();
15241         if(this.modal){
15242             this.mask.hide();
15243             Roo.get(document.body).removeClass("x-body-masked");
15244         }
15245         this.fireEvent("hide", this);
15246         if(typeof callback == "function"){
15247             callback();
15248         }
15249     },
15250
15251     // private
15252     hideAction : function(){
15253         this.setLeft("-10000px");
15254         this.setTop("-10000px");
15255         this.setStyle("visibility", "hidden");
15256     },
15257
15258     // private
15259     refreshSize : function(){
15260         this.size = this.el.getSize();
15261         this.xy = this.el.getXY();
15262         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15263     },
15264
15265     // private
15266     // z-index is managed by the DialogManager and may be overwritten at any time
15267     setZIndex : function(index){
15268         if(this.modal){
15269             this.mask.setStyle("z-index", index);
15270         }
15271         if(this.shim){
15272             this.shim.setStyle("z-index", ++index);
15273         }
15274         if(this.shadow){
15275             this.shadow.setZIndex(++index);
15276         }
15277         this.el.setStyle("z-index", ++index);
15278         if(this.proxy){
15279             this.proxy.setStyle("z-index", ++index);
15280         }
15281         if(this.resizer){
15282             this.resizer.proxy.setStyle("z-index", ++index);
15283         }
15284
15285         this.lastZIndex = index;
15286     },
15287
15288     /**
15289      * Returns the element for this dialog
15290      * @return {Roo.Element} The underlying dialog Element
15291      */
15292     getEl : function(){
15293         return this.el;
15294     }
15295 });
15296
15297 /**
15298  * @class Roo.DialogManager
15299  * Provides global access to BasicDialogs that have been created and
15300  * support for z-indexing (layering) multiple open dialogs.
15301  */
15302 Roo.DialogManager = function(){
15303     var list = {};
15304     var accessList = [];
15305     var front = null;
15306
15307     // private
15308     var sortDialogs = function(d1, d2){
15309         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15310     };
15311
15312     // private
15313     var orderDialogs = function(){
15314         accessList.sort(sortDialogs);
15315         var seed = Roo.DialogManager.zseed;
15316         for(var i = 0, len = accessList.length; i < len; i++){
15317             var dlg = accessList[i];
15318             if(dlg){
15319                 dlg.setZIndex(seed + (i*10));
15320             }
15321         }
15322     };
15323
15324     return {
15325         /**
15326          * The starting z-index for BasicDialogs (defaults to 9000)
15327          * @type Number The z-index value
15328          */
15329         zseed : 9000,
15330
15331         // private
15332         register : function(dlg){
15333             list[dlg.id] = dlg;
15334             accessList.push(dlg);
15335         },
15336
15337         // private
15338         unregister : function(dlg){
15339             delete list[dlg.id];
15340             var i=0;
15341             var len=0;
15342             if(!accessList.indexOf){
15343                 for(  i = 0, len = accessList.length; i < len; i++){
15344                     if(accessList[i] == dlg){
15345                         accessList.splice(i, 1);
15346                         return;
15347                     }
15348                 }
15349             }else{
15350                  i = accessList.indexOf(dlg);
15351                 if(i != -1){
15352                     accessList.splice(i, 1);
15353                 }
15354             }
15355         },
15356
15357         /**
15358          * Gets a registered dialog by id
15359          * @param {String/Object} id The id of the dialog or a dialog
15360          * @return {Roo.BasicDialog} this
15361          */
15362         get : function(id){
15363             return typeof id == "object" ? id : list[id];
15364         },
15365
15366         /**
15367          * Brings the specified dialog to the front
15368          * @param {String/Object} dlg The id of the dialog or a dialog
15369          * @return {Roo.BasicDialog} this
15370          */
15371         bringToFront : function(dlg){
15372             dlg = this.get(dlg);
15373             if(dlg != front){
15374                 front = dlg;
15375                 dlg._lastAccess = new Date().getTime();
15376                 orderDialogs();
15377             }
15378             return dlg;
15379         },
15380
15381         /**
15382          * Sends the specified dialog to the back
15383          * @param {String/Object} dlg The id of the dialog or a dialog
15384          * @return {Roo.BasicDialog} this
15385          */
15386         sendToBack : function(dlg){
15387             dlg = this.get(dlg);
15388             dlg._lastAccess = -(new Date().getTime());
15389             orderDialogs();
15390             return dlg;
15391         },
15392
15393         /**
15394          * Hides all dialogs
15395          */
15396         hideAll : function(){
15397             for(var id in list){
15398                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15399                     list[id].hide();
15400                 }
15401             }
15402         }
15403     };
15404 }();
15405
15406 /**
15407  * @class Roo.LayoutDialog
15408  * @extends Roo.BasicDialog
15409  * Dialog which provides adjustments for working with a layout in a Dialog.
15410  * Add your necessary layout config options to the dialog's config.<br>
15411  * Example usage (including a nested layout):
15412  * <pre><code>
15413 if(!dialog){
15414     dialog = new Roo.LayoutDialog("download-dlg", {
15415         modal: true,
15416         width:600,
15417         height:450,
15418         shadow:true,
15419         minWidth:500,
15420         minHeight:350,
15421         autoTabs:true,
15422         proxyDrag:true,
15423         // layout config merges with the dialog config
15424         center:{
15425             tabPosition: "top",
15426             alwaysShowTabs: true
15427         }
15428     });
15429     dialog.addKeyListener(27, dialog.hide, dialog);
15430     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15431     dialog.addButton("Build It!", this.getDownload, this);
15432
15433     // we can even add nested layouts
15434     var innerLayout = new Roo.BorderLayout("dl-inner", {
15435         east: {
15436             initialSize: 200,
15437             autoScroll:true,
15438             split:true
15439         },
15440         center: {
15441             autoScroll:true
15442         }
15443     });
15444     innerLayout.beginUpdate();
15445     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15446     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15447     innerLayout.endUpdate(true);
15448
15449     var layout = dialog.getLayout();
15450     layout.beginUpdate();
15451     layout.add("center", new Roo.ContentPanel("standard-panel",
15452                         {title: "Download the Source", fitToFrame:true}));
15453     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15454                {title: "Build your own roo.js"}));
15455     layout.getRegion("center").showPanel(sp);
15456     layout.endUpdate();
15457 }
15458 </code></pre>
15459     * @constructor
15460     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15461     * @param {Object} config configuration options
15462   */
15463 Roo.LayoutDialog = function(el, cfg){
15464     
15465     var config=  cfg;
15466     if (typeof(cfg) == 'undefined') {
15467         config = Roo.apply({}, el);
15468         // not sure why we use documentElement here.. - it should always be body.
15469         // IE7 borks horribly if we use documentElement.
15470         // webkit also does not like documentElement - it creates a body element...
15471         el = Roo.get( document.body || document.documentElement ).createChild();
15472         //config.autoCreate = true;
15473     }
15474     
15475     
15476     config.autoTabs = false;
15477     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15478     this.body.setStyle({overflow:"hidden", position:"relative"});
15479     this.layout = new Roo.BorderLayout(this.body.dom, config);
15480     this.layout.monitorWindowResize = false;
15481     this.el.addClass("x-dlg-auto-layout");
15482     // fix case when center region overwrites center function
15483     this.center = Roo.BasicDialog.prototype.center;
15484     this.on("show", this.layout.layout, this.layout, true);
15485     if (config.items) {
15486         var xitems = config.items;
15487         delete config.items;
15488         Roo.each(xitems, this.addxtype, this);
15489     }
15490     
15491     
15492 };
15493 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15494     /**
15495      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15496      * @deprecated
15497      */
15498     endUpdate : function(){
15499         this.layout.endUpdate();
15500     },
15501
15502     /**
15503      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15504      *  @deprecated
15505      */
15506     beginUpdate : function(){
15507         this.layout.beginUpdate();
15508     },
15509
15510     /**
15511      * Get the BorderLayout for this dialog
15512      * @return {Roo.BorderLayout}
15513      */
15514     getLayout : function(){
15515         return this.layout;
15516     },
15517
15518     showEl : function(){
15519         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15520         if(Roo.isIE7){
15521             this.layout.layout();
15522         }
15523     },
15524
15525     // private
15526     // Use the syncHeightBeforeShow config option to control this automatically
15527     syncBodyHeight : function(){
15528         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15529         if(this.layout){this.layout.layout();}
15530     },
15531     
15532       /**
15533      * Add an xtype element (actually adds to the layout.)
15534      * @return {Object} xdata xtype object data.
15535      */
15536     
15537     addxtype : function(c) {
15538         return this.layout.addxtype(c);
15539     }
15540 });/*
15541  * Based on:
15542  * Ext JS Library 1.1.1
15543  * Copyright(c) 2006-2007, Ext JS, LLC.
15544  *
15545  * Originally Released Under LGPL - original licence link has changed is not relivant.
15546  *
15547  * Fork - LGPL
15548  * <script type="text/javascript">
15549  */
15550  
15551 /**
15552  * @class Roo.MessageBox
15553  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15554  * Example usage:
15555  *<pre><code>
15556 // Basic alert:
15557 Roo.Msg.alert('Status', 'Changes saved successfully.');
15558
15559 // Prompt for user data:
15560 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15561     if (btn == 'ok'){
15562         // process text value...
15563     }
15564 });
15565
15566 // Show a dialog using config options:
15567 Roo.Msg.show({
15568    title:'Save Changes?',
15569    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15570    buttons: Roo.Msg.YESNOCANCEL,
15571    fn: processResult,
15572    animEl: 'elId'
15573 });
15574 </code></pre>
15575  * @singleton
15576  */
15577 Roo.MessageBox = function(){
15578     var dlg, opt, mask, waitTimer;
15579     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15580     var buttons, activeTextEl, bwidth;
15581
15582     // private
15583     var handleButton = function(button){
15584         dlg.hide();
15585         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15586     };
15587
15588     // private
15589     var handleHide = function(){
15590         if(opt && opt.cls){
15591             dlg.el.removeClass(opt.cls);
15592         }
15593         if(waitTimer){
15594             Roo.TaskMgr.stop(waitTimer);
15595             waitTimer = null;
15596         }
15597     };
15598
15599     // private
15600     var updateButtons = function(b){
15601         var width = 0;
15602         if(!b){
15603             buttons["ok"].hide();
15604             buttons["cancel"].hide();
15605             buttons["yes"].hide();
15606             buttons["no"].hide();
15607             dlg.footer.dom.style.display = 'none';
15608             return width;
15609         }
15610         dlg.footer.dom.style.display = '';
15611         for(var k in buttons){
15612             if(typeof buttons[k] != "function"){
15613                 if(b[k]){
15614                     buttons[k].show();
15615                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15616                     width += buttons[k].el.getWidth()+15;
15617                 }else{
15618                     buttons[k].hide();
15619                 }
15620             }
15621         }
15622         return width;
15623     };
15624
15625     // private
15626     var handleEsc = function(d, k, e){
15627         if(opt && opt.closable !== false){
15628             dlg.hide();
15629         }
15630         if(e){
15631             e.stopEvent();
15632         }
15633     };
15634
15635     return {
15636         /**
15637          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15638          * @return {Roo.BasicDialog} The BasicDialog element
15639          */
15640         getDialog : function(){
15641            if(!dlg){
15642                 dlg = new Roo.BasicDialog("x-msg-box", {
15643                     autoCreate : true,
15644                     shadow: true,
15645                     draggable: true,
15646                     resizable:false,
15647                     constraintoviewport:false,
15648                     fixedcenter:true,
15649                     collapsible : false,
15650                     shim:true,
15651                     modal: true,
15652                     width:400, height:100,
15653                     buttonAlign:"center",
15654                     closeClick : function(){
15655                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15656                             handleButton("no");
15657                         }else{
15658                             handleButton("cancel");
15659                         }
15660                     }
15661                 });
15662                 dlg.on("hide", handleHide);
15663                 mask = dlg.mask;
15664                 dlg.addKeyListener(27, handleEsc);
15665                 buttons = {};
15666                 var bt = this.buttonText;
15667                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15668                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15669                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15670                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15671                 bodyEl = dlg.body.createChild({
15672
15673                     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>'
15674                 });
15675                 msgEl = bodyEl.dom.firstChild;
15676                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15677                 textboxEl.enableDisplayMode();
15678                 textboxEl.addKeyListener([10,13], function(){
15679                     if(dlg.isVisible() && opt && opt.buttons){
15680                         if(opt.buttons.ok){
15681                             handleButton("ok");
15682                         }else if(opt.buttons.yes){
15683                             handleButton("yes");
15684                         }
15685                     }
15686                 });
15687                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15688                 textareaEl.enableDisplayMode();
15689                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15690                 progressEl.enableDisplayMode();
15691                 var pf = progressEl.dom.firstChild;
15692                 if (pf) {
15693                     pp = Roo.get(pf.firstChild);
15694                     pp.setHeight(pf.offsetHeight);
15695                 }
15696                 
15697             }
15698             return dlg;
15699         },
15700
15701         /**
15702          * Updates the message box body text
15703          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15704          * the XHTML-compliant non-breaking space character '&amp;#160;')
15705          * @return {Roo.MessageBox} This message box
15706          */
15707         updateText : function(text){
15708             if(!dlg.isVisible() && !opt.width){
15709                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15710             }
15711             msgEl.innerHTML = text || '&#160;';
15712       
15713             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15714             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15715             var w = Math.max(
15716                     Math.min(opt.width || cw , this.maxWidth), 
15717                     Math.max(opt.minWidth || this.minWidth, bwidth)
15718             );
15719             if(opt.prompt){
15720                 activeTextEl.setWidth(w);
15721             }
15722             if(dlg.isVisible()){
15723                 dlg.fixedcenter = false;
15724             }
15725             // to big, make it scroll. = But as usual stupid IE does not support
15726             // !important..
15727             
15728             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15729                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15730                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15731             } else {
15732                 bodyEl.dom.style.height = '';
15733                 bodyEl.dom.style.overflowY = '';
15734             }
15735             if (cw > w) {
15736                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15737             } else {
15738                 bodyEl.dom.style.overflowX = '';
15739             }
15740             
15741             dlg.setContentSize(w, bodyEl.getHeight());
15742             if(dlg.isVisible()){
15743                 dlg.fixedcenter = true;
15744             }
15745             return this;
15746         },
15747
15748         /**
15749          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15750          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15751          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15752          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15753          * @return {Roo.MessageBox} This message box
15754          */
15755         updateProgress : function(value, text){
15756             if(text){
15757                 this.updateText(text);
15758             }
15759             if (pp) { // weird bug on my firefox - for some reason this is not defined
15760                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15761             }
15762             return this;
15763         },        
15764
15765         /**
15766          * Returns true if the message box is currently displayed
15767          * @return {Boolean} True if the message box is visible, else false
15768          */
15769         isVisible : function(){
15770             return dlg && dlg.isVisible();  
15771         },
15772
15773         /**
15774          * Hides the message box if it is displayed
15775          */
15776         hide : function(){
15777             if(this.isVisible()){
15778                 dlg.hide();
15779             }  
15780         },
15781
15782         /**
15783          * Displays a new message box, or reinitializes an existing message box, based on the config options
15784          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15785          * The following config object properties are supported:
15786          * <pre>
15787 Property    Type             Description
15788 ----------  ---------------  ------------------------------------------------------------------------------------
15789 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15790                                    closes (defaults to undefined)
15791 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15792                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15793 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15794                                    progress and wait dialogs will ignore this property and always hide the
15795                                    close button as they can only be closed programmatically.
15796 cls               String           A custom CSS class to apply to the message box element
15797 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15798                                    displayed (defaults to 75)
15799 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15800                                    function will be btn (the name of the button that was clicked, if applicable,
15801                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15802                                    Progress and wait dialogs will ignore this option since they do not respond to
15803                                    user actions and can only be closed programmatically, so any required function
15804                                    should be called by the same code after it closes the dialog.
15805 icon              String           A CSS class that provides a background image to be used as an icon for
15806                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15807 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15808 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15809 modal             Boolean          False to allow user interaction with the page while the message box is
15810                                    displayed (defaults to true)
15811 msg               String           A string that will replace the existing message box body text (defaults
15812                                    to the XHTML-compliant non-breaking space character '&#160;')
15813 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15814 progress          Boolean          True to display a progress bar (defaults to false)
15815 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15816 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15817 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15818 title             String           The title text
15819 value             String           The string value to set into the active textbox element if displayed
15820 wait              Boolean          True to display a progress bar (defaults to false)
15821 width             Number           The width of the dialog in pixels
15822 </pre>
15823          *
15824          * Example usage:
15825          * <pre><code>
15826 Roo.Msg.show({
15827    title: 'Address',
15828    msg: 'Please enter your address:',
15829    width: 300,
15830    buttons: Roo.MessageBox.OKCANCEL,
15831    multiline: true,
15832    fn: saveAddress,
15833    animEl: 'addAddressBtn'
15834 });
15835 </code></pre>
15836          * @param {Object} config Configuration options
15837          * @return {Roo.MessageBox} This message box
15838          */
15839         show : function(options)
15840         {
15841             
15842             // this causes nightmares if you show one dialog after another
15843             // especially on callbacks..
15844              
15845             if(this.isVisible()){
15846                 
15847                 this.hide();
15848                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15849                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15850                 Roo.log("New Dialog Message:" +  options.msg )
15851                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15852                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15853                 
15854             }
15855             var d = this.getDialog();
15856             opt = options;
15857             d.setTitle(opt.title || "&#160;");
15858             d.close.setDisplayed(opt.closable !== false);
15859             activeTextEl = textboxEl;
15860             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15861             if(opt.prompt){
15862                 if(opt.multiline){
15863                     textboxEl.hide();
15864                     textareaEl.show();
15865                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15866                         opt.multiline : this.defaultTextHeight);
15867                     activeTextEl = textareaEl;
15868                 }else{
15869                     textboxEl.show();
15870                     textareaEl.hide();
15871                 }
15872             }else{
15873                 textboxEl.hide();
15874                 textareaEl.hide();
15875             }
15876             progressEl.setDisplayed(opt.progress === true);
15877             this.updateProgress(0);
15878             activeTextEl.dom.value = opt.value || "";
15879             if(opt.prompt){
15880                 dlg.setDefaultButton(activeTextEl);
15881             }else{
15882                 var bs = opt.buttons;
15883                 var db = null;
15884                 if(bs && bs.ok){
15885                     db = buttons["ok"];
15886                 }else if(bs && bs.yes){
15887                     db = buttons["yes"];
15888                 }
15889                 dlg.setDefaultButton(db);
15890             }
15891             bwidth = updateButtons(opt.buttons);
15892             this.updateText(opt.msg);
15893             if(opt.cls){
15894                 d.el.addClass(opt.cls);
15895             }
15896             d.proxyDrag = opt.proxyDrag === true;
15897             d.modal = opt.modal !== false;
15898             d.mask = opt.modal !== false ? mask : false;
15899             if(!d.isVisible()){
15900                 // force it to the end of the z-index stack so it gets a cursor in FF
15901                 document.body.appendChild(dlg.el.dom);
15902                 d.animateTarget = null;
15903                 d.show(options.animEl);
15904             }
15905             return this;
15906         },
15907
15908         /**
15909          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15910          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15911          * and closing the message box when the process is complete.
15912          * @param {String} title The title bar text
15913          * @param {String} msg The message box body text
15914          * @return {Roo.MessageBox} This message box
15915          */
15916         progress : function(title, msg){
15917             this.show({
15918                 title : title,
15919                 msg : msg,
15920                 buttons: false,
15921                 progress:true,
15922                 closable:false,
15923                 minWidth: this.minProgressWidth,
15924                 modal : true
15925             });
15926             return this;
15927         },
15928
15929         /**
15930          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15931          * If a callback function is passed it will be called after the user clicks the button, and the
15932          * id of the button that was clicked will be passed as the only parameter to the callback
15933          * (could also be the top-right close button).
15934          * @param {String} title The title bar text
15935          * @param {String} msg The message box body text
15936          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15937          * @param {Object} scope (optional) The scope of the callback function
15938          * @return {Roo.MessageBox} This message box
15939          */
15940         alert : function(title, msg, fn, scope){
15941             this.show({
15942                 title : title,
15943                 msg : msg,
15944                 buttons: this.OK,
15945                 fn: fn,
15946                 scope : scope,
15947                 modal : true
15948             });
15949             return this;
15950         },
15951
15952         /**
15953          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15954          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15955          * You are responsible for closing the message box when the process is complete.
15956          * @param {String} msg The message box body text
15957          * @param {String} title (optional) The title bar text
15958          * @return {Roo.MessageBox} This message box
15959          */
15960         wait : function(msg, title){
15961             this.show({
15962                 title : title,
15963                 msg : msg,
15964                 buttons: false,
15965                 closable:false,
15966                 progress:true,
15967                 modal:true,
15968                 width:300,
15969                 wait:true
15970             });
15971             waitTimer = Roo.TaskMgr.start({
15972                 run: function(i){
15973                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15974                 },
15975                 interval: 1000
15976             });
15977             return this;
15978         },
15979
15980         /**
15981          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15982          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15983          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15984          * @param {String} title The title bar text
15985          * @param {String} msg The message box body text
15986          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15987          * @param {Object} scope (optional) The scope of the callback function
15988          * @return {Roo.MessageBox} This message box
15989          */
15990         confirm : function(title, msg, fn, scope){
15991             this.show({
15992                 title : title,
15993                 msg : msg,
15994                 buttons: this.YESNO,
15995                 fn: fn,
15996                 scope : scope,
15997                 modal : true
15998             });
15999             return this;
16000         },
16001
16002         /**
16003          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16004          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16005          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16006          * (could also be the top-right close button) and the text that was entered will be passed as the two
16007          * parameters to the callback.
16008          * @param {String} title The title bar text
16009          * @param {String} msg The message box body text
16010          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16011          * @param {Object} scope (optional) The scope of the callback function
16012          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16013          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16014          * @return {Roo.MessageBox} This message box
16015          */
16016         prompt : function(title, msg, fn, scope, multiline){
16017             this.show({
16018                 title : title,
16019                 msg : msg,
16020                 buttons: this.OKCANCEL,
16021                 fn: fn,
16022                 minWidth:250,
16023                 scope : scope,
16024                 prompt:true,
16025                 multiline: multiline,
16026                 modal : true
16027             });
16028             return this;
16029         },
16030
16031         /**
16032          * Button config that displays a single OK button
16033          * @type Object
16034          */
16035         OK : {ok:true},
16036         /**
16037          * Button config that displays Yes and No buttons
16038          * @type Object
16039          */
16040         YESNO : {yes:true, no:true},
16041         /**
16042          * Button config that displays OK and Cancel buttons
16043          * @type Object
16044          */
16045         OKCANCEL : {ok:true, cancel:true},
16046         /**
16047          * Button config that displays Yes, No and Cancel buttons
16048          * @type Object
16049          */
16050         YESNOCANCEL : {yes:true, no:true, cancel:true},
16051
16052         /**
16053          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16054          * @type Number
16055          */
16056         defaultTextHeight : 75,
16057         /**
16058          * The maximum width in pixels of the message box (defaults to 600)
16059          * @type Number
16060          */
16061         maxWidth : 600,
16062         /**
16063          * The minimum width in pixels of the message box (defaults to 100)
16064          * @type Number
16065          */
16066         minWidth : 100,
16067         /**
16068          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16069          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16070          * @type Number
16071          */
16072         minProgressWidth : 250,
16073         /**
16074          * An object containing the default button text strings that can be overriden for localized language support.
16075          * Supported properties are: ok, cancel, yes and no.
16076          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16077          * @type Object
16078          */
16079         buttonText : {
16080             ok : "OK",
16081             cancel : "Cancel",
16082             yes : "Yes",
16083             no : "No"
16084         }
16085     };
16086 }();
16087
16088 /**
16089  * Shorthand for {@link Roo.MessageBox}
16090  */
16091 Roo.Msg = Roo.MessageBox;/*
16092  * Based on:
16093  * Ext JS Library 1.1.1
16094  * Copyright(c) 2006-2007, Ext JS, LLC.
16095  *
16096  * Originally Released Under LGPL - original licence link has changed is not relivant.
16097  *
16098  * Fork - LGPL
16099  * <script type="text/javascript">
16100  */
16101 /**
16102  * @class Roo.QuickTips
16103  * Provides attractive and customizable tooltips for any element.
16104  * @singleton
16105  */
16106 Roo.QuickTips = function(){
16107     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16108     var ce, bd, xy, dd;
16109     var visible = false, disabled = true, inited = false;
16110     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16111     
16112     var onOver = function(e){
16113         if(disabled){
16114             return;
16115         }
16116         var t = e.getTarget();
16117         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16118             return;
16119         }
16120         if(ce && t == ce.el){
16121             clearTimeout(hideProc);
16122             return;
16123         }
16124         if(t && tagEls[t.id]){
16125             tagEls[t.id].el = t;
16126             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16127             return;
16128         }
16129         var ttp, et = Roo.fly(t);
16130         var ns = cfg.namespace;
16131         if(tm.interceptTitles && t.title){
16132             ttp = t.title;
16133             t.qtip = ttp;
16134             t.removeAttribute("title");
16135             e.preventDefault();
16136         }else{
16137             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16138         }
16139         if(ttp){
16140             showProc = show.defer(tm.showDelay, tm, [{
16141                 el: t, 
16142                 text: ttp, 
16143                 width: et.getAttributeNS(ns, cfg.width),
16144                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16145                 title: et.getAttributeNS(ns, cfg.title),
16146                     cls: et.getAttributeNS(ns, cfg.cls)
16147             }]);
16148         }
16149     };
16150     
16151     var onOut = function(e){
16152         clearTimeout(showProc);
16153         var t = e.getTarget();
16154         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16155             hideProc = setTimeout(hide, tm.hideDelay);
16156         }
16157     };
16158     
16159     var onMove = function(e){
16160         if(disabled){
16161             return;
16162         }
16163         xy = e.getXY();
16164         xy[1] += 18;
16165         if(tm.trackMouse && ce){
16166             el.setXY(xy);
16167         }
16168     };
16169     
16170     var onDown = function(e){
16171         clearTimeout(showProc);
16172         clearTimeout(hideProc);
16173         if(!e.within(el)){
16174             if(tm.hideOnClick){
16175                 hide();
16176                 tm.disable();
16177                 tm.enable.defer(100, tm);
16178             }
16179         }
16180     };
16181     
16182     var getPad = function(){
16183         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16184     };
16185
16186     var show = function(o){
16187         if(disabled){
16188             return;
16189         }
16190         clearTimeout(dismissProc);
16191         ce = o;
16192         if(removeCls){ // in case manually hidden
16193             el.removeClass(removeCls);
16194             removeCls = null;
16195         }
16196         if(ce.cls){
16197             el.addClass(ce.cls);
16198             removeCls = ce.cls;
16199         }
16200         if(ce.title){
16201             tipTitle.update(ce.title);
16202             tipTitle.show();
16203         }else{
16204             tipTitle.update('');
16205             tipTitle.hide();
16206         }
16207         el.dom.style.width  = tm.maxWidth+'px';
16208         //tipBody.dom.style.width = '';
16209         tipBodyText.update(o.text);
16210         var p = getPad(), w = ce.width;
16211         if(!w){
16212             var td = tipBodyText.dom;
16213             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16214             if(aw > tm.maxWidth){
16215                 w = tm.maxWidth;
16216             }else if(aw < tm.minWidth){
16217                 w = tm.minWidth;
16218             }else{
16219                 w = aw;
16220             }
16221         }
16222         //tipBody.setWidth(w);
16223         el.setWidth(parseInt(w, 10) + p);
16224         if(ce.autoHide === false){
16225             close.setDisplayed(true);
16226             if(dd){
16227                 dd.unlock();
16228             }
16229         }else{
16230             close.setDisplayed(false);
16231             if(dd){
16232                 dd.lock();
16233             }
16234         }
16235         if(xy){
16236             el.avoidY = xy[1]-18;
16237             el.setXY(xy);
16238         }
16239         if(tm.animate){
16240             el.setOpacity(.1);
16241             el.setStyle("visibility", "visible");
16242             el.fadeIn({callback: afterShow});
16243         }else{
16244             afterShow();
16245         }
16246     };
16247     
16248     var afterShow = function(){
16249         if(ce){
16250             el.show();
16251             esc.enable();
16252             if(tm.autoDismiss && ce.autoHide !== false){
16253                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16254             }
16255         }
16256     };
16257     
16258     var hide = function(noanim){
16259         clearTimeout(dismissProc);
16260         clearTimeout(hideProc);
16261         ce = null;
16262         if(el.isVisible()){
16263             esc.disable();
16264             if(noanim !== true && tm.animate){
16265                 el.fadeOut({callback: afterHide});
16266             }else{
16267                 afterHide();
16268             } 
16269         }
16270     };
16271     
16272     var afterHide = function(){
16273         el.hide();
16274         if(removeCls){
16275             el.removeClass(removeCls);
16276             removeCls = null;
16277         }
16278     };
16279     
16280     return {
16281         /**
16282         * @cfg {Number} minWidth
16283         * The minimum width of the quick tip (defaults to 40)
16284         */
16285        minWidth : 40,
16286         /**
16287         * @cfg {Number} maxWidth
16288         * The maximum width of the quick tip (defaults to 300)
16289         */
16290        maxWidth : 300,
16291         /**
16292         * @cfg {Boolean} interceptTitles
16293         * True to automatically use the element's DOM title value if available (defaults to false)
16294         */
16295        interceptTitles : false,
16296         /**
16297         * @cfg {Boolean} trackMouse
16298         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16299         */
16300        trackMouse : false,
16301         /**
16302         * @cfg {Boolean} hideOnClick
16303         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16304         */
16305        hideOnClick : true,
16306         /**
16307         * @cfg {Number} showDelay
16308         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16309         */
16310        showDelay : 500,
16311         /**
16312         * @cfg {Number} hideDelay
16313         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16314         */
16315        hideDelay : 200,
16316         /**
16317         * @cfg {Boolean} autoHide
16318         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16319         * Used in conjunction with hideDelay.
16320         */
16321        autoHide : true,
16322         /**
16323         * @cfg {Boolean}
16324         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16325         * (defaults to true).  Used in conjunction with autoDismissDelay.
16326         */
16327        autoDismiss : true,
16328         /**
16329         * @cfg {Number}
16330         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16331         */
16332        autoDismissDelay : 5000,
16333        /**
16334         * @cfg {Boolean} animate
16335         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16336         */
16337        animate : false,
16338
16339        /**
16340         * @cfg {String} title
16341         * Title text to display (defaults to '').  This can be any valid HTML markup.
16342         */
16343         title: '',
16344        /**
16345         * @cfg {String} text
16346         * Body text to display (defaults to '').  This can be any valid HTML markup.
16347         */
16348         text : '',
16349        /**
16350         * @cfg {String} cls
16351         * A CSS class to apply to the base quick tip element (defaults to '').
16352         */
16353         cls : '',
16354        /**
16355         * @cfg {Number} width
16356         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16357         * minWidth or maxWidth.
16358         */
16359         width : null,
16360
16361     /**
16362      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16363      * or display QuickTips in a page.
16364      */
16365        init : function(){
16366           tm = Roo.QuickTips;
16367           cfg = tm.tagConfig;
16368           if(!inited){
16369               if(!Roo.isReady){ // allow calling of init() before onReady
16370                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16371                   return;
16372               }
16373               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16374               el.fxDefaults = {stopFx: true};
16375               // maximum custom styling
16376               //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>');
16377               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>');              
16378               tipTitle = el.child('h3');
16379               tipTitle.enableDisplayMode("block");
16380               tipBody = el.child('div.x-tip-bd');
16381               tipBodyText = el.child('div.x-tip-bd-inner');
16382               //bdLeft = el.child('div.x-tip-bd-left');
16383               //bdRight = el.child('div.x-tip-bd-right');
16384               close = el.child('div.x-tip-close');
16385               close.enableDisplayMode("block");
16386               close.on("click", hide);
16387               var d = Roo.get(document);
16388               d.on("mousedown", onDown);
16389               d.on("mouseover", onOver);
16390               d.on("mouseout", onOut);
16391               d.on("mousemove", onMove);
16392               esc = d.addKeyListener(27, hide);
16393               esc.disable();
16394               if(Roo.dd.DD){
16395                   dd = el.initDD("default", null, {
16396                       onDrag : function(){
16397                           el.sync();  
16398                       }
16399                   });
16400                   dd.setHandleElId(tipTitle.id);
16401                   dd.lock();
16402               }
16403               inited = true;
16404           }
16405           this.enable(); 
16406        },
16407
16408     /**
16409      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16410      * are supported:
16411      * <pre>
16412 Property    Type                   Description
16413 ----------  ---------------------  ------------------------------------------------------------------------
16414 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16415      * </ul>
16416      * @param {Object} config The config object
16417      */
16418        register : function(config){
16419            var cs = config instanceof Array ? config : arguments;
16420            for(var i = 0, len = cs.length; i < len; i++) {
16421                var c = cs[i];
16422                var target = c.target;
16423                if(target){
16424                    if(target instanceof Array){
16425                        for(var j = 0, jlen = target.length; j < jlen; j++){
16426                            tagEls[target[j]] = c;
16427                        }
16428                    }else{
16429                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16430                    }
16431                }
16432            }
16433        },
16434
16435     /**
16436      * Removes this quick tip from its element and destroys it.
16437      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16438      */
16439        unregister : function(el){
16440            delete tagEls[Roo.id(el)];
16441        },
16442
16443     /**
16444      * Enable this quick tip.
16445      */
16446        enable : function(){
16447            if(inited && disabled){
16448                locks.pop();
16449                if(locks.length < 1){
16450                    disabled = false;
16451                }
16452            }
16453        },
16454
16455     /**
16456      * Disable this quick tip.
16457      */
16458        disable : function(){
16459           disabled = true;
16460           clearTimeout(showProc);
16461           clearTimeout(hideProc);
16462           clearTimeout(dismissProc);
16463           if(ce){
16464               hide(true);
16465           }
16466           locks.push(1);
16467        },
16468
16469     /**
16470      * Returns true if the quick tip is enabled, else false.
16471      */
16472        isEnabled : function(){
16473             return !disabled;
16474        },
16475
16476         // private
16477        tagConfig : {
16478            namespace : "ext",
16479            attribute : "qtip",
16480            width : "width",
16481            target : "target",
16482            title : "qtitle",
16483            hide : "hide",
16484            cls : "qclass"
16485        }
16486    };
16487 }();
16488
16489 // backwards compat
16490 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16491  * Based on:
16492  * Ext JS Library 1.1.1
16493  * Copyright(c) 2006-2007, Ext JS, LLC.
16494  *
16495  * Originally Released Under LGPL - original licence link has changed is not relivant.
16496  *
16497  * Fork - LGPL
16498  * <script type="text/javascript">
16499  */
16500  
16501
16502 /**
16503  * @class Roo.tree.TreePanel
16504  * @extends Roo.data.Tree
16505
16506  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16507  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16508  * @cfg {Boolean} enableDD true to enable drag and drop
16509  * @cfg {Boolean} enableDrag true to enable just drag
16510  * @cfg {Boolean} enableDrop true to enable just drop
16511  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16512  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16513  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16514  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16515  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16516  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16517  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16518  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16519  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16520  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16521  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16522  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16523  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16524  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16525  * @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>
16526  * @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>
16527  * 
16528  * @constructor
16529  * @param {String/HTMLElement/Element} el The container element
16530  * @param {Object} config
16531  */
16532 Roo.tree.TreePanel = function(el, config){
16533     var root = false;
16534     var loader = false;
16535     if (config.root) {
16536         root = config.root;
16537         delete config.root;
16538     }
16539     if (config.loader) {
16540         loader = config.loader;
16541         delete config.loader;
16542     }
16543     
16544     Roo.apply(this, config);
16545     Roo.tree.TreePanel.superclass.constructor.call(this);
16546     this.el = Roo.get(el);
16547     this.el.addClass('x-tree');
16548     //console.log(root);
16549     if (root) {
16550         this.setRootNode( Roo.factory(root, Roo.tree));
16551     }
16552     if (loader) {
16553         this.loader = Roo.factory(loader, Roo.tree);
16554     }
16555    /**
16556     * Read-only. The id of the container element becomes this TreePanel's id.
16557     */
16558     this.id = this.el.id;
16559     this.addEvents({
16560         /**
16561         * @event beforeload
16562         * Fires before a node is loaded, return false to cancel
16563         * @param {Node} node The node being loaded
16564         */
16565         "beforeload" : true,
16566         /**
16567         * @event load
16568         * Fires when a node is loaded
16569         * @param {Node} node The node that was loaded
16570         */
16571         "load" : true,
16572         /**
16573         * @event textchange
16574         * Fires when the text for a node is changed
16575         * @param {Node} node The node
16576         * @param {String} text The new text
16577         * @param {String} oldText The old text
16578         */
16579         "textchange" : true,
16580         /**
16581         * @event beforeexpand
16582         * Fires before a node is expanded, return false to cancel.
16583         * @param {Node} node The node
16584         * @param {Boolean} deep
16585         * @param {Boolean} anim
16586         */
16587         "beforeexpand" : true,
16588         /**
16589         * @event beforecollapse
16590         * Fires before a node is collapsed, return false to cancel.
16591         * @param {Node} node The node
16592         * @param {Boolean} deep
16593         * @param {Boolean} anim
16594         */
16595         "beforecollapse" : true,
16596         /**
16597         * @event expand
16598         * Fires when a node is expanded
16599         * @param {Node} node The node
16600         */
16601         "expand" : true,
16602         /**
16603         * @event disabledchange
16604         * Fires when the disabled status of a node changes
16605         * @param {Node} node The node
16606         * @param {Boolean} disabled
16607         */
16608         "disabledchange" : true,
16609         /**
16610         * @event collapse
16611         * Fires when a node is collapsed
16612         * @param {Node} node The node
16613         */
16614         "collapse" : true,
16615         /**
16616         * @event beforeclick
16617         * Fires before click processing on a node. Return false to cancel the default action.
16618         * @param {Node} node The node
16619         * @param {Roo.EventObject} e The event object
16620         */
16621         "beforeclick":true,
16622         /**
16623         * @event checkchange
16624         * Fires when a node with a checkbox's checked property changes
16625         * @param {Node} this This node
16626         * @param {Boolean} checked
16627         */
16628         "checkchange":true,
16629         /**
16630         * @event click
16631         * Fires when a node is clicked
16632         * @param {Node} node The node
16633         * @param {Roo.EventObject} e The event object
16634         */
16635         "click":true,
16636         /**
16637         * @event dblclick
16638         * Fires when a node is double clicked
16639         * @param {Node} node The node
16640         * @param {Roo.EventObject} e The event object
16641         */
16642         "dblclick":true,
16643         /**
16644         * @event contextmenu
16645         * Fires when a node is right clicked
16646         * @param {Node} node The node
16647         * @param {Roo.EventObject} e The event object
16648         */
16649         "contextmenu":true,
16650         /**
16651         * @event beforechildrenrendered
16652         * Fires right before the child nodes for a node are rendered
16653         * @param {Node} node The node
16654         */
16655         "beforechildrenrendered":true,
16656         /**
16657         * @event startdrag
16658         * Fires when a node starts being dragged
16659         * @param {Roo.tree.TreePanel} this
16660         * @param {Roo.tree.TreeNode} node
16661         * @param {event} e The raw browser event
16662         */ 
16663        "startdrag" : true,
16664        /**
16665         * @event enddrag
16666         * Fires when a drag operation is complete
16667         * @param {Roo.tree.TreePanel} this
16668         * @param {Roo.tree.TreeNode} node
16669         * @param {event} e The raw browser event
16670         */
16671        "enddrag" : true,
16672        /**
16673         * @event dragdrop
16674         * Fires when a dragged node is dropped on a valid DD target
16675         * @param {Roo.tree.TreePanel} this
16676         * @param {Roo.tree.TreeNode} node
16677         * @param {DD} dd The dd it was dropped on
16678         * @param {event} e The raw browser event
16679         */
16680        "dragdrop" : true,
16681        /**
16682         * @event beforenodedrop
16683         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16684         * passed to handlers has the following properties:<br />
16685         * <ul style="padding:5px;padding-left:16px;">
16686         * <li>tree - The TreePanel</li>
16687         * <li>target - The node being targeted for the drop</li>
16688         * <li>data - The drag data from the drag source</li>
16689         * <li>point - The point of the drop - append, above or below</li>
16690         * <li>source - The drag source</li>
16691         * <li>rawEvent - Raw mouse event</li>
16692         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16693         * to be inserted by setting them on this object.</li>
16694         * <li>cancel - Set this to true to cancel the drop.</li>
16695         * </ul>
16696         * @param {Object} dropEvent
16697         */
16698        "beforenodedrop" : true,
16699        /**
16700         * @event nodedrop
16701         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16702         * passed to handlers has the following properties:<br />
16703         * <ul style="padding:5px;padding-left:16px;">
16704         * <li>tree - The TreePanel</li>
16705         * <li>target - The node being targeted for the drop</li>
16706         * <li>data - The drag data from the drag source</li>
16707         * <li>point - The point of the drop - append, above or below</li>
16708         * <li>source - The drag source</li>
16709         * <li>rawEvent - Raw mouse event</li>
16710         * <li>dropNode - Dropped node(s).</li>
16711         * </ul>
16712         * @param {Object} dropEvent
16713         */
16714        "nodedrop" : true,
16715         /**
16716         * @event nodedragover
16717         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16718         * passed to handlers has the following properties:<br />
16719         * <ul style="padding:5px;padding-left:16px;">
16720         * <li>tree - The TreePanel</li>
16721         * <li>target - The node being targeted for the drop</li>
16722         * <li>data - The drag data from the drag source</li>
16723         * <li>point - The point of the drop - append, above or below</li>
16724         * <li>source - The drag source</li>
16725         * <li>rawEvent - Raw mouse event</li>
16726         * <li>dropNode - Drop node(s) provided by the source.</li>
16727         * <li>cancel - Set this to true to signal drop not allowed.</li>
16728         * </ul>
16729         * @param {Object} dragOverEvent
16730         */
16731        "nodedragover" : true
16732         
16733     });
16734     if(this.singleExpand){
16735        this.on("beforeexpand", this.restrictExpand, this);
16736     }
16737     if (this.editor) {
16738         this.editor.tree = this;
16739         this.editor = Roo.factory(this.editor, Roo.tree);
16740     }
16741     
16742     if (this.selModel) {
16743         this.selModel = Roo.factory(this.selModel, Roo.tree);
16744     }
16745    
16746 };
16747 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16748     rootVisible : true,
16749     animate: Roo.enableFx,
16750     lines : true,
16751     enableDD : false,
16752     hlDrop : Roo.enableFx,
16753   
16754     renderer: false,
16755     
16756     rendererTip: false,
16757     // private
16758     restrictExpand : function(node){
16759         var p = node.parentNode;
16760         if(p){
16761             if(p.expandedChild && p.expandedChild.parentNode == p){
16762                 p.expandedChild.collapse();
16763             }
16764             p.expandedChild = node;
16765         }
16766     },
16767
16768     // private override
16769     setRootNode : function(node){
16770         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16771         if(!this.rootVisible){
16772             node.ui = new Roo.tree.RootTreeNodeUI(node);
16773         }
16774         return node;
16775     },
16776
16777     /**
16778      * Returns the container element for this TreePanel
16779      */
16780     getEl : function(){
16781         return this.el;
16782     },
16783
16784     /**
16785      * Returns the default TreeLoader for this TreePanel
16786      */
16787     getLoader : function(){
16788         return this.loader;
16789     },
16790
16791     /**
16792      * Expand all nodes
16793      */
16794     expandAll : function(){
16795         this.root.expand(true);
16796     },
16797
16798     /**
16799      * Collapse all nodes
16800      */
16801     collapseAll : function(){
16802         this.root.collapse(true);
16803     },
16804
16805     /**
16806      * Returns the selection model used by this TreePanel
16807      */
16808     getSelectionModel : function(){
16809         if(!this.selModel){
16810             this.selModel = new Roo.tree.DefaultSelectionModel();
16811         }
16812         return this.selModel;
16813     },
16814
16815     /**
16816      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16817      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16818      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16819      * @return {Array}
16820      */
16821     getChecked : function(a, startNode){
16822         startNode = startNode || this.root;
16823         var r = [];
16824         var f = function(){
16825             if(this.attributes.checked){
16826                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16827             }
16828         }
16829         startNode.cascade(f);
16830         return r;
16831     },
16832
16833     /**
16834      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16835      * @param {String} path
16836      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16837      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16838      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16839      */
16840     expandPath : function(path, attr, callback){
16841         attr = attr || "id";
16842         var keys = path.split(this.pathSeparator);
16843         var curNode = this.root;
16844         if(curNode.attributes[attr] != keys[1]){ // invalid root
16845             if(callback){
16846                 callback(false, null);
16847             }
16848             return;
16849         }
16850         var index = 1;
16851         var f = function(){
16852             if(++index == keys.length){
16853                 if(callback){
16854                     callback(true, curNode);
16855                 }
16856                 return;
16857             }
16858             var c = curNode.findChild(attr, keys[index]);
16859             if(!c){
16860                 if(callback){
16861                     callback(false, curNode);
16862                 }
16863                 return;
16864             }
16865             curNode = c;
16866             c.expand(false, false, f);
16867         };
16868         curNode.expand(false, false, f);
16869     },
16870
16871     /**
16872      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16873      * @param {String} path
16874      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16875      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16876      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16877      */
16878     selectPath : function(path, attr, callback){
16879         attr = attr || "id";
16880         var keys = path.split(this.pathSeparator);
16881         var v = keys.pop();
16882         if(keys.length > 0){
16883             var f = function(success, node){
16884                 if(success && node){
16885                     var n = node.findChild(attr, v);
16886                     if(n){
16887                         n.select();
16888                         if(callback){
16889                             callback(true, n);
16890                         }
16891                     }else if(callback){
16892                         callback(false, n);
16893                     }
16894                 }else{
16895                     if(callback){
16896                         callback(false, n);
16897                     }
16898                 }
16899             };
16900             this.expandPath(keys.join(this.pathSeparator), attr, f);
16901         }else{
16902             this.root.select();
16903             if(callback){
16904                 callback(true, this.root);
16905             }
16906         }
16907     },
16908
16909     getTreeEl : function(){
16910         return this.el;
16911     },
16912
16913     /**
16914      * Trigger rendering of this TreePanel
16915      */
16916     render : function(){
16917         if (this.innerCt) {
16918             return this; // stop it rendering more than once!!
16919         }
16920         
16921         this.innerCt = this.el.createChild({tag:"ul",
16922                cls:"x-tree-root-ct " +
16923                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16924
16925         if(this.containerScroll){
16926             Roo.dd.ScrollManager.register(this.el);
16927         }
16928         if((this.enableDD || this.enableDrop) && !this.dropZone){
16929            /**
16930             * The dropZone used by this tree if drop is enabled
16931             * @type Roo.tree.TreeDropZone
16932             */
16933              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16934                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16935            });
16936         }
16937         if((this.enableDD || this.enableDrag) && !this.dragZone){
16938            /**
16939             * The dragZone used by this tree if drag is enabled
16940             * @type Roo.tree.TreeDragZone
16941             */
16942             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16943                ddGroup: this.ddGroup || "TreeDD",
16944                scroll: this.ddScroll
16945            });
16946         }
16947         this.getSelectionModel().init(this);
16948         if (!this.root) {
16949             Roo.log("ROOT not set in tree");
16950             return this;
16951         }
16952         this.root.render();
16953         if(!this.rootVisible){
16954             this.root.renderChildren();
16955         }
16956         return this;
16957     }
16958 });/*
16959  * Based on:
16960  * Ext JS Library 1.1.1
16961  * Copyright(c) 2006-2007, Ext JS, LLC.
16962  *
16963  * Originally Released Under LGPL - original licence link has changed is not relivant.
16964  *
16965  * Fork - LGPL
16966  * <script type="text/javascript">
16967  */
16968  
16969
16970 /**
16971  * @class Roo.tree.DefaultSelectionModel
16972  * @extends Roo.util.Observable
16973  * The default single selection for a TreePanel.
16974  * @param {Object} cfg Configuration
16975  */
16976 Roo.tree.DefaultSelectionModel = function(cfg){
16977    this.selNode = null;
16978    
16979    
16980    
16981    this.addEvents({
16982        /**
16983         * @event selectionchange
16984         * Fires when the selected node changes
16985         * @param {DefaultSelectionModel} this
16986         * @param {TreeNode} node the new selection
16987         */
16988        "selectionchange" : true,
16989
16990        /**
16991         * @event beforeselect
16992         * Fires before the selected node changes, return false to cancel the change
16993         * @param {DefaultSelectionModel} this
16994         * @param {TreeNode} node the new selection
16995         * @param {TreeNode} node the old selection
16996         */
16997        "beforeselect" : true
16998    });
16999    
17000     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17001 };
17002
17003 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17004     init : function(tree){
17005         this.tree = tree;
17006         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17007         tree.on("click", this.onNodeClick, this);
17008     },
17009     
17010     onNodeClick : function(node, e){
17011         if (e.ctrlKey && this.selNode == node)  {
17012             this.unselect(node);
17013             return;
17014         }
17015         this.select(node);
17016     },
17017     
17018     /**
17019      * Select a node.
17020      * @param {TreeNode} node The node to select
17021      * @return {TreeNode} The selected node
17022      */
17023     select : function(node){
17024         var last = this.selNode;
17025         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17026             if(last){
17027                 last.ui.onSelectedChange(false);
17028             }
17029             this.selNode = node;
17030             node.ui.onSelectedChange(true);
17031             this.fireEvent("selectionchange", this, node, last);
17032         }
17033         return node;
17034     },
17035     
17036     /**
17037      * Deselect a node.
17038      * @param {TreeNode} node The node to unselect
17039      */
17040     unselect : function(node){
17041         if(this.selNode == node){
17042             this.clearSelections();
17043         }    
17044     },
17045     
17046     /**
17047      * Clear all selections
17048      */
17049     clearSelections : function(){
17050         var n = this.selNode;
17051         if(n){
17052             n.ui.onSelectedChange(false);
17053             this.selNode = null;
17054             this.fireEvent("selectionchange", this, null);
17055         }
17056         return n;
17057     },
17058     
17059     /**
17060      * Get the selected node
17061      * @return {TreeNode} The selected node
17062      */
17063     getSelectedNode : function(){
17064         return this.selNode;    
17065     },
17066     
17067     /**
17068      * Returns true if the node is selected
17069      * @param {TreeNode} node The node to check
17070      * @return {Boolean}
17071      */
17072     isSelected : function(node){
17073         return this.selNode == node;  
17074     },
17075
17076     /**
17077      * Selects the node above the selected node in the tree, intelligently walking the nodes
17078      * @return TreeNode The new selection
17079      */
17080     selectPrevious : function(){
17081         var s = this.selNode || this.lastSelNode;
17082         if(!s){
17083             return null;
17084         }
17085         var ps = s.previousSibling;
17086         if(ps){
17087             if(!ps.isExpanded() || ps.childNodes.length < 1){
17088                 return this.select(ps);
17089             } else{
17090                 var lc = ps.lastChild;
17091                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17092                     lc = lc.lastChild;
17093                 }
17094                 return this.select(lc);
17095             }
17096         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17097             return this.select(s.parentNode);
17098         }
17099         return null;
17100     },
17101
17102     /**
17103      * Selects the node above the selected node in the tree, intelligently walking the nodes
17104      * @return TreeNode The new selection
17105      */
17106     selectNext : function(){
17107         var s = this.selNode || this.lastSelNode;
17108         if(!s){
17109             return null;
17110         }
17111         if(s.firstChild && s.isExpanded()){
17112              return this.select(s.firstChild);
17113          }else if(s.nextSibling){
17114              return this.select(s.nextSibling);
17115          }else if(s.parentNode){
17116             var newS = null;
17117             s.parentNode.bubble(function(){
17118                 if(this.nextSibling){
17119                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17120                     return false;
17121                 }
17122             });
17123             return newS;
17124          }
17125         return null;
17126     },
17127
17128     onKeyDown : function(e){
17129         var s = this.selNode || this.lastSelNode;
17130         // undesirable, but required
17131         var sm = this;
17132         if(!s){
17133             return;
17134         }
17135         var k = e.getKey();
17136         switch(k){
17137              case e.DOWN:
17138                  e.stopEvent();
17139                  this.selectNext();
17140              break;
17141              case e.UP:
17142                  e.stopEvent();
17143                  this.selectPrevious();
17144              break;
17145              case e.RIGHT:
17146                  e.preventDefault();
17147                  if(s.hasChildNodes()){
17148                      if(!s.isExpanded()){
17149                          s.expand();
17150                      }else if(s.firstChild){
17151                          this.select(s.firstChild, e);
17152                      }
17153                  }
17154              break;
17155              case e.LEFT:
17156                  e.preventDefault();
17157                  if(s.hasChildNodes() && s.isExpanded()){
17158                      s.collapse();
17159                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17160                      this.select(s.parentNode, e);
17161                  }
17162              break;
17163         };
17164     }
17165 });
17166
17167 /**
17168  * @class Roo.tree.MultiSelectionModel
17169  * @extends Roo.util.Observable
17170  * Multi selection for a TreePanel.
17171  * @param {Object} cfg Configuration
17172  */
17173 Roo.tree.MultiSelectionModel = function(){
17174    this.selNodes = [];
17175    this.selMap = {};
17176    this.addEvents({
17177        /**
17178         * @event selectionchange
17179         * Fires when the selected nodes change
17180         * @param {MultiSelectionModel} this
17181         * @param {Array} nodes Array of the selected nodes
17182         */
17183        "selectionchange" : true
17184    });
17185    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17186    
17187 };
17188
17189 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17190     init : function(tree){
17191         this.tree = tree;
17192         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17193         tree.on("click", this.onNodeClick, this);
17194     },
17195     
17196     onNodeClick : function(node, e){
17197         this.select(node, e, e.ctrlKey);
17198     },
17199     
17200     /**
17201      * Select a node.
17202      * @param {TreeNode} node The node to select
17203      * @param {EventObject} e (optional) An event associated with the selection
17204      * @param {Boolean} keepExisting True to retain existing selections
17205      * @return {TreeNode} The selected node
17206      */
17207     select : function(node, e, keepExisting){
17208         if(keepExisting !== true){
17209             this.clearSelections(true);
17210         }
17211         if(this.isSelected(node)){
17212             this.lastSelNode = node;
17213             return node;
17214         }
17215         this.selNodes.push(node);
17216         this.selMap[node.id] = node;
17217         this.lastSelNode = node;
17218         node.ui.onSelectedChange(true);
17219         this.fireEvent("selectionchange", this, this.selNodes);
17220         return node;
17221     },
17222     
17223     /**
17224      * Deselect a node.
17225      * @param {TreeNode} node The node to unselect
17226      */
17227     unselect : function(node){
17228         if(this.selMap[node.id]){
17229             node.ui.onSelectedChange(false);
17230             var sn = this.selNodes;
17231             var index = -1;
17232             if(sn.indexOf){
17233                 index = sn.indexOf(node);
17234             }else{
17235                 for(var i = 0, len = sn.length; i < len; i++){
17236                     if(sn[i] == node){
17237                         index = i;
17238                         break;
17239                     }
17240                 }
17241             }
17242             if(index != -1){
17243                 this.selNodes.splice(index, 1);
17244             }
17245             delete this.selMap[node.id];
17246             this.fireEvent("selectionchange", this, this.selNodes);
17247         }
17248     },
17249     
17250     /**
17251      * Clear all selections
17252      */
17253     clearSelections : function(suppressEvent){
17254         var sn = this.selNodes;
17255         if(sn.length > 0){
17256             for(var i = 0, len = sn.length; i < len; i++){
17257                 sn[i].ui.onSelectedChange(false);
17258             }
17259             this.selNodes = [];
17260             this.selMap = {};
17261             if(suppressEvent !== true){
17262                 this.fireEvent("selectionchange", this, this.selNodes);
17263             }
17264         }
17265     },
17266     
17267     /**
17268      * Returns true if the node is selected
17269      * @param {TreeNode} node The node to check
17270      * @return {Boolean}
17271      */
17272     isSelected : function(node){
17273         return this.selMap[node.id] ? true : false;  
17274     },
17275     
17276     /**
17277      * Returns an array of the selected nodes
17278      * @return {Array}
17279      */
17280     getSelectedNodes : function(){
17281         return this.selNodes;    
17282     },
17283
17284     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17285
17286     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17287
17288     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17289 });/*
17290  * Based on:
17291  * Ext JS Library 1.1.1
17292  * Copyright(c) 2006-2007, Ext JS, LLC.
17293  *
17294  * Originally Released Under LGPL - original licence link has changed is not relivant.
17295  *
17296  * Fork - LGPL
17297  * <script type="text/javascript">
17298  */
17299  
17300 /**
17301  * @class Roo.tree.TreeNode
17302  * @extends Roo.data.Node
17303  * @cfg {String} text The text for this node
17304  * @cfg {Boolean} expanded true to start the node expanded
17305  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17306  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17307  * @cfg {Boolean} disabled true to start the node disabled
17308  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17309  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17310  * @cfg {String} cls A css class to be added to the node
17311  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17312  * @cfg {String} href URL of the link used for the node (defaults to #)
17313  * @cfg {String} hrefTarget target frame for the link
17314  * @cfg {String} qtip An Ext QuickTip for the node
17315  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17316  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17317  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17318  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17319  * (defaults to undefined with no checkbox rendered)
17320  * @constructor
17321  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17322  */
17323 Roo.tree.TreeNode = function(attributes){
17324     attributes = attributes || {};
17325     if(typeof attributes == "string"){
17326         attributes = {text: attributes};
17327     }
17328     this.childrenRendered = false;
17329     this.rendered = false;
17330     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17331     this.expanded = attributes.expanded === true;
17332     this.isTarget = attributes.isTarget !== false;
17333     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17334     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17335
17336     /**
17337      * Read-only. The text for this node. To change it use setText().
17338      * @type String
17339      */
17340     this.text = attributes.text;
17341     /**
17342      * True if this node is disabled.
17343      * @type Boolean
17344      */
17345     this.disabled = attributes.disabled === true;
17346
17347     this.addEvents({
17348         /**
17349         * @event textchange
17350         * Fires when the text for this node is changed
17351         * @param {Node} this This node
17352         * @param {String} text The new text
17353         * @param {String} oldText The old text
17354         */
17355         "textchange" : true,
17356         /**
17357         * @event beforeexpand
17358         * Fires before this node is expanded, return false to cancel.
17359         * @param {Node} this This node
17360         * @param {Boolean} deep
17361         * @param {Boolean} anim
17362         */
17363         "beforeexpand" : true,
17364         /**
17365         * @event beforecollapse
17366         * Fires before this node is collapsed, return false to cancel.
17367         * @param {Node} this This node
17368         * @param {Boolean} deep
17369         * @param {Boolean} anim
17370         */
17371         "beforecollapse" : true,
17372         /**
17373         * @event expand
17374         * Fires when this node is expanded
17375         * @param {Node} this This node
17376         */
17377         "expand" : true,
17378         /**
17379         * @event disabledchange
17380         * Fires when the disabled status of this node changes
17381         * @param {Node} this This node
17382         * @param {Boolean} disabled
17383         */
17384         "disabledchange" : true,
17385         /**
17386         * @event collapse
17387         * Fires when this node is collapsed
17388         * @param {Node} this This node
17389         */
17390         "collapse" : true,
17391         /**
17392         * @event beforeclick
17393         * Fires before click processing. Return false to cancel the default action.
17394         * @param {Node} this This node
17395         * @param {Roo.EventObject} e The event object
17396         */
17397         "beforeclick":true,
17398         /**
17399         * @event checkchange
17400         * Fires when a node with a checkbox's checked property changes
17401         * @param {Node} this This node
17402         * @param {Boolean} checked
17403         */
17404         "checkchange":true,
17405         /**
17406         * @event click
17407         * Fires when this node is clicked
17408         * @param {Node} this This node
17409         * @param {Roo.EventObject} e The event object
17410         */
17411         "click":true,
17412         /**
17413         * @event dblclick
17414         * Fires when this node is double clicked
17415         * @param {Node} this This node
17416         * @param {Roo.EventObject} e The event object
17417         */
17418         "dblclick":true,
17419         /**
17420         * @event contextmenu
17421         * Fires when this node is right clicked
17422         * @param {Node} this This node
17423         * @param {Roo.EventObject} e The event object
17424         */
17425         "contextmenu":true,
17426         /**
17427         * @event beforechildrenrendered
17428         * Fires right before the child nodes for this node are rendered
17429         * @param {Node} this This node
17430         */
17431         "beforechildrenrendered":true
17432     });
17433
17434     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17435
17436     /**
17437      * Read-only. The UI for this node
17438      * @type TreeNodeUI
17439      */
17440     this.ui = new uiClass(this);
17441     
17442     // finally support items[]
17443     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17444         return;
17445     }
17446     
17447     
17448     Roo.each(this.attributes.items, function(c) {
17449         this.appendChild(Roo.factory(c,Roo.Tree));
17450     }, this);
17451     delete this.attributes.items;
17452     
17453     
17454     
17455 };
17456 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17457     preventHScroll: true,
17458     /**
17459      * Returns true if this node is expanded
17460      * @return {Boolean}
17461      */
17462     isExpanded : function(){
17463         return this.expanded;
17464     },
17465
17466     /**
17467      * Returns the UI object for this node
17468      * @return {TreeNodeUI}
17469      */
17470     getUI : function(){
17471         return this.ui;
17472     },
17473
17474     // private override
17475     setFirstChild : function(node){
17476         var of = this.firstChild;
17477         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17478         if(this.childrenRendered && of && node != of){
17479             of.renderIndent(true, true);
17480         }
17481         if(this.rendered){
17482             this.renderIndent(true, true);
17483         }
17484     },
17485
17486     // private override
17487     setLastChild : function(node){
17488         var ol = this.lastChild;
17489         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17490         if(this.childrenRendered && ol && node != ol){
17491             ol.renderIndent(true, true);
17492         }
17493         if(this.rendered){
17494             this.renderIndent(true, true);
17495         }
17496     },
17497
17498     // these methods are overridden to provide lazy rendering support
17499     // private override
17500     appendChild : function()
17501     {
17502         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17503         if(node && this.childrenRendered){
17504             node.render();
17505         }
17506         this.ui.updateExpandIcon();
17507         return node;
17508     },
17509
17510     // private override
17511     removeChild : function(node){
17512         this.ownerTree.getSelectionModel().unselect(node);
17513         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17514         // if it's been rendered remove dom node
17515         if(this.childrenRendered){
17516             node.ui.remove();
17517         }
17518         if(this.childNodes.length < 1){
17519             this.collapse(false, false);
17520         }else{
17521             this.ui.updateExpandIcon();
17522         }
17523         if(!this.firstChild) {
17524             this.childrenRendered = false;
17525         }
17526         return node;
17527     },
17528
17529     // private override
17530     insertBefore : function(node, refNode){
17531         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17532         if(newNode && refNode && this.childrenRendered){
17533             node.render();
17534         }
17535         this.ui.updateExpandIcon();
17536         return newNode;
17537     },
17538
17539     /**
17540      * Sets the text for this node
17541      * @param {String} text
17542      */
17543     setText : function(text){
17544         var oldText = this.text;
17545         this.text = text;
17546         this.attributes.text = text;
17547         if(this.rendered){ // event without subscribing
17548             this.ui.onTextChange(this, text, oldText);
17549         }
17550         this.fireEvent("textchange", this, text, oldText);
17551     },
17552
17553     /**
17554      * Triggers selection of this node
17555      */
17556     select : function(){
17557         this.getOwnerTree().getSelectionModel().select(this);
17558     },
17559
17560     /**
17561      * Triggers deselection of this node
17562      */
17563     unselect : function(){
17564         this.getOwnerTree().getSelectionModel().unselect(this);
17565     },
17566
17567     /**
17568      * Returns true if this node is selected
17569      * @return {Boolean}
17570      */
17571     isSelected : function(){
17572         return this.getOwnerTree().getSelectionModel().isSelected(this);
17573     },
17574
17575     /**
17576      * Expand this node.
17577      * @param {Boolean} deep (optional) True to expand all children as well
17578      * @param {Boolean} anim (optional) false to cancel the default animation
17579      * @param {Function} callback (optional) A callback to be called when
17580      * expanding this node completes (does not wait for deep expand to complete).
17581      * Called with 1 parameter, this node.
17582      */
17583     expand : function(deep, anim, callback){
17584         if(!this.expanded){
17585             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17586                 return;
17587             }
17588             if(!this.childrenRendered){
17589                 this.renderChildren();
17590             }
17591             this.expanded = true;
17592             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17593                 this.ui.animExpand(function(){
17594                     this.fireEvent("expand", this);
17595                     if(typeof callback == "function"){
17596                         callback(this);
17597                     }
17598                     if(deep === true){
17599                         this.expandChildNodes(true);
17600                     }
17601                 }.createDelegate(this));
17602                 return;
17603             }else{
17604                 this.ui.expand();
17605                 this.fireEvent("expand", this);
17606                 if(typeof callback == "function"){
17607                     callback(this);
17608                 }
17609             }
17610         }else{
17611            if(typeof callback == "function"){
17612                callback(this);
17613            }
17614         }
17615         if(deep === true){
17616             this.expandChildNodes(true);
17617         }
17618     },
17619
17620     isHiddenRoot : function(){
17621         return this.isRoot && !this.getOwnerTree().rootVisible;
17622     },
17623
17624     /**
17625      * Collapse this node.
17626      * @param {Boolean} deep (optional) True to collapse all children as well
17627      * @param {Boolean} anim (optional) false to cancel the default animation
17628      */
17629     collapse : function(deep, anim){
17630         if(this.expanded && !this.isHiddenRoot()){
17631             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17632                 return;
17633             }
17634             this.expanded = false;
17635             if((this.getOwnerTree().animate && anim !== false) || anim){
17636                 this.ui.animCollapse(function(){
17637                     this.fireEvent("collapse", this);
17638                     if(deep === true){
17639                         this.collapseChildNodes(true);
17640                     }
17641                 }.createDelegate(this));
17642                 return;
17643             }else{
17644                 this.ui.collapse();
17645                 this.fireEvent("collapse", this);
17646             }
17647         }
17648         if(deep === true){
17649             var cs = this.childNodes;
17650             for(var i = 0, len = cs.length; i < len; i++) {
17651                 cs[i].collapse(true, false);
17652             }
17653         }
17654     },
17655
17656     // private
17657     delayedExpand : function(delay){
17658         if(!this.expandProcId){
17659             this.expandProcId = this.expand.defer(delay, this);
17660         }
17661     },
17662
17663     // private
17664     cancelExpand : function(){
17665         if(this.expandProcId){
17666             clearTimeout(this.expandProcId);
17667         }
17668         this.expandProcId = false;
17669     },
17670
17671     /**
17672      * Toggles expanded/collapsed state of the node
17673      */
17674     toggle : function(){
17675         if(this.expanded){
17676             this.collapse();
17677         }else{
17678             this.expand();
17679         }
17680     },
17681
17682     /**
17683      * Ensures all parent nodes are expanded
17684      */
17685     ensureVisible : function(callback){
17686         var tree = this.getOwnerTree();
17687         tree.expandPath(this.parentNode.getPath(), false, function(){
17688             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17689             Roo.callback(callback);
17690         }.createDelegate(this));
17691     },
17692
17693     /**
17694      * Expand all child nodes
17695      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17696      */
17697     expandChildNodes : function(deep){
17698         var cs = this.childNodes;
17699         for(var i = 0, len = cs.length; i < len; i++) {
17700                 cs[i].expand(deep);
17701         }
17702     },
17703
17704     /**
17705      * Collapse all child nodes
17706      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17707      */
17708     collapseChildNodes : function(deep){
17709         var cs = this.childNodes;
17710         for(var i = 0, len = cs.length; i < len; i++) {
17711                 cs[i].collapse(deep);
17712         }
17713     },
17714
17715     /**
17716      * Disables this node
17717      */
17718     disable : function(){
17719         this.disabled = true;
17720         this.unselect();
17721         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17722             this.ui.onDisableChange(this, true);
17723         }
17724         this.fireEvent("disabledchange", this, true);
17725     },
17726
17727     /**
17728      * Enables this node
17729      */
17730     enable : function(){
17731         this.disabled = false;
17732         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17733             this.ui.onDisableChange(this, false);
17734         }
17735         this.fireEvent("disabledchange", this, false);
17736     },
17737
17738     // private
17739     renderChildren : function(suppressEvent){
17740         if(suppressEvent !== false){
17741             this.fireEvent("beforechildrenrendered", this);
17742         }
17743         var cs = this.childNodes;
17744         for(var i = 0, len = cs.length; i < len; i++){
17745             cs[i].render(true);
17746         }
17747         this.childrenRendered = true;
17748     },
17749
17750     // private
17751     sort : function(fn, scope){
17752         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17753         if(this.childrenRendered){
17754             var cs = this.childNodes;
17755             for(var i = 0, len = cs.length; i < len; i++){
17756                 cs[i].render(true);
17757             }
17758         }
17759     },
17760
17761     // private
17762     render : function(bulkRender){
17763         this.ui.render(bulkRender);
17764         if(!this.rendered){
17765             this.rendered = true;
17766             if(this.expanded){
17767                 this.expanded = false;
17768                 this.expand(false, false);
17769             }
17770         }
17771     },
17772
17773     // private
17774     renderIndent : function(deep, refresh){
17775         if(refresh){
17776             this.ui.childIndent = null;
17777         }
17778         this.ui.renderIndent();
17779         if(deep === true && this.childrenRendered){
17780             var cs = this.childNodes;
17781             for(var i = 0, len = cs.length; i < len; i++){
17782                 cs[i].renderIndent(true, refresh);
17783             }
17784         }
17785     }
17786 });/*
17787  * Based on:
17788  * Ext JS Library 1.1.1
17789  * Copyright(c) 2006-2007, Ext JS, LLC.
17790  *
17791  * Originally Released Under LGPL - original licence link has changed is not relivant.
17792  *
17793  * Fork - LGPL
17794  * <script type="text/javascript">
17795  */
17796  
17797 /**
17798  * @class Roo.tree.AsyncTreeNode
17799  * @extends Roo.tree.TreeNode
17800  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17801  * @constructor
17802  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17803  */
17804  Roo.tree.AsyncTreeNode = function(config){
17805     this.loaded = false;
17806     this.loading = false;
17807     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17808     /**
17809     * @event beforeload
17810     * Fires before this node is loaded, return false to cancel
17811     * @param {Node} this This node
17812     */
17813     this.addEvents({'beforeload':true, 'load': true});
17814     /**
17815     * @event load
17816     * Fires when this node is loaded
17817     * @param {Node} this This node
17818     */
17819     /**
17820      * The loader used by this node (defaults to using the tree's defined loader)
17821      * @type TreeLoader
17822      * @property loader
17823      */
17824 };
17825 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17826     expand : function(deep, anim, callback){
17827         if(this.loading){ // if an async load is already running, waiting til it's done
17828             var timer;
17829             var f = function(){
17830                 if(!this.loading){ // done loading
17831                     clearInterval(timer);
17832                     this.expand(deep, anim, callback);
17833                 }
17834             }.createDelegate(this);
17835             timer = setInterval(f, 200);
17836             return;
17837         }
17838         if(!this.loaded){
17839             if(this.fireEvent("beforeload", this) === false){
17840                 return;
17841             }
17842             this.loading = true;
17843             this.ui.beforeLoad(this);
17844             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17845             if(loader){
17846                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17847                 return;
17848             }
17849         }
17850         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17851     },
17852     
17853     /**
17854      * Returns true if this node is currently loading
17855      * @return {Boolean}
17856      */
17857     isLoading : function(){
17858         return this.loading;  
17859     },
17860     
17861     loadComplete : function(deep, anim, callback){
17862         this.loading = false;
17863         this.loaded = true;
17864         this.ui.afterLoad(this);
17865         this.fireEvent("load", this);
17866         this.expand(deep, anim, callback);
17867     },
17868     
17869     /**
17870      * Returns true if this node has been loaded
17871      * @return {Boolean}
17872      */
17873     isLoaded : function(){
17874         return this.loaded;
17875     },
17876     
17877     hasChildNodes : function(){
17878         if(!this.isLeaf() && !this.loaded){
17879             return true;
17880         }else{
17881             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17882         }
17883     },
17884
17885     /**
17886      * Trigger a reload for this node
17887      * @param {Function} callback
17888      */
17889     reload : function(callback){
17890         this.collapse(false, false);
17891         while(this.firstChild){
17892             this.removeChild(this.firstChild);
17893         }
17894         this.childrenRendered = false;
17895         this.loaded = false;
17896         if(this.isHiddenRoot()){
17897             this.expanded = false;
17898         }
17899         this.expand(false, false, callback);
17900     }
17901 });/*
17902  * Based on:
17903  * Ext JS Library 1.1.1
17904  * Copyright(c) 2006-2007, Ext JS, LLC.
17905  *
17906  * Originally Released Under LGPL - original licence link has changed is not relivant.
17907  *
17908  * Fork - LGPL
17909  * <script type="text/javascript">
17910  */
17911  
17912 /**
17913  * @class Roo.tree.TreeNodeUI
17914  * @constructor
17915  * @param {Object} node The node to render
17916  * The TreeNode UI implementation is separate from the
17917  * tree implementation. Unless you are customizing the tree UI,
17918  * you should never have to use this directly.
17919  */
17920 Roo.tree.TreeNodeUI = function(node){
17921     this.node = node;
17922     this.rendered = false;
17923     this.animating = false;
17924     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17925 };
17926
17927 Roo.tree.TreeNodeUI.prototype = {
17928     removeChild : function(node){
17929         if(this.rendered){
17930             this.ctNode.removeChild(node.ui.getEl());
17931         }
17932     },
17933
17934     beforeLoad : function(){
17935          this.addClass("x-tree-node-loading");
17936     },
17937
17938     afterLoad : function(){
17939          this.removeClass("x-tree-node-loading");
17940     },
17941
17942     onTextChange : function(node, text, oldText){
17943         if(this.rendered){
17944             this.textNode.innerHTML = text;
17945         }
17946     },
17947
17948     onDisableChange : function(node, state){
17949         this.disabled = state;
17950         if(state){
17951             this.addClass("x-tree-node-disabled");
17952         }else{
17953             this.removeClass("x-tree-node-disabled");
17954         }
17955     },
17956
17957     onSelectedChange : function(state){
17958         if(state){
17959             this.focus();
17960             this.addClass("x-tree-selected");
17961         }else{
17962             //this.blur();
17963             this.removeClass("x-tree-selected");
17964         }
17965     },
17966
17967     onMove : function(tree, node, oldParent, newParent, index, refNode){
17968         this.childIndent = null;
17969         if(this.rendered){
17970             var targetNode = newParent.ui.getContainer();
17971             if(!targetNode){//target not rendered
17972                 this.holder = document.createElement("div");
17973                 this.holder.appendChild(this.wrap);
17974                 return;
17975             }
17976             var insertBefore = refNode ? refNode.ui.getEl() : null;
17977             if(insertBefore){
17978                 targetNode.insertBefore(this.wrap, insertBefore);
17979             }else{
17980                 targetNode.appendChild(this.wrap);
17981             }
17982             this.node.renderIndent(true);
17983         }
17984     },
17985
17986     addClass : function(cls){
17987         if(this.elNode){
17988             Roo.fly(this.elNode).addClass(cls);
17989         }
17990     },
17991
17992     removeClass : function(cls){
17993         if(this.elNode){
17994             Roo.fly(this.elNode).removeClass(cls);
17995         }
17996     },
17997
17998     remove : function(){
17999         if(this.rendered){
18000             this.holder = document.createElement("div");
18001             this.holder.appendChild(this.wrap);
18002         }
18003     },
18004
18005     fireEvent : function(){
18006         return this.node.fireEvent.apply(this.node, arguments);
18007     },
18008
18009     initEvents : function(){
18010         this.node.on("move", this.onMove, this);
18011         var E = Roo.EventManager;
18012         var a = this.anchor;
18013
18014         var el = Roo.fly(a, '_treeui');
18015
18016         if(Roo.isOpera){ // opera render bug ignores the CSS
18017             el.setStyle("text-decoration", "none");
18018         }
18019
18020         el.on("click", this.onClick, this);
18021         el.on("dblclick", this.onDblClick, this);
18022
18023         if(this.checkbox){
18024             Roo.EventManager.on(this.checkbox,
18025                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18026         }
18027
18028         el.on("contextmenu", this.onContextMenu, this);
18029
18030         var icon = Roo.fly(this.iconNode);
18031         icon.on("click", this.onClick, this);
18032         icon.on("dblclick", this.onDblClick, this);
18033         icon.on("contextmenu", this.onContextMenu, this);
18034         E.on(this.ecNode, "click", this.ecClick, this, true);
18035
18036         if(this.node.disabled){
18037             this.addClass("x-tree-node-disabled");
18038         }
18039         if(this.node.hidden){
18040             this.addClass("x-tree-node-disabled");
18041         }
18042         var ot = this.node.getOwnerTree();
18043         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18044         if(dd && (!this.node.isRoot || ot.rootVisible)){
18045             Roo.dd.Registry.register(this.elNode, {
18046                 node: this.node,
18047                 handles: this.getDDHandles(),
18048                 isHandle: false
18049             });
18050         }
18051     },
18052
18053     getDDHandles : function(){
18054         return [this.iconNode, this.textNode];
18055     },
18056
18057     hide : function(){
18058         if(this.rendered){
18059             this.wrap.style.display = "none";
18060         }
18061     },
18062
18063     show : function(){
18064         if(this.rendered){
18065             this.wrap.style.display = "";
18066         }
18067     },
18068
18069     onContextMenu : function(e){
18070         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18071             e.preventDefault();
18072             this.focus();
18073             this.fireEvent("contextmenu", this.node, e);
18074         }
18075     },
18076
18077     onClick : function(e){
18078         if(this.dropping){
18079             e.stopEvent();
18080             return;
18081         }
18082         if(this.fireEvent("beforeclick", this.node, e) !== false){
18083             if(!this.disabled && this.node.attributes.href){
18084                 this.fireEvent("click", this.node, e);
18085                 return;
18086             }
18087             e.preventDefault();
18088             if(this.disabled){
18089                 return;
18090             }
18091
18092             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18093                 this.node.toggle();
18094             }
18095
18096             this.fireEvent("click", this.node, e);
18097         }else{
18098             e.stopEvent();
18099         }
18100     },
18101
18102     onDblClick : function(e){
18103         e.preventDefault();
18104         if(this.disabled){
18105             return;
18106         }
18107         if(this.checkbox){
18108             this.toggleCheck();
18109         }
18110         if(!this.animating && this.node.hasChildNodes()){
18111             this.node.toggle();
18112         }
18113         this.fireEvent("dblclick", this.node, e);
18114     },
18115
18116     onCheckChange : function(){
18117         var checked = this.checkbox.checked;
18118         this.node.attributes.checked = checked;
18119         this.fireEvent('checkchange', this.node, checked);
18120     },
18121
18122     ecClick : function(e){
18123         if(!this.animating && this.node.hasChildNodes()){
18124             this.node.toggle();
18125         }
18126     },
18127
18128     startDrop : function(){
18129         this.dropping = true;
18130     },
18131
18132     // delayed drop so the click event doesn't get fired on a drop
18133     endDrop : function(){
18134        setTimeout(function(){
18135            this.dropping = false;
18136        }.createDelegate(this), 50);
18137     },
18138
18139     expand : function(){
18140         this.updateExpandIcon();
18141         this.ctNode.style.display = "";
18142     },
18143
18144     focus : function(){
18145         if(!this.node.preventHScroll){
18146             try{this.anchor.focus();
18147             }catch(e){}
18148         }else if(!Roo.isIE){
18149             try{
18150                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18151                 var l = noscroll.scrollLeft;
18152                 this.anchor.focus();
18153                 noscroll.scrollLeft = l;
18154             }catch(e){}
18155         }
18156     },
18157
18158     toggleCheck : function(value){
18159         var cb = this.checkbox;
18160         if(cb){
18161             cb.checked = (value === undefined ? !cb.checked : value);
18162         }
18163     },
18164
18165     blur : function(){
18166         try{
18167             this.anchor.blur();
18168         }catch(e){}
18169     },
18170
18171     animExpand : function(callback){
18172         var ct = Roo.get(this.ctNode);
18173         ct.stopFx();
18174         if(!this.node.hasChildNodes()){
18175             this.updateExpandIcon();
18176             this.ctNode.style.display = "";
18177             Roo.callback(callback);
18178             return;
18179         }
18180         this.animating = true;
18181         this.updateExpandIcon();
18182
18183         ct.slideIn('t', {
18184            callback : function(){
18185                this.animating = false;
18186                Roo.callback(callback);
18187             },
18188             scope: this,
18189             duration: this.node.ownerTree.duration || .25
18190         });
18191     },
18192
18193     highlight : function(){
18194         var tree = this.node.getOwnerTree();
18195         Roo.fly(this.wrap).highlight(
18196             tree.hlColor || "C3DAF9",
18197             {endColor: tree.hlBaseColor}
18198         );
18199     },
18200
18201     collapse : function(){
18202         this.updateExpandIcon();
18203         this.ctNode.style.display = "none";
18204     },
18205
18206     animCollapse : function(callback){
18207         var ct = Roo.get(this.ctNode);
18208         ct.enableDisplayMode('block');
18209         ct.stopFx();
18210
18211         this.animating = true;
18212         this.updateExpandIcon();
18213
18214         ct.slideOut('t', {
18215             callback : function(){
18216                this.animating = false;
18217                Roo.callback(callback);
18218             },
18219             scope: this,
18220             duration: this.node.ownerTree.duration || .25
18221         });
18222     },
18223
18224     getContainer : function(){
18225         return this.ctNode;
18226     },
18227
18228     getEl : function(){
18229         return this.wrap;
18230     },
18231
18232     appendDDGhost : function(ghostNode){
18233         ghostNode.appendChild(this.elNode.cloneNode(true));
18234     },
18235
18236     getDDRepairXY : function(){
18237         return Roo.lib.Dom.getXY(this.iconNode);
18238     },
18239
18240     onRender : function(){
18241         this.render();
18242     },
18243
18244     render : function(bulkRender){
18245         var n = this.node, a = n.attributes;
18246         var targetNode = n.parentNode ?
18247               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18248
18249         if(!this.rendered){
18250             this.rendered = true;
18251
18252             this.renderElements(n, a, targetNode, bulkRender);
18253
18254             if(a.qtip){
18255                if(this.textNode.setAttributeNS){
18256                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18257                    if(a.qtipTitle){
18258                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18259                    }
18260                }else{
18261                    this.textNode.setAttribute("ext:qtip", a.qtip);
18262                    if(a.qtipTitle){
18263                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18264                    }
18265                }
18266             }else if(a.qtipCfg){
18267                 a.qtipCfg.target = Roo.id(this.textNode);
18268                 Roo.QuickTips.register(a.qtipCfg);
18269             }
18270             this.initEvents();
18271             if(!this.node.expanded){
18272                 this.updateExpandIcon();
18273             }
18274         }else{
18275             if(bulkRender === true) {
18276                 targetNode.appendChild(this.wrap);
18277             }
18278         }
18279     },
18280
18281     renderElements : function(n, a, targetNode, bulkRender)
18282     {
18283         // add some indent caching, this helps performance when rendering a large tree
18284         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18285         var t = n.getOwnerTree();
18286         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18287         if (typeof(n.attributes.html) != 'undefined') {
18288             txt = n.attributes.html;
18289         }
18290         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18291         var cb = typeof a.checked == 'boolean';
18292         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18293         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18294             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18295             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18296             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18297             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18298             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18299              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18300                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18301             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18302             "</li>"];
18303
18304         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18305             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18306                                 n.nextSibling.ui.getEl(), buf.join(""));
18307         }else{
18308             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18309         }
18310
18311         this.elNode = this.wrap.childNodes[0];
18312         this.ctNode = this.wrap.childNodes[1];
18313         var cs = this.elNode.childNodes;
18314         this.indentNode = cs[0];
18315         this.ecNode = cs[1];
18316         this.iconNode = cs[2];
18317         var index = 3;
18318         if(cb){
18319             this.checkbox = cs[3];
18320             index++;
18321         }
18322         this.anchor = cs[index];
18323         this.textNode = cs[index].firstChild;
18324     },
18325
18326     getAnchor : function(){
18327         return this.anchor;
18328     },
18329
18330     getTextEl : function(){
18331         return this.textNode;
18332     },
18333
18334     getIconEl : function(){
18335         return this.iconNode;
18336     },
18337
18338     isChecked : function(){
18339         return this.checkbox ? this.checkbox.checked : false;
18340     },
18341
18342     updateExpandIcon : function(){
18343         if(this.rendered){
18344             var n = this.node, c1, c2;
18345             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18346             var hasChild = n.hasChildNodes();
18347             if(hasChild){
18348                 if(n.expanded){
18349                     cls += "-minus";
18350                     c1 = "x-tree-node-collapsed";
18351                     c2 = "x-tree-node-expanded";
18352                 }else{
18353                     cls += "-plus";
18354                     c1 = "x-tree-node-expanded";
18355                     c2 = "x-tree-node-collapsed";
18356                 }
18357                 if(this.wasLeaf){
18358                     this.removeClass("x-tree-node-leaf");
18359                     this.wasLeaf = false;
18360                 }
18361                 if(this.c1 != c1 || this.c2 != c2){
18362                     Roo.fly(this.elNode).replaceClass(c1, c2);
18363                     this.c1 = c1; this.c2 = c2;
18364                 }
18365             }else{
18366                 // this changes non-leafs into leafs if they have no children.
18367                 // it's not very rational behaviour..
18368                 
18369                 if(!this.wasLeaf && this.node.leaf){
18370                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18371                     delete this.c1;
18372                     delete this.c2;
18373                     this.wasLeaf = true;
18374                 }
18375             }
18376             var ecc = "x-tree-ec-icon "+cls;
18377             if(this.ecc != ecc){
18378                 this.ecNode.className = ecc;
18379                 this.ecc = ecc;
18380             }
18381         }
18382     },
18383
18384     getChildIndent : function(){
18385         if(!this.childIndent){
18386             var buf = [];
18387             var p = this.node;
18388             while(p){
18389                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18390                     if(!p.isLast()) {
18391                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18392                     } else {
18393                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18394                     }
18395                 }
18396                 p = p.parentNode;
18397             }
18398             this.childIndent = buf.join("");
18399         }
18400         return this.childIndent;
18401     },
18402
18403     renderIndent : function(){
18404         if(this.rendered){
18405             var indent = "";
18406             var p = this.node.parentNode;
18407             if(p){
18408                 indent = p.ui.getChildIndent();
18409             }
18410             if(this.indentMarkup != indent){ // don't rerender if not required
18411                 this.indentNode.innerHTML = indent;
18412                 this.indentMarkup = indent;
18413             }
18414             this.updateExpandIcon();
18415         }
18416     }
18417 };
18418
18419 Roo.tree.RootTreeNodeUI = function(){
18420     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18421 };
18422 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18423     render : function(){
18424         if(!this.rendered){
18425             var targetNode = this.node.ownerTree.innerCt.dom;
18426             this.node.expanded = true;
18427             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18428             this.wrap = this.ctNode = targetNode.firstChild;
18429         }
18430     },
18431     collapse : function(){
18432     },
18433     expand : function(){
18434     }
18435 });/*
18436  * Based on:
18437  * Ext JS Library 1.1.1
18438  * Copyright(c) 2006-2007, Ext JS, LLC.
18439  *
18440  * Originally Released Under LGPL - original licence link has changed is not relivant.
18441  *
18442  * Fork - LGPL
18443  * <script type="text/javascript">
18444  */
18445 /**
18446  * @class Roo.tree.TreeLoader
18447  * @extends Roo.util.Observable
18448  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18449  * nodes from a specified URL. The response must be a javascript Array definition
18450  * who's elements are node definition objects. eg:
18451  * <pre><code>
18452 {  success : true,
18453    data :      [
18454    
18455     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18456     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18457     ]
18458 }
18459
18460
18461 </code></pre>
18462  * <br><br>
18463  * The old style respose with just an array is still supported, but not recommended.
18464  * <br><br>
18465  *
18466  * A server request is sent, and child nodes are loaded only when a node is expanded.
18467  * The loading node's id is passed to the server under the parameter name "node" to
18468  * enable the server to produce the correct child nodes.
18469  * <br><br>
18470  * To pass extra parameters, an event handler may be attached to the "beforeload"
18471  * event, and the parameters specified in the TreeLoader's baseParams property:
18472  * <pre><code>
18473     myTreeLoader.on("beforeload", function(treeLoader, node) {
18474         this.baseParams.category = node.attributes.category;
18475     }, this);
18476 </code></pre><
18477  * This would pass an HTTP parameter called "category" to the server containing
18478  * the value of the Node's "category" attribute.
18479  * @constructor
18480  * Creates a new Treeloader.
18481  * @param {Object} config A config object containing config properties.
18482  */
18483 Roo.tree.TreeLoader = function(config){
18484     this.baseParams = {};
18485     this.requestMethod = "POST";
18486     Roo.apply(this, config);
18487
18488     this.addEvents({
18489     
18490         /**
18491          * @event beforeload
18492          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18493          * @param {Object} This TreeLoader object.
18494          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18495          * @param {Object} callback The callback function specified in the {@link #load} call.
18496          */
18497         beforeload : true,
18498         /**
18499          * @event load
18500          * Fires when the node has been successfuly loaded.
18501          * @param {Object} This TreeLoader object.
18502          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18503          * @param {Object} response The response object containing the data from the server.
18504          */
18505         load : true,
18506         /**
18507          * @event loadexception
18508          * Fires if the network request failed.
18509          * @param {Object} This TreeLoader object.
18510          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18511          * @param {Object} response The response object containing the data from the server.
18512          */
18513         loadexception : true,
18514         /**
18515          * @event create
18516          * Fires before a node is created, enabling you to return custom Node types 
18517          * @param {Object} This TreeLoader object.
18518          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18519          */
18520         create : true
18521     });
18522
18523     Roo.tree.TreeLoader.superclass.constructor.call(this);
18524 };
18525
18526 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18527     /**
18528     * @cfg {String} dataUrl The URL from which to request a Json string which
18529     * specifies an array of node definition object representing the child nodes
18530     * to be loaded.
18531     */
18532     /**
18533     * @cfg {String} requestMethod either GET or POST
18534     * defaults to POST (due to BC)
18535     * to be loaded.
18536     */
18537     /**
18538     * @cfg {Object} baseParams (optional) An object containing properties which
18539     * specify HTTP parameters to be passed to each request for child nodes.
18540     */
18541     /**
18542     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18543     * created by this loader. If the attributes sent by the server have an attribute in this object,
18544     * they take priority.
18545     */
18546     /**
18547     * @cfg {Object} uiProviders (optional) An object containing properties which
18548     * 
18549     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18550     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18551     * <i>uiProvider</i> attribute of a returned child node is a string rather
18552     * than a reference to a TreeNodeUI implementation, this that string value
18553     * is used as a property name in the uiProviders object. You can define the provider named
18554     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18555     */
18556     uiProviders : {},
18557
18558     /**
18559     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18560     * child nodes before loading.
18561     */
18562     clearOnLoad : true,
18563
18564     /**
18565     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18566     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18567     * Grid query { data : [ .....] }
18568     */
18569     
18570     root : false,
18571      /**
18572     * @cfg {String} queryParam (optional) 
18573     * Name of the query as it will be passed on the querystring (defaults to 'node')
18574     * eg. the request will be ?node=[id]
18575     */
18576     
18577     
18578     queryParam: false,
18579     
18580     /**
18581      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18582      * This is called automatically when a node is expanded, but may be used to reload
18583      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18584      * @param {Roo.tree.TreeNode} node
18585      * @param {Function} callback
18586      */
18587     load : function(node, callback){
18588         if(this.clearOnLoad){
18589             while(node.firstChild){
18590                 node.removeChild(node.firstChild);
18591             }
18592         }
18593         if(node.attributes.children){ // preloaded json children
18594             var cs = node.attributes.children;
18595             for(var i = 0, len = cs.length; i < len; i++){
18596                 node.appendChild(this.createNode(cs[i]));
18597             }
18598             if(typeof callback == "function"){
18599                 callback();
18600             }
18601         }else if(this.dataUrl){
18602             this.requestData(node, callback);
18603         }
18604     },
18605
18606     getParams: function(node){
18607         var buf = [], bp = this.baseParams;
18608         for(var key in bp){
18609             if(typeof bp[key] != "function"){
18610                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18611             }
18612         }
18613         var n = this.queryParam === false ? 'node' : this.queryParam;
18614         buf.push(n + "=", encodeURIComponent(node.id));
18615         return buf.join("");
18616     },
18617
18618     requestData : function(node, callback){
18619         if(this.fireEvent("beforeload", this, node, callback) !== false){
18620             this.transId = Roo.Ajax.request({
18621                 method:this.requestMethod,
18622                 url: this.dataUrl||this.url,
18623                 success: this.handleResponse,
18624                 failure: this.handleFailure,
18625                 scope: this,
18626                 argument: {callback: callback, node: node},
18627                 params: this.getParams(node)
18628             });
18629         }else{
18630             // if the load is cancelled, make sure we notify
18631             // the node that we are done
18632             if(typeof callback == "function"){
18633                 callback();
18634             }
18635         }
18636     },
18637
18638     isLoading : function(){
18639         return this.transId ? true : false;
18640     },
18641
18642     abort : function(){
18643         if(this.isLoading()){
18644             Roo.Ajax.abort(this.transId);
18645         }
18646     },
18647
18648     // private
18649     createNode : function(attr)
18650     {
18651         // apply baseAttrs, nice idea Corey!
18652         if(this.baseAttrs){
18653             Roo.applyIf(attr, this.baseAttrs);
18654         }
18655         if(this.applyLoader !== false){
18656             attr.loader = this;
18657         }
18658         // uiProvider = depreciated..
18659         
18660         if(typeof(attr.uiProvider) == 'string'){
18661            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18662                 /**  eval:var:attr */ eval(attr.uiProvider);
18663         }
18664         if(typeof(this.uiProviders['default']) != 'undefined') {
18665             attr.uiProvider = this.uiProviders['default'];
18666         }
18667         
18668         this.fireEvent('create', this, attr);
18669         
18670         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18671         return(attr.leaf ?
18672                         new Roo.tree.TreeNode(attr) :
18673                         new Roo.tree.AsyncTreeNode(attr));
18674     },
18675
18676     processResponse : function(response, node, callback)
18677     {
18678         var json = response.responseText;
18679         try {
18680             
18681             var o = Roo.decode(json);
18682             
18683             if (this.root === false && typeof(o.success) != undefined) {
18684                 this.root = 'data'; // the default behaviour for list like data..
18685                 }
18686                 
18687             if (this.root !== false &&  !o.success) {
18688                 // it's a failure condition.
18689                 var a = response.argument;
18690                 this.fireEvent("loadexception", this, a.node, response);
18691                 Roo.log("Load failed - should have a handler really");
18692                 return;
18693             }
18694             
18695             
18696             
18697             if (this.root !== false) {
18698                  o = o[this.root];
18699             }
18700             
18701             for(var i = 0, len = o.length; i < len; i++){
18702                 var n = this.createNode(o[i]);
18703                 if(n){
18704                     node.appendChild(n);
18705                 }
18706             }
18707             if(typeof callback == "function"){
18708                 callback(this, node);
18709             }
18710         }catch(e){
18711             this.handleFailure(response);
18712         }
18713     },
18714
18715     handleResponse : function(response){
18716         this.transId = false;
18717         var a = response.argument;
18718         this.processResponse(response, a.node, a.callback);
18719         this.fireEvent("load", this, a.node, response);
18720     },
18721
18722     handleFailure : function(response)
18723     {
18724         // should handle failure better..
18725         this.transId = false;
18726         var a = response.argument;
18727         this.fireEvent("loadexception", this, a.node, response);
18728         if(typeof a.callback == "function"){
18729             a.callback(this, a.node);
18730         }
18731     }
18732 });/*
18733  * Based on:
18734  * Ext JS Library 1.1.1
18735  * Copyright(c) 2006-2007, Ext JS, LLC.
18736  *
18737  * Originally Released Under LGPL - original licence link has changed is not relivant.
18738  *
18739  * Fork - LGPL
18740  * <script type="text/javascript">
18741  */
18742
18743 /**
18744 * @class Roo.tree.TreeFilter
18745 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18746 * @param {TreePanel} tree
18747 * @param {Object} config (optional)
18748  */
18749 Roo.tree.TreeFilter = function(tree, config){
18750     this.tree = tree;
18751     this.filtered = {};
18752     Roo.apply(this, config);
18753 };
18754
18755 Roo.tree.TreeFilter.prototype = {
18756     clearBlank:false,
18757     reverse:false,
18758     autoClear:false,
18759     remove:false,
18760
18761      /**
18762      * Filter the data by a specific attribute.
18763      * @param {String/RegExp} value Either string that the attribute value
18764      * should start with or a RegExp to test against the attribute
18765      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18766      * @param {TreeNode} startNode (optional) The node to start the filter at.
18767      */
18768     filter : function(value, attr, startNode){
18769         attr = attr || "text";
18770         var f;
18771         if(typeof value == "string"){
18772             var vlen = value.length;
18773             // auto clear empty filter
18774             if(vlen == 0 && this.clearBlank){
18775                 this.clear();
18776                 return;
18777             }
18778             value = value.toLowerCase();
18779             f = function(n){
18780                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18781             };
18782         }else if(value.exec){ // regex?
18783             f = function(n){
18784                 return value.test(n.attributes[attr]);
18785             };
18786         }else{
18787             throw 'Illegal filter type, must be string or regex';
18788         }
18789         this.filterBy(f, null, startNode);
18790         },
18791
18792     /**
18793      * Filter by a function. The passed function will be called with each
18794      * node in the tree (or from the startNode). If the function returns true, the node is kept
18795      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18796      * @param {Function} fn The filter function
18797      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18798      */
18799     filterBy : function(fn, scope, startNode){
18800         startNode = startNode || this.tree.root;
18801         if(this.autoClear){
18802             this.clear();
18803         }
18804         var af = this.filtered, rv = this.reverse;
18805         var f = function(n){
18806             if(n == startNode){
18807                 return true;
18808             }
18809             if(af[n.id]){
18810                 return false;
18811             }
18812             var m = fn.call(scope || n, n);
18813             if(!m || rv){
18814                 af[n.id] = n;
18815                 n.ui.hide();
18816                 return false;
18817             }
18818             return true;
18819         };
18820         startNode.cascade(f);
18821         if(this.remove){
18822            for(var id in af){
18823                if(typeof id != "function"){
18824                    var n = af[id];
18825                    if(n && n.parentNode){
18826                        n.parentNode.removeChild(n);
18827                    }
18828                }
18829            }
18830         }
18831     },
18832
18833     /**
18834      * Clears the current filter. Note: with the "remove" option
18835      * set a filter cannot be cleared.
18836      */
18837     clear : function(){
18838         var t = this.tree;
18839         var af = this.filtered;
18840         for(var id in af){
18841             if(typeof id != "function"){
18842                 var n = af[id];
18843                 if(n){
18844                     n.ui.show();
18845                 }
18846             }
18847         }
18848         this.filtered = {};
18849     }
18850 };
18851 /*
18852  * Based on:
18853  * Ext JS Library 1.1.1
18854  * Copyright(c) 2006-2007, Ext JS, LLC.
18855  *
18856  * Originally Released Under LGPL - original licence link has changed is not relivant.
18857  *
18858  * Fork - LGPL
18859  * <script type="text/javascript">
18860  */
18861  
18862
18863 /**
18864  * @class Roo.tree.TreeSorter
18865  * Provides sorting of nodes in a TreePanel
18866  * 
18867  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18868  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18869  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18870  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18871  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18872  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18873  * @constructor
18874  * @param {TreePanel} tree
18875  * @param {Object} config
18876  */
18877 Roo.tree.TreeSorter = function(tree, config){
18878     Roo.apply(this, config);
18879     tree.on("beforechildrenrendered", this.doSort, this);
18880     tree.on("append", this.updateSort, this);
18881     tree.on("insert", this.updateSort, this);
18882     
18883     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18884     var p = this.property || "text";
18885     var sortType = this.sortType;
18886     var fs = this.folderSort;
18887     var cs = this.caseSensitive === true;
18888     var leafAttr = this.leafAttr || 'leaf';
18889
18890     this.sortFn = function(n1, n2){
18891         if(fs){
18892             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18893                 return 1;
18894             }
18895             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18896                 return -1;
18897             }
18898         }
18899         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18900         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18901         if(v1 < v2){
18902                         return dsc ? +1 : -1;
18903                 }else if(v1 > v2){
18904                         return dsc ? -1 : +1;
18905         }else{
18906                 return 0;
18907         }
18908     };
18909 };
18910
18911 Roo.tree.TreeSorter.prototype = {
18912     doSort : function(node){
18913         node.sort(this.sortFn);
18914     },
18915     
18916     compareNodes : function(n1, n2){
18917         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18918     },
18919     
18920     updateSort : function(tree, node){
18921         if(node.childrenRendered){
18922             this.doSort.defer(1, this, [node]);
18923         }
18924     }
18925 };/*
18926  * Based on:
18927  * Ext JS Library 1.1.1
18928  * Copyright(c) 2006-2007, Ext JS, LLC.
18929  *
18930  * Originally Released Under LGPL - original licence link has changed is not relivant.
18931  *
18932  * Fork - LGPL
18933  * <script type="text/javascript">
18934  */
18935
18936 if(Roo.dd.DropZone){
18937     
18938 Roo.tree.TreeDropZone = function(tree, config){
18939     this.allowParentInsert = false;
18940     this.allowContainerDrop = false;
18941     this.appendOnly = false;
18942     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18943     this.tree = tree;
18944     this.lastInsertClass = "x-tree-no-status";
18945     this.dragOverData = {};
18946 };
18947
18948 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18949     ddGroup : "TreeDD",
18950     scroll:  true,
18951     
18952     expandDelay : 1000,
18953     
18954     expandNode : function(node){
18955         if(node.hasChildNodes() && !node.isExpanded()){
18956             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18957         }
18958     },
18959     
18960     queueExpand : function(node){
18961         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18962     },
18963     
18964     cancelExpand : function(){
18965         if(this.expandProcId){
18966             clearTimeout(this.expandProcId);
18967             this.expandProcId = false;
18968         }
18969     },
18970     
18971     isValidDropPoint : function(n, pt, dd, e, data){
18972         if(!n || !data){ return false; }
18973         var targetNode = n.node;
18974         var dropNode = data.node;
18975         // default drop rules
18976         if(!(targetNode && targetNode.isTarget && pt)){
18977             return false;
18978         }
18979         if(pt == "append" && targetNode.allowChildren === false){
18980             return false;
18981         }
18982         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18983             return false;
18984         }
18985         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18986             return false;
18987         }
18988         // reuse the object
18989         var overEvent = this.dragOverData;
18990         overEvent.tree = this.tree;
18991         overEvent.target = targetNode;
18992         overEvent.data = data;
18993         overEvent.point = pt;
18994         overEvent.source = dd;
18995         overEvent.rawEvent = e;
18996         overEvent.dropNode = dropNode;
18997         overEvent.cancel = false;  
18998         var result = this.tree.fireEvent("nodedragover", overEvent);
18999         return overEvent.cancel === false && result !== false;
19000     },
19001     
19002     getDropPoint : function(e, n, dd)
19003     {
19004         var tn = n.node;
19005         if(tn.isRoot){
19006             return tn.allowChildren !== false ? "append" : false; // always append for root
19007         }
19008         var dragEl = n.ddel;
19009         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19010         var y = Roo.lib.Event.getPageY(e);
19011         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19012         
19013         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19014         var noAppend = tn.allowChildren === false;
19015         if(this.appendOnly || tn.parentNode.allowChildren === false){
19016             return noAppend ? false : "append";
19017         }
19018         var noBelow = false;
19019         if(!this.allowParentInsert){
19020             noBelow = tn.hasChildNodes() && tn.isExpanded();
19021         }
19022         var q = (b - t) / (noAppend ? 2 : 3);
19023         if(y >= t && y < (t + q)){
19024             return "above";
19025         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19026             return "below";
19027         }else{
19028             return "append";
19029         }
19030     },
19031     
19032     onNodeEnter : function(n, dd, e, data)
19033     {
19034         this.cancelExpand();
19035     },
19036     
19037     onNodeOver : function(n, dd, e, data)
19038     {
19039        
19040         var pt = this.getDropPoint(e, n, dd);
19041         var node = n.node;
19042         
19043         // auto node expand check
19044         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19045             this.queueExpand(node);
19046         }else if(pt != "append"){
19047             this.cancelExpand();
19048         }
19049         
19050         // set the insert point style on the target node
19051         var returnCls = this.dropNotAllowed;
19052         if(this.isValidDropPoint(n, pt, dd, e, data)){
19053            if(pt){
19054                var el = n.ddel;
19055                var cls;
19056                if(pt == "above"){
19057                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19058                    cls = "x-tree-drag-insert-above";
19059                }else if(pt == "below"){
19060                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19061                    cls = "x-tree-drag-insert-below";
19062                }else{
19063                    returnCls = "x-tree-drop-ok-append";
19064                    cls = "x-tree-drag-append";
19065                }
19066                if(this.lastInsertClass != cls){
19067                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19068                    this.lastInsertClass = cls;
19069                }
19070            }
19071        }
19072        return returnCls;
19073     },
19074     
19075     onNodeOut : function(n, dd, e, data){
19076         
19077         this.cancelExpand();
19078         this.removeDropIndicators(n);
19079     },
19080     
19081     onNodeDrop : function(n, dd, e, data){
19082         var point = this.getDropPoint(e, n, dd);
19083         var targetNode = n.node;
19084         targetNode.ui.startDrop();
19085         if(!this.isValidDropPoint(n, point, dd, e, data)){
19086             targetNode.ui.endDrop();
19087             return false;
19088         }
19089         // first try to find the drop node
19090         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19091         var dropEvent = {
19092             tree : this.tree,
19093             target: targetNode,
19094             data: data,
19095             point: point,
19096             source: dd,
19097             rawEvent: e,
19098             dropNode: dropNode,
19099             cancel: !dropNode   
19100         };
19101         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19102         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19103             targetNode.ui.endDrop();
19104             return false;
19105         }
19106         // allow target changing
19107         targetNode = dropEvent.target;
19108         if(point == "append" && !targetNode.isExpanded()){
19109             targetNode.expand(false, null, function(){
19110                 this.completeDrop(dropEvent);
19111             }.createDelegate(this));
19112         }else{
19113             this.completeDrop(dropEvent);
19114         }
19115         return true;
19116     },
19117     
19118     completeDrop : function(de){
19119         var ns = de.dropNode, p = de.point, t = de.target;
19120         if(!(ns instanceof Array)){
19121             ns = [ns];
19122         }
19123         var n;
19124         for(var i = 0, len = ns.length; i < len; i++){
19125             n = ns[i];
19126             if(p == "above"){
19127                 t.parentNode.insertBefore(n, t);
19128             }else if(p == "below"){
19129                 t.parentNode.insertBefore(n, t.nextSibling);
19130             }else{
19131                 t.appendChild(n);
19132             }
19133         }
19134         n.ui.focus();
19135         if(this.tree.hlDrop){
19136             n.ui.highlight();
19137         }
19138         t.ui.endDrop();
19139         this.tree.fireEvent("nodedrop", de);
19140     },
19141     
19142     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19143         if(this.tree.hlDrop){
19144             dropNode.ui.focus();
19145             dropNode.ui.highlight();
19146         }
19147         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19148     },
19149     
19150     getTree : function(){
19151         return this.tree;
19152     },
19153     
19154     removeDropIndicators : function(n){
19155         if(n && n.ddel){
19156             var el = n.ddel;
19157             Roo.fly(el).removeClass([
19158                     "x-tree-drag-insert-above",
19159                     "x-tree-drag-insert-below",
19160                     "x-tree-drag-append"]);
19161             this.lastInsertClass = "_noclass";
19162         }
19163     },
19164     
19165     beforeDragDrop : function(target, e, id){
19166         this.cancelExpand();
19167         return true;
19168     },
19169     
19170     afterRepair : function(data){
19171         if(data && Roo.enableFx){
19172             data.node.ui.highlight();
19173         }
19174         this.hideProxy();
19175     } 
19176     
19177 });
19178
19179 }
19180 /*
19181  * Based on:
19182  * Ext JS Library 1.1.1
19183  * Copyright(c) 2006-2007, Ext JS, LLC.
19184  *
19185  * Originally Released Under LGPL - original licence link has changed is not relivant.
19186  *
19187  * Fork - LGPL
19188  * <script type="text/javascript">
19189  */
19190  
19191
19192 if(Roo.dd.DragZone){
19193 Roo.tree.TreeDragZone = function(tree, config){
19194     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19195     this.tree = tree;
19196 };
19197
19198 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19199     ddGroup : "TreeDD",
19200    
19201     onBeforeDrag : function(data, e){
19202         var n = data.node;
19203         return n && n.draggable && !n.disabled;
19204     },
19205      
19206     
19207     onInitDrag : function(e){
19208         var data = this.dragData;
19209         this.tree.getSelectionModel().select(data.node);
19210         this.proxy.update("");
19211         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19212         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19213     },
19214     
19215     getRepairXY : function(e, data){
19216         return data.node.ui.getDDRepairXY();
19217     },
19218     
19219     onEndDrag : function(data, e){
19220         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19221         
19222         
19223     },
19224     
19225     onValidDrop : function(dd, e, id){
19226         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19227         this.hideProxy();
19228     },
19229     
19230     beforeInvalidDrop : function(e, id){
19231         // this scrolls the original position back into view
19232         var sm = this.tree.getSelectionModel();
19233         sm.clearSelections();
19234         sm.select(this.dragData.node);
19235     }
19236 });
19237 }/*
19238  * Based on:
19239  * Ext JS Library 1.1.1
19240  * Copyright(c) 2006-2007, Ext JS, LLC.
19241  *
19242  * Originally Released Under LGPL - original licence link has changed is not relivant.
19243  *
19244  * Fork - LGPL
19245  * <script type="text/javascript">
19246  */
19247 /**
19248  * @class Roo.tree.TreeEditor
19249  * @extends Roo.Editor
19250  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19251  * as the editor field.
19252  * @constructor
19253  * @param {Object} config (used to be the tree panel.)
19254  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19255  * 
19256  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19257  * @cfg {Roo.form.TextField|Object} field The field configuration
19258  *
19259  * 
19260  */
19261 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19262     var tree = config;
19263     var field;
19264     if (oldconfig) { // old style..
19265         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19266     } else {
19267         // new style..
19268         tree = config.tree;
19269         config.field = config.field  || {};
19270         config.field.xtype = 'TextField';
19271         field = Roo.factory(config.field, Roo.form);
19272     }
19273     config = config || {};
19274     
19275     
19276     this.addEvents({
19277         /**
19278          * @event beforenodeedit
19279          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19280          * false from the handler of this event.
19281          * @param {Editor} this
19282          * @param {Roo.tree.Node} node 
19283          */
19284         "beforenodeedit" : true
19285     });
19286     
19287     //Roo.log(config);
19288     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19289
19290     this.tree = tree;
19291
19292     tree.on('beforeclick', this.beforeNodeClick, this);
19293     tree.getTreeEl().on('mousedown', this.hide, this);
19294     this.on('complete', this.updateNode, this);
19295     this.on('beforestartedit', this.fitToTree, this);
19296     this.on('startedit', this.bindScroll, this, {delay:10});
19297     this.on('specialkey', this.onSpecialKey, this);
19298 };
19299
19300 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19301     /**
19302      * @cfg {String} alignment
19303      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19304      */
19305     alignment: "l-l",
19306     // inherit
19307     autoSize: false,
19308     /**
19309      * @cfg {Boolean} hideEl
19310      * True to hide the bound element while the editor is displayed (defaults to false)
19311      */
19312     hideEl : false,
19313     /**
19314      * @cfg {String} cls
19315      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19316      */
19317     cls: "x-small-editor x-tree-editor",
19318     /**
19319      * @cfg {Boolean} shim
19320      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19321      */
19322     shim:false,
19323     // inherit
19324     shadow:"frame",
19325     /**
19326      * @cfg {Number} maxWidth
19327      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19328      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19329      * scroll and client offsets into account prior to each edit.
19330      */
19331     maxWidth: 250,
19332
19333     editDelay : 350,
19334
19335     // private
19336     fitToTree : function(ed, el){
19337         var td = this.tree.getTreeEl().dom, nd = el.dom;
19338         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19339             td.scrollLeft = nd.offsetLeft;
19340         }
19341         var w = Math.min(
19342                 this.maxWidth,
19343                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19344         this.setSize(w, '');
19345         
19346         return this.fireEvent('beforenodeedit', this, this.editNode);
19347         
19348     },
19349
19350     // private
19351     triggerEdit : function(node){
19352         this.completeEdit();
19353         this.editNode = node;
19354         this.startEdit(node.ui.textNode, node.text);
19355     },
19356
19357     // private
19358     bindScroll : function(){
19359         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19360     },
19361
19362     // private
19363     beforeNodeClick : function(node, e){
19364         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19365         this.lastClick = new Date();
19366         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19367             e.stopEvent();
19368             this.triggerEdit(node);
19369             return false;
19370         }
19371         return true;
19372     },
19373
19374     // private
19375     updateNode : function(ed, value){
19376         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19377         this.editNode.setText(value);
19378     },
19379
19380     // private
19381     onHide : function(){
19382         Roo.tree.TreeEditor.superclass.onHide.call(this);
19383         if(this.editNode){
19384             this.editNode.ui.focus();
19385         }
19386     },
19387
19388     // private
19389     onSpecialKey : function(field, e){
19390         var k = e.getKey();
19391         if(k == e.ESC){
19392             e.stopEvent();
19393             this.cancelEdit();
19394         }else if(k == e.ENTER && !e.hasModifier()){
19395             e.stopEvent();
19396             this.completeEdit();
19397         }
19398     }
19399 });//<Script type="text/javascript">
19400 /*
19401  * Based on:
19402  * Ext JS Library 1.1.1
19403  * Copyright(c) 2006-2007, Ext JS, LLC.
19404  *
19405  * Originally Released Under LGPL - original licence link has changed is not relivant.
19406  *
19407  * Fork - LGPL
19408  * <script type="text/javascript">
19409  */
19410  
19411 /**
19412  * Not documented??? - probably should be...
19413  */
19414
19415 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19416     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19417     
19418     renderElements : function(n, a, targetNode, bulkRender){
19419         //consel.log("renderElements?");
19420         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19421
19422         var t = n.getOwnerTree();
19423         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19424         
19425         var cols = t.columns;
19426         var bw = t.borderWidth;
19427         var c = cols[0];
19428         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19429          var cb = typeof a.checked == "boolean";
19430         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19431         var colcls = 'x-t-' + tid + '-c0';
19432         var buf = [
19433             '<li class="x-tree-node">',
19434             
19435                 
19436                 '<div class="x-tree-node-el ', a.cls,'">',
19437                     // extran...
19438                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19439                 
19440                 
19441                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19442                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19443                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19444                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19445                            (a.iconCls ? ' '+a.iconCls : ''),
19446                            '" unselectable="on" />',
19447                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19448                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19449                              
19450                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19451                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19452                             '<span unselectable="on" qtip="' + tx + '">',
19453                              tx,
19454                              '</span></a>' ,
19455                     '</div>',
19456                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19457                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19458                  ];
19459         for(var i = 1, len = cols.length; i < len; i++){
19460             c = cols[i];
19461             colcls = 'x-t-' + tid + '-c' +i;
19462             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19463             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19464                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19465                       "</div>");
19466          }
19467          
19468          buf.push(
19469             '</a>',
19470             '<div class="x-clear"></div></div>',
19471             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19472             "</li>");
19473         
19474         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19475             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19476                                 n.nextSibling.ui.getEl(), buf.join(""));
19477         }else{
19478             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19479         }
19480         var el = this.wrap.firstChild;
19481         this.elRow = el;
19482         this.elNode = el.firstChild;
19483         this.ranchor = el.childNodes[1];
19484         this.ctNode = this.wrap.childNodes[1];
19485         var cs = el.firstChild.childNodes;
19486         this.indentNode = cs[0];
19487         this.ecNode = cs[1];
19488         this.iconNode = cs[2];
19489         var index = 3;
19490         if(cb){
19491             this.checkbox = cs[3];
19492             index++;
19493         }
19494         this.anchor = cs[index];
19495         
19496         this.textNode = cs[index].firstChild;
19497         
19498         //el.on("click", this.onClick, this);
19499         //el.on("dblclick", this.onDblClick, this);
19500         
19501         
19502        // console.log(this);
19503     },
19504     initEvents : function(){
19505         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19506         
19507             
19508         var a = this.ranchor;
19509
19510         var el = Roo.get(a);
19511
19512         if(Roo.isOpera){ // opera render bug ignores the CSS
19513             el.setStyle("text-decoration", "none");
19514         }
19515
19516         el.on("click", this.onClick, this);
19517         el.on("dblclick", this.onDblClick, this);
19518         el.on("contextmenu", this.onContextMenu, this);
19519         
19520     },
19521     
19522     /*onSelectedChange : function(state){
19523         if(state){
19524             this.focus();
19525             this.addClass("x-tree-selected");
19526         }else{
19527             //this.blur();
19528             this.removeClass("x-tree-selected");
19529         }
19530     },*/
19531     addClass : function(cls){
19532         if(this.elRow){
19533             Roo.fly(this.elRow).addClass(cls);
19534         }
19535         
19536     },
19537     
19538     
19539     removeClass : function(cls){
19540         if(this.elRow){
19541             Roo.fly(this.elRow).removeClass(cls);
19542         }
19543     }
19544
19545     
19546     
19547 });//<Script type="text/javascript">
19548
19549 /*
19550  * Based on:
19551  * Ext JS Library 1.1.1
19552  * Copyright(c) 2006-2007, Ext JS, LLC.
19553  *
19554  * Originally Released Under LGPL - original licence link has changed is not relivant.
19555  *
19556  * Fork - LGPL
19557  * <script type="text/javascript">
19558  */
19559  
19560
19561 /**
19562  * @class Roo.tree.ColumnTree
19563  * @extends Roo.data.TreePanel
19564  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19565  * @cfg {int} borderWidth  compined right/left border allowance
19566  * @constructor
19567  * @param {String/HTMLElement/Element} el The container element
19568  * @param {Object} config
19569  */
19570 Roo.tree.ColumnTree =  function(el, config)
19571 {
19572    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19573    this.addEvents({
19574         /**
19575         * @event resize
19576         * Fire this event on a container when it resizes
19577         * @param {int} w Width
19578         * @param {int} h Height
19579         */
19580        "resize" : true
19581     });
19582     this.on('resize', this.onResize, this);
19583 };
19584
19585 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19586     //lines:false,
19587     
19588     
19589     borderWidth: Roo.isBorderBox ? 0 : 2, 
19590     headEls : false,
19591     
19592     render : function(){
19593         // add the header.....
19594        
19595         Roo.tree.ColumnTree.superclass.render.apply(this);
19596         
19597         this.el.addClass('x-column-tree');
19598         
19599         this.headers = this.el.createChild(
19600             {cls:'x-tree-headers'},this.innerCt.dom);
19601    
19602         var cols = this.columns, c;
19603         var totalWidth = 0;
19604         this.headEls = [];
19605         var  len = cols.length;
19606         for(var i = 0; i < len; i++){
19607              c = cols[i];
19608              totalWidth += c.width;
19609             this.headEls.push(this.headers.createChild({
19610                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19611                  cn: {
19612                      cls:'x-tree-hd-text',
19613                      html: c.header
19614                  },
19615                  style:'width:'+(c.width-this.borderWidth)+'px;'
19616              }));
19617         }
19618         this.headers.createChild({cls:'x-clear'});
19619         // prevent floats from wrapping when clipped
19620         this.headers.setWidth(totalWidth);
19621         //this.innerCt.setWidth(totalWidth);
19622         this.innerCt.setStyle({ overflow: 'auto' });
19623         this.onResize(this.width, this.height);
19624              
19625         
19626     },
19627     onResize : function(w,h)
19628     {
19629         this.height = h;
19630         this.width = w;
19631         // resize cols..
19632         this.innerCt.setWidth(this.width);
19633         this.innerCt.setHeight(this.height-20);
19634         
19635         // headers...
19636         var cols = this.columns, c;
19637         var totalWidth = 0;
19638         var expEl = false;
19639         var len = cols.length;
19640         for(var i = 0; i < len; i++){
19641             c = cols[i];
19642             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19643                 // it's the expander..
19644                 expEl  = this.headEls[i];
19645                 continue;
19646             }
19647             totalWidth += c.width;
19648             
19649         }
19650         if (expEl) {
19651             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19652         }
19653         this.headers.setWidth(w-20);
19654
19655         
19656         
19657         
19658     }
19659 });
19660 /*
19661  * Based on:
19662  * Ext JS Library 1.1.1
19663  * Copyright(c) 2006-2007, Ext JS, LLC.
19664  *
19665  * Originally Released Under LGPL - original licence link has changed is not relivant.
19666  *
19667  * Fork - LGPL
19668  * <script type="text/javascript">
19669  */
19670  
19671 /**
19672  * @class Roo.menu.Menu
19673  * @extends Roo.util.Observable
19674  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19675  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19676  * @constructor
19677  * Creates a new Menu
19678  * @param {Object} config Configuration options
19679  */
19680 Roo.menu.Menu = function(config){
19681     Roo.apply(this, config);
19682     this.id = this.id || Roo.id();
19683     this.addEvents({
19684         /**
19685          * @event beforeshow
19686          * Fires before this menu is displayed
19687          * @param {Roo.menu.Menu} this
19688          */
19689         beforeshow : true,
19690         /**
19691          * @event beforehide
19692          * Fires before this menu is hidden
19693          * @param {Roo.menu.Menu} this
19694          */
19695         beforehide : true,
19696         /**
19697          * @event show
19698          * Fires after this menu is displayed
19699          * @param {Roo.menu.Menu} this
19700          */
19701         show : true,
19702         /**
19703          * @event hide
19704          * Fires after this menu is hidden
19705          * @param {Roo.menu.Menu} this
19706          */
19707         hide : true,
19708         /**
19709          * @event click
19710          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19711          * @param {Roo.menu.Menu} this
19712          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19713          * @param {Roo.EventObject} e
19714          */
19715         click : true,
19716         /**
19717          * @event mouseover
19718          * Fires when the mouse is hovering over this menu
19719          * @param {Roo.menu.Menu} this
19720          * @param {Roo.EventObject} e
19721          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19722          */
19723         mouseover : true,
19724         /**
19725          * @event mouseout
19726          * Fires when the mouse exits this menu
19727          * @param {Roo.menu.Menu} this
19728          * @param {Roo.EventObject} e
19729          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19730          */
19731         mouseout : true,
19732         /**
19733          * @event itemclick
19734          * Fires when a menu item contained in this menu is clicked
19735          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19736          * @param {Roo.EventObject} e
19737          */
19738         itemclick: true
19739     });
19740     if (this.registerMenu) {
19741         Roo.menu.MenuMgr.register(this);
19742     }
19743     
19744     var mis = this.items;
19745     this.items = new Roo.util.MixedCollection();
19746     if(mis){
19747         this.add.apply(this, mis);
19748     }
19749 };
19750
19751 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19752     /**
19753      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19754      */
19755     minWidth : 120,
19756     /**
19757      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19758      * for bottom-right shadow (defaults to "sides")
19759      */
19760     shadow : "sides",
19761     /**
19762      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19763      * this menu (defaults to "tl-tr?")
19764      */
19765     subMenuAlign : "tl-tr?",
19766     /**
19767      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19768      * relative to its element of origin (defaults to "tl-bl?")
19769      */
19770     defaultAlign : "tl-bl?",
19771     /**
19772      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19773      */
19774     allowOtherMenus : false,
19775     /**
19776      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19777      */
19778     registerMenu : true,
19779
19780     hidden:true,
19781
19782     // private
19783     render : function(){
19784         if(this.el){
19785             return;
19786         }
19787         var el = this.el = new Roo.Layer({
19788             cls: "x-menu",
19789             shadow:this.shadow,
19790             constrain: false,
19791             parentEl: this.parentEl || document.body,
19792             zindex:15000
19793         });
19794
19795         this.keyNav = new Roo.menu.MenuNav(this);
19796
19797         if(this.plain){
19798             el.addClass("x-menu-plain");
19799         }
19800         if(this.cls){
19801             el.addClass(this.cls);
19802         }
19803         // generic focus element
19804         this.focusEl = el.createChild({
19805             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19806         });
19807         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19808         ul.on("click", this.onClick, this);
19809         ul.on("mouseover", this.onMouseOver, this);
19810         ul.on("mouseout", this.onMouseOut, this);
19811         this.items.each(function(item){
19812             var li = document.createElement("li");
19813             li.className = "x-menu-list-item";
19814             ul.dom.appendChild(li);
19815             item.render(li, this);
19816         }, this);
19817         this.ul = ul;
19818         this.autoWidth();
19819     },
19820
19821     // private
19822     autoWidth : function(){
19823         var el = this.el, ul = this.ul;
19824         if(!el){
19825             return;
19826         }
19827         var w = this.width;
19828         if(w){
19829             el.setWidth(w);
19830         }else if(Roo.isIE){
19831             el.setWidth(this.minWidth);
19832             var t = el.dom.offsetWidth; // force recalc
19833             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19834         }
19835     },
19836
19837     // private
19838     delayAutoWidth : function(){
19839         if(this.rendered){
19840             if(!this.awTask){
19841                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19842             }
19843             this.awTask.delay(20);
19844         }
19845     },
19846
19847     // private
19848     findTargetItem : function(e){
19849         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19850         if(t && t.menuItemId){
19851             return this.items.get(t.menuItemId);
19852         }
19853     },
19854
19855     // private
19856     onClick : function(e){
19857         var t;
19858         if(t = this.findTargetItem(e)){
19859             t.onClick(e);
19860             this.fireEvent("click", this, t, e);
19861         }
19862     },
19863
19864     // private
19865     setActiveItem : function(item, autoExpand){
19866         if(item != this.activeItem){
19867             if(this.activeItem){
19868                 this.activeItem.deactivate();
19869             }
19870             this.activeItem = item;
19871             item.activate(autoExpand);
19872         }else if(autoExpand){
19873             item.expandMenu();
19874         }
19875     },
19876
19877     // private
19878     tryActivate : function(start, step){
19879         var items = this.items;
19880         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19881             var item = items.get(i);
19882             if(!item.disabled && item.canActivate){
19883                 this.setActiveItem(item, false);
19884                 return item;
19885             }
19886         }
19887         return false;
19888     },
19889
19890     // private
19891     onMouseOver : function(e){
19892         var t;
19893         if(t = this.findTargetItem(e)){
19894             if(t.canActivate && !t.disabled){
19895                 this.setActiveItem(t, true);
19896             }
19897         }
19898         this.fireEvent("mouseover", this, e, t);
19899     },
19900
19901     // private
19902     onMouseOut : function(e){
19903         var t;
19904         if(t = this.findTargetItem(e)){
19905             if(t == this.activeItem && t.shouldDeactivate(e)){
19906                 this.activeItem.deactivate();
19907                 delete this.activeItem;
19908             }
19909         }
19910         this.fireEvent("mouseout", this, e, t);
19911     },
19912
19913     /**
19914      * Read-only.  Returns true if the menu is currently displayed, else false.
19915      * @type Boolean
19916      */
19917     isVisible : function(){
19918         return this.el && !this.hidden;
19919     },
19920
19921     /**
19922      * Displays this menu relative to another element
19923      * @param {String/HTMLElement/Roo.Element} element The element to align to
19924      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19925      * the element (defaults to this.defaultAlign)
19926      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19927      */
19928     show : function(el, pos, parentMenu){
19929         this.parentMenu = parentMenu;
19930         if(!this.el){
19931             this.render();
19932         }
19933         this.fireEvent("beforeshow", this);
19934         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19935     },
19936
19937     /**
19938      * Displays this menu at a specific xy position
19939      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19940      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19941      */
19942     showAt : function(xy, parentMenu, /* private: */_e){
19943         this.parentMenu = parentMenu;
19944         if(!this.el){
19945             this.render();
19946         }
19947         if(_e !== false){
19948             this.fireEvent("beforeshow", this);
19949             xy = this.el.adjustForConstraints(xy);
19950         }
19951         this.el.setXY(xy);
19952         this.el.show();
19953         this.hidden = false;
19954         this.focus();
19955         this.fireEvent("show", this);
19956     },
19957
19958     focus : function(){
19959         if(!this.hidden){
19960             this.doFocus.defer(50, this);
19961         }
19962     },
19963
19964     doFocus : function(){
19965         if(!this.hidden){
19966             this.focusEl.focus();
19967         }
19968     },
19969
19970     /**
19971      * Hides this menu and optionally all parent menus
19972      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19973      */
19974     hide : function(deep){
19975         if(this.el && this.isVisible()){
19976             this.fireEvent("beforehide", this);
19977             if(this.activeItem){
19978                 this.activeItem.deactivate();
19979                 this.activeItem = null;
19980             }
19981             this.el.hide();
19982             this.hidden = true;
19983             this.fireEvent("hide", this);
19984         }
19985         if(deep === true && this.parentMenu){
19986             this.parentMenu.hide(true);
19987         }
19988     },
19989
19990     /**
19991      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19992      * Any of the following are valid:
19993      * <ul>
19994      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19995      * <li>An HTMLElement object which will be converted to a menu item</li>
19996      * <li>A menu item config object that will be created as a new menu item</li>
19997      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19998      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19999      * </ul>
20000      * Usage:
20001      * <pre><code>
20002 // Create the menu
20003 var menu = new Roo.menu.Menu();
20004
20005 // Create a menu item to add by reference
20006 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20007
20008 // Add a bunch of items at once using different methods.
20009 // Only the last item added will be returned.
20010 var item = menu.add(
20011     menuItem,                // add existing item by ref
20012     'Dynamic Item',          // new TextItem
20013     '-',                     // new separator
20014     { text: 'Config Item' }  // new item by config
20015 );
20016 </code></pre>
20017      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20018      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20019      */
20020     add : function(){
20021         var a = arguments, l = a.length, item;
20022         for(var i = 0; i < l; i++){
20023             var el = a[i];
20024             if ((typeof(el) == "object") && el.xtype && el.xns) {
20025                 el = Roo.factory(el, Roo.menu);
20026             }
20027             
20028             if(el.render){ // some kind of Item
20029                 item = this.addItem(el);
20030             }else if(typeof el == "string"){ // string
20031                 if(el == "separator" || el == "-"){
20032                     item = this.addSeparator();
20033                 }else{
20034                     item = this.addText(el);
20035                 }
20036             }else if(el.tagName || el.el){ // element
20037                 item = this.addElement(el);
20038             }else if(typeof el == "object"){ // must be menu item config?
20039                 item = this.addMenuItem(el);
20040             }
20041         }
20042         return item;
20043     },
20044
20045     /**
20046      * Returns this menu's underlying {@link Roo.Element} object
20047      * @return {Roo.Element} The element
20048      */
20049     getEl : function(){
20050         if(!this.el){
20051             this.render();
20052         }
20053         return this.el;
20054     },
20055
20056     /**
20057      * Adds a separator bar to the menu
20058      * @return {Roo.menu.Item} The menu item that was added
20059      */
20060     addSeparator : function(){
20061         return this.addItem(new Roo.menu.Separator());
20062     },
20063
20064     /**
20065      * Adds an {@link Roo.Element} object to the menu
20066      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20067      * @return {Roo.menu.Item} The menu item that was added
20068      */
20069     addElement : function(el){
20070         return this.addItem(new Roo.menu.BaseItem(el));
20071     },
20072
20073     /**
20074      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20075      * @param {Roo.menu.Item} item The menu item to add
20076      * @return {Roo.menu.Item} The menu item that was added
20077      */
20078     addItem : function(item){
20079         this.items.add(item);
20080         if(this.ul){
20081             var li = document.createElement("li");
20082             li.className = "x-menu-list-item";
20083             this.ul.dom.appendChild(li);
20084             item.render(li, this);
20085             this.delayAutoWidth();
20086         }
20087         return item;
20088     },
20089
20090     /**
20091      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20092      * @param {Object} config A MenuItem config object
20093      * @return {Roo.menu.Item} The menu item that was added
20094      */
20095     addMenuItem : function(config){
20096         if(!(config instanceof Roo.menu.Item)){
20097             if(typeof config.checked == "boolean"){ // must be check menu item config?
20098                 config = new Roo.menu.CheckItem(config);
20099             }else{
20100                 config = new Roo.menu.Item(config);
20101             }
20102         }
20103         return this.addItem(config);
20104     },
20105
20106     /**
20107      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20108      * @param {String} text The text to display in the menu item
20109      * @return {Roo.menu.Item} The menu item that was added
20110      */
20111     addText : function(text){
20112         return this.addItem(new Roo.menu.TextItem({ text : text }));
20113     },
20114
20115     /**
20116      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20117      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20118      * @param {Roo.menu.Item} item The menu item to add
20119      * @return {Roo.menu.Item} The menu item that was added
20120      */
20121     insert : function(index, item){
20122         this.items.insert(index, item);
20123         if(this.ul){
20124             var li = document.createElement("li");
20125             li.className = "x-menu-list-item";
20126             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20127             item.render(li, this);
20128             this.delayAutoWidth();
20129         }
20130         return item;
20131     },
20132
20133     /**
20134      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20135      * @param {Roo.menu.Item} item The menu item to remove
20136      */
20137     remove : function(item){
20138         this.items.removeKey(item.id);
20139         item.destroy();
20140     },
20141
20142     /**
20143      * Removes and destroys all items in the menu
20144      */
20145     removeAll : function(){
20146         var f;
20147         while(f = this.items.first()){
20148             this.remove(f);
20149         }
20150     }
20151 });
20152
20153 // MenuNav is a private utility class used internally by the Menu
20154 Roo.menu.MenuNav = function(menu){
20155     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20156     this.scope = this.menu = menu;
20157 };
20158
20159 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20160     doRelay : function(e, h){
20161         var k = e.getKey();
20162         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20163             this.menu.tryActivate(0, 1);
20164             return false;
20165         }
20166         return h.call(this.scope || this, e, this.menu);
20167     },
20168
20169     up : function(e, m){
20170         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20171             m.tryActivate(m.items.length-1, -1);
20172         }
20173     },
20174
20175     down : function(e, m){
20176         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20177             m.tryActivate(0, 1);
20178         }
20179     },
20180
20181     right : function(e, m){
20182         if(m.activeItem){
20183             m.activeItem.expandMenu(true);
20184         }
20185     },
20186
20187     left : function(e, m){
20188         m.hide();
20189         if(m.parentMenu && m.parentMenu.activeItem){
20190             m.parentMenu.activeItem.activate();
20191         }
20192     },
20193
20194     enter : function(e, m){
20195         if(m.activeItem){
20196             e.stopPropagation();
20197             m.activeItem.onClick(e);
20198             m.fireEvent("click", this, m.activeItem);
20199             return true;
20200         }
20201     }
20202 });/*
20203  * Based on:
20204  * Ext JS Library 1.1.1
20205  * Copyright(c) 2006-2007, Ext JS, LLC.
20206  *
20207  * Originally Released Under LGPL - original licence link has changed is not relivant.
20208  *
20209  * Fork - LGPL
20210  * <script type="text/javascript">
20211  */
20212  
20213 /**
20214  * @class Roo.menu.MenuMgr
20215  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20216  * @singleton
20217  */
20218 Roo.menu.MenuMgr = function(){
20219    var menus, active, groups = {}, attached = false, lastShow = new Date();
20220
20221    // private - called when first menu is created
20222    function init(){
20223        menus = {};
20224        active = new Roo.util.MixedCollection();
20225        Roo.get(document).addKeyListener(27, function(){
20226            if(active.length > 0){
20227                hideAll();
20228            }
20229        });
20230    }
20231
20232    // private
20233    function hideAll(){
20234        if(active && active.length > 0){
20235            var c = active.clone();
20236            c.each(function(m){
20237                m.hide();
20238            });
20239        }
20240    }
20241
20242    // private
20243    function onHide(m){
20244        active.remove(m);
20245        if(active.length < 1){
20246            Roo.get(document).un("mousedown", onMouseDown);
20247            attached = false;
20248        }
20249    }
20250
20251    // private
20252    function onShow(m){
20253        var last = active.last();
20254        lastShow = new Date();
20255        active.add(m);
20256        if(!attached){
20257            Roo.get(document).on("mousedown", onMouseDown);
20258            attached = true;
20259        }
20260        if(m.parentMenu){
20261           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20262           m.parentMenu.activeChild = m;
20263        }else if(last && last.isVisible()){
20264           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20265        }
20266    }
20267
20268    // private
20269    function onBeforeHide(m){
20270        if(m.activeChild){
20271            m.activeChild.hide();
20272        }
20273        if(m.autoHideTimer){
20274            clearTimeout(m.autoHideTimer);
20275            delete m.autoHideTimer;
20276        }
20277    }
20278
20279    // private
20280    function onBeforeShow(m){
20281        var pm = m.parentMenu;
20282        if(!pm && !m.allowOtherMenus){
20283            hideAll();
20284        }else if(pm && pm.activeChild && active != m){
20285            pm.activeChild.hide();
20286        }
20287    }
20288
20289    // private
20290    function onMouseDown(e){
20291        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20292            hideAll();
20293        }
20294    }
20295
20296    // private
20297    function onBeforeCheck(mi, state){
20298        if(state){
20299            var g = groups[mi.group];
20300            for(var i = 0, l = g.length; i < l; i++){
20301                if(g[i] != mi){
20302                    g[i].setChecked(false);
20303                }
20304            }
20305        }
20306    }
20307
20308    return {
20309
20310        /**
20311         * Hides all menus that are currently visible
20312         */
20313        hideAll : function(){
20314             hideAll();  
20315        },
20316
20317        // private
20318        register : function(menu){
20319            if(!menus){
20320                init();
20321            }
20322            menus[menu.id] = menu;
20323            menu.on("beforehide", onBeforeHide);
20324            menu.on("hide", onHide);
20325            menu.on("beforeshow", onBeforeShow);
20326            menu.on("show", onShow);
20327            var g = menu.group;
20328            if(g && menu.events["checkchange"]){
20329                if(!groups[g]){
20330                    groups[g] = [];
20331                }
20332                groups[g].push(menu);
20333                menu.on("checkchange", onCheck);
20334            }
20335        },
20336
20337         /**
20338          * Returns a {@link Roo.menu.Menu} object
20339          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20340          * be used to generate and return a new Menu instance.
20341          */
20342        get : function(menu){
20343            if(typeof menu == "string"){ // menu id
20344                return menus[menu];
20345            }else if(menu.events){  // menu instance
20346                return menu;
20347            }else if(typeof menu.length == 'number'){ // array of menu items?
20348                return new Roo.menu.Menu({items:menu});
20349            }else{ // otherwise, must be a config
20350                return new Roo.menu.Menu(menu);
20351            }
20352        },
20353
20354        // private
20355        unregister : function(menu){
20356            delete menus[menu.id];
20357            menu.un("beforehide", onBeforeHide);
20358            menu.un("hide", onHide);
20359            menu.un("beforeshow", onBeforeShow);
20360            menu.un("show", onShow);
20361            var g = menu.group;
20362            if(g && menu.events["checkchange"]){
20363                groups[g].remove(menu);
20364                menu.un("checkchange", onCheck);
20365            }
20366        },
20367
20368        // private
20369        registerCheckable : function(menuItem){
20370            var g = menuItem.group;
20371            if(g){
20372                if(!groups[g]){
20373                    groups[g] = [];
20374                }
20375                groups[g].push(menuItem);
20376                menuItem.on("beforecheckchange", onBeforeCheck);
20377            }
20378        },
20379
20380        // private
20381        unregisterCheckable : function(menuItem){
20382            var g = menuItem.group;
20383            if(g){
20384                groups[g].remove(menuItem);
20385                menuItem.un("beforecheckchange", onBeforeCheck);
20386            }
20387        }
20388    };
20389 }();/*
20390  * Based on:
20391  * Ext JS Library 1.1.1
20392  * Copyright(c) 2006-2007, Ext JS, LLC.
20393  *
20394  * Originally Released Under LGPL - original licence link has changed is not relivant.
20395  *
20396  * Fork - LGPL
20397  * <script type="text/javascript">
20398  */
20399  
20400
20401 /**
20402  * @class Roo.menu.BaseItem
20403  * @extends Roo.Component
20404  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20405  * management and base configuration options shared by all menu components.
20406  * @constructor
20407  * Creates a new BaseItem
20408  * @param {Object} config Configuration options
20409  */
20410 Roo.menu.BaseItem = function(config){
20411     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20412
20413     this.addEvents({
20414         /**
20415          * @event click
20416          * Fires when this item is clicked
20417          * @param {Roo.menu.BaseItem} this
20418          * @param {Roo.EventObject} e
20419          */
20420         click: true,
20421         /**
20422          * @event activate
20423          * Fires when this item is activated
20424          * @param {Roo.menu.BaseItem} this
20425          */
20426         activate : true,
20427         /**
20428          * @event deactivate
20429          * Fires when this item is deactivated
20430          * @param {Roo.menu.BaseItem} this
20431          */
20432         deactivate : true
20433     });
20434
20435     if(this.handler){
20436         this.on("click", this.handler, this.scope, true);
20437     }
20438 };
20439
20440 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20441     /**
20442      * @cfg {Function} handler
20443      * A function that will handle the click event of this menu item (defaults to undefined)
20444      */
20445     /**
20446      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20447      */
20448     canActivate : false,
20449     /**
20450      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20451      */
20452     activeClass : "x-menu-item-active",
20453     /**
20454      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20455      */
20456     hideOnClick : true,
20457     /**
20458      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20459      */
20460     hideDelay : 100,
20461
20462     // private
20463     ctype: "Roo.menu.BaseItem",
20464
20465     // private
20466     actionMode : "container",
20467
20468     // private
20469     render : function(container, parentMenu){
20470         this.parentMenu = parentMenu;
20471         Roo.menu.BaseItem.superclass.render.call(this, container);
20472         this.container.menuItemId = this.id;
20473     },
20474
20475     // private
20476     onRender : function(container, position){
20477         this.el = Roo.get(this.el);
20478         container.dom.appendChild(this.el.dom);
20479     },
20480
20481     // private
20482     onClick : function(e){
20483         if(!this.disabled && this.fireEvent("click", this, e) !== false
20484                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20485             this.handleClick(e);
20486         }else{
20487             e.stopEvent();
20488         }
20489     },
20490
20491     // private
20492     activate : function(){
20493         if(this.disabled){
20494             return false;
20495         }
20496         var li = this.container;
20497         li.addClass(this.activeClass);
20498         this.region = li.getRegion().adjust(2, 2, -2, -2);
20499         this.fireEvent("activate", this);
20500         return true;
20501     },
20502
20503     // private
20504     deactivate : function(){
20505         this.container.removeClass(this.activeClass);
20506         this.fireEvent("deactivate", this);
20507     },
20508
20509     // private
20510     shouldDeactivate : function(e){
20511         return !this.region || !this.region.contains(e.getPoint());
20512     },
20513
20514     // private
20515     handleClick : function(e){
20516         if(this.hideOnClick){
20517             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20518         }
20519     },
20520
20521     // private
20522     expandMenu : function(autoActivate){
20523         // do nothing
20524     },
20525
20526     // private
20527     hideMenu : function(){
20528         // do nothing
20529     }
20530 });/*
20531  * Based on:
20532  * Ext JS Library 1.1.1
20533  * Copyright(c) 2006-2007, Ext JS, LLC.
20534  *
20535  * Originally Released Under LGPL - original licence link has changed is not relivant.
20536  *
20537  * Fork - LGPL
20538  * <script type="text/javascript">
20539  */
20540  
20541 /**
20542  * @class Roo.menu.Adapter
20543  * @extends Roo.menu.BaseItem
20544  * 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.
20545  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20546  * @constructor
20547  * Creates a new Adapter
20548  * @param {Object} config Configuration options
20549  */
20550 Roo.menu.Adapter = function(component, config){
20551     Roo.menu.Adapter.superclass.constructor.call(this, config);
20552     this.component = component;
20553 };
20554 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20555     // private
20556     canActivate : true,
20557
20558     // private
20559     onRender : function(container, position){
20560         this.component.render(container);
20561         this.el = this.component.getEl();
20562     },
20563
20564     // private
20565     activate : function(){
20566         if(this.disabled){
20567             return false;
20568         }
20569         this.component.focus();
20570         this.fireEvent("activate", this);
20571         return true;
20572     },
20573
20574     // private
20575     deactivate : function(){
20576         this.fireEvent("deactivate", this);
20577     },
20578
20579     // private
20580     disable : function(){
20581         this.component.disable();
20582         Roo.menu.Adapter.superclass.disable.call(this);
20583     },
20584
20585     // private
20586     enable : function(){
20587         this.component.enable();
20588         Roo.menu.Adapter.superclass.enable.call(this);
20589     }
20590 });/*
20591  * Based on:
20592  * Ext JS Library 1.1.1
20593  * Copyright(c) 2006-2007, Ext JS, LLC.
20594  *
20595  * Originally Released Under LGPL - original licence link has changed is not relivant.
20596  *
20597  * Fork - LGPL
20598  * <script type="text/javascript">
20599  */
20600
20601 /**
20602  * @class Roo.menu.TextItem
20603  * @extends Roo.menu.BaseItem
20604  * Adds a static text string to a menu, usually used as either a heading or group separator.
20605  * Note: old style constructor with text is still supported.
20606  * 
20607  * @constructor
20608  * Creates a new TextItem
20609  * @param {Object} cfg Configuration
20610  */
20611 Roo.menu.TextItem = function(cfg){
20612     if (typeof(cfg) == 'string') {
20613         this.text = cfg;
20614     } else {
20615         Roo.apply(this,cfg);
20616     }
20617     
20618     Roo.menu.TextItem.superclass.constructor.call(this);
20619 };
20620
20621 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20622     /**
20623      * @cfg {Boolean} text Text to show on item.
20624      */
20625     text : '',
20626     
20627     /**
20628      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20629      */
20630     hideOnClick : false,
20631     /**
20632      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20633      */
20634     itemCls : "x-menu-text",
20635
20636     // private
20637     onRender : function(){
20638         var s = document.createElement("span");
20639         s.className = this.itemCls;
20640         s.innerHTML = this.text;
20641         this.el = s;
20642         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20643     }
20644 });/*
20645  * Based on:
20646  * Ext JS Library 1.1.1
20647  * Copyright(c) 2006-2007, Ext JS, LLC.
20648  *
20649  * Originally Released Under LGPL - original licence link has changed is not relivant.
20650  *
20651  * Fork - LGPL
20652  * <script type="text/javascript">
20653  */
20654
20655 /**
20656  * @class Roo.menu.Separator
20657  * @extends Roo.menu.BaseItem
20658  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20659  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20660  * @constructor
20661  * @param {Object} config Configuration options
20662  */
20663 Roo.menu.Separator = function(config){
20664     Roo.menu.Separator.superclass.constructor.call(this, config);
20665 };
20666
20667 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20668     /**
20669      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20670      */
20671     itemCls : "x-menu-sep",
20672     /**
20673      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20674      */
20675     hideOnClick : false,
20676
20677     // private
20678     onRender : function(li){
20679         var s = document.createElement("span");
20680         s.className = this.itemCls;
20681         s.innerHTML = "&#160;";
20682         this.el = s;
20683         li.addClass("x-menu-sep-li");
20684         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20685     }
20686 });/*
20687  * Based on:
20688  * Ext JS Library 1.1.1
20689  * Copyright(c) 2006-2007, Ext JS, LLC.
20690  *
20691  * Originally Released Under LGPL - original licence link has changed is not relivant.
20692  *
20693  * Fork - LGPL
20694  * <script type="text/javascript">
20695  */
20696 /**
20697  * @class Roo.menu.Item
20698  * @extends Roo.menu.BaseItem
20699  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20700  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20701  * activation and click handling.
20702  * @constructor
20703  * Creates a new Item
20704  * @param {Object} config Configuration options
20705  */
20706 Roo.menu.Item = function(config){
20707     Roo.menu.Item.superclass.constructor.call(this, config);
20708     if(this.menu){
20709         this.menu = Roo.menu.MenuMgr.get(this.menu);
20710     }
20711 };
20712 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20713     
20714     /**
20715      * @cfg {String} text
20716      * The text to show on the menu item.
20717      */
20718     text: '',
20719      /**
20720      * @cfg {String} HTML to render in menu
20721      * The text to show on the menu item (HTML version).
20722      */
20723     html: '',
20724     /**
20725      * @cfg {String} icon
20726      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20727      */
20728     icon: undefined,
20729     /**
20730      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20731      */
20732     itemCls : "x-menu-item",
20733     /**
20734      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20735      */
20736     canActivate : true,
20737     /**
20738      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20739      */
20740     showDelay: 200,
20741     // doc'd in BaseItem
20742     hideDelay: 200,
20743
20744     // private
20745     ctype: "Roo.menu.Item",
20746     
20747     // private
20748     onRender : function(container, position){
20749         var el = document.createElement("a");
20750         el.hideFocus = true;
20751         el.unselectable = "on";
20752         el.href = this.href || "#";
20753         if(this.hrefTarget){
20754             el.target = this.hrefTarget;
20755         }
20756         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20757         
20758         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20759         
20760         el.innerHTML = String.format(
20761                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20762                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20763         this.el = el;
20764         Roo.menu.Item.superclass.onRender.call(this, container, position);
20765     },
20766
20767     /**
20768      * Sets the text to display in this menu item
20769      * @param {String} text The text to display
20770      * @param {Boolean} isHTML true to indicate text is pure html.
20771      */
20772     setText : function(text, isHTML){
20773         if (isHTML) {
20774             this.html = text;
20775         } else {
20776             this.text = text;
20777             this.html = '';
20778         }
20779         if(this.rendered){
20780             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20781      
20782             this.el.update(String.format(
20783                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20784                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20785             this.parentMenu.autoWidth();
20786         }
20787     },
20788
20789     // private
20790     handleClick : function(e){
20791         if(!this.href){ // if no link defined, stop the event automatically
20792             e.stopEvent();
20793         }
20794         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20795     },
20796
20797     // private
20798     activate : function(autoExpand){
20799         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20800             this.focus();
20801             if(autoExpand){
20802                 this.expandMenu();
20803             }
20804         }
20805         return true;
20806     },
20807
20808     // private
20809     shouldDeactivate : function(e){
20810         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20811             if(this.menu && this.menu.isVisible()){
20812                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20813             }
20814             return true;
20815         }
20816         return false;
20817     },
20818
20819     // private
20820     deactivate : function(){
20821         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20822         this.hideMenu();
20823     },
20824
20825     // private
20826     expandMenu : function(autoActivate){
20827         if(!this.disabled && this.menu){
20828             clearTimeout(this.hideTimer);
20829             delete this.hideTimer;
20830             if(!this.menu.isVisible() && !this.showTimer){
20831                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20832             }else if (this.menu.isVisible() && autoActivate){
20833                 this.menu.tryActivate(0, 1);
20834             }
20835         }
20836     },
20837
20838     // private
20839     deferExpand : function(autoActivate){
20840         delete this.showTimer;
20841         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20842         if(autoActivate){
20843             this.menu.tryActivate(0, 1);
20844         }
20845     },
20846
20847     // private
20848     hideMenu : function(){
20849         clearTimeout(this.showTimer);
20850         delete this.showTimer;
20851         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20852             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20853         }
20854     },
20855
20856     // private
20857     deferHide : function(){
20858         delete this.hideTimer;
20859         this.menu.hide();
20860     }
20861 });/*
20862  * Based on:
20863  * Ext JS Library 1.1.1
20864  * Copyright(c) 2006-2007, Ext JS, LLC.
20865  *
20866  * Originally Released Under LGPL - original licence link has changed is not relivant.
20867  *
20868  * Fork - LGPL
20869  * <script type="text/javascript">
20870  */
20871  
20872 /**
20873  * @class Roo.menu.CheckItem
20874  * @extends Roo.menu.Item
20875  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20876  * @constructor
20877  * Creates a new CheckItem
20878  * @param {Object} config Configuration options
20879  */
20880 Roo.menu.CheckItem = function(config){
20881     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20882     this.addEvents({
20883         /**
20884          * @event beforecheckchange
20885          * Fires before the checked value is set, providing an opportunity to cancel if needed
20886          * @param {Roo.menu.CheckItem} this
20887          * @param {Boolean} checked The new checked value that will be set
20888          */
20889         "beforecheckchange" : true,
20890         /**
20891          * @event checkchange
20892          * Fires after the checked value has been set
20893          * @param {Roo.menu.CheckItem} this
20894          * @param {Boolean} checked The checked value that was set
20895          */
20896         "checkchange" : true
20897     });
20898     if(this.checkHandler){
20899         this.on('checkchange', this.checkHandler, this.scope);
20900     }
20901 };
20902 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20903     /**
20904      * @cfg {String} group
20905      * All check items with the same group name will automatically be grouped into a single-select
20906      * radio button group (defaults to '')
20907      */
20908     /**
20909      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20910      */
20911     itemCls : "x-menu-item x-menu-check-item",
20912     /**
20913      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20914      */
20915     groupClass : "x-menu-group-item",
20916
20917     /**
20918      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20919      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20920      * initialized with checked = true will be rendered as checked.
20921      */
20922     checked: false,
20923
20924     // private
20925     ctype: "Roo.menu.CheckItem",
20926
20927     // private
20928     onRender : function(c){
20929         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20930         if(this.group){
20931             this.el.addClass(this.groupClass);
20932         }
20933         Roo.menu.MenuMgr.registerCheckable(this);
20934         if(this.checked){
20935             this.checked = false;
20936             this.setChecked(true, true);
20937         }
20938     },
20939
20940     // private
20941     destroy : function(){
20942         if(this.rendered){
20943             Roo.menu.MenuMgr.unregisterCheckable(this);
20944         }
20945         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20946     },
20947
20948     /**
20949      * Set the checked state of this item
20950      * @param {Boolean} checked The new checked value
20951      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20952      */
20953     setChecked : function(state, suppressEvent){
20954         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20955             if(this.container){
20956                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20957             }
20958             this.checked = state;
20959             if(suppressEvent !== true){
20960                 this.fireEvent("checkchange", this, state);
20961             }
20962         }
20963     },
20964
20965     // private
20966     handleClick : function(e){
20967        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20968            this.setChecked(!this.checked);
20969        }
20970        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20971     }
20972 });/*
20973  * Based on:
20974  * Ext JS Library 1.1.1
20975  * Copyright(c) 2006-2007, Ext JS, LLC.
20976  *
20977  * Originally Released Under LGPL - original licence link has changed is not relivant.
20978  *
20979  * Fork - LGPL
20980  * <script type="text/javascript">
20981  */
20982  
20983 /**
20984  * @class Roo.menu.DateItem
20985  * @extends Roo.menu.Adapter
20986  * A menu item that wraps the {@link Roo.DatPicker} component.
20987  * @constructor
20988  * Creates a new DateItem
20989  * @param {Object} config Configuration options
20990  */
20991 Roo.menu.DateItem = function(config){
20992     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20993     /** The Roo.DatePicker object @type Roo.DatePicker */
20994     this.picker = this.component;
20995     this.addEvents({select: true});
20996     
20997     this.picker.on("render", function(picker){
20998         picker.getEl().swallowEvent("click");
20999         picker.container.addClass("x-menu-date-item");
21000     });
21001
21002     this.picker.on("select", this.onSelect, this);
21003 };
21004
21005 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21006     // private
21007     onSelect : function(picker, date){
21008         this.fireEvent("select", this, date, picker);
21009         Roo.menu.DateItem.superclass.handleClick.call(this);
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.ColorItem
21024  * @extends Roo.menu.Adapter
21025  * A menu item that wraps the {@link Roo.ColorPalette} component.
21026  * @constructor
21027  * Creates a new ColorItem
21028  * @param {Object} config Configuration options
21029  */
21030 Roo.menu.ColorItem = function(config){
21031     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21032     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21033     this.palette = this.component;
21034     this.relayEvents(this.palette, ["select"]);
21035     if(this.selectHandler){
21036         this.on('select', this.selectHandler, this.scope);
21037     }
21038 };
21039 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21040  * Based on:
21041  * Ext JS Library 1.1.1
21042  * Copyright(c) 2006-2007, Ext JS, LLC.
21043  *
21044  * Originally Released Under LGPL - original licence link has changed is not relivant.
21045  *
21046  * Fork - LGPL
21047  * <script type="text/javascript">
21048  */
21049  
21050
21051 /**
21052  * @class Roo.menu.DateMenu
21053  * @extends Roo.menu.Menu
21054  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21055  * @constructor
21056  * Creates a new DateMenu
21057  * @param {Object} config Configuration options
21058  */
21059 Roo.menu.DateMenu = function(config){
21060     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21061     this.plain = true;
21062     var di = new Roo.menu.DateItem(config);
21063     this.add(di);
21064     /**
21065      * The {@link Roo.DatePicker} instance for this DateMenu
21066      * @type DatePicker
21067      */
21068     this.picker = di.picker;
21069     /**
21070      * @event select
21071      * @param {DatePicker} picker
21072      * @param {Date} date
21073      */
21074     this.relayEvents(di, ["select"]);
21075     this.on('beforeshow', function(){
21076         if(this.picker){
21077             this.picker.hideMonthPicker(false);
21078         }
21079     }, this);
21080 };
21081 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21082     cls:'x-date-menu'
21083 });/*
21084  * Based on:
21085  * Ext JS Library 1.1.1
21086  * Copyright(c) 2006-2007, Ext JS, LLC.
21087  *
21088  * Originally Released Under LGPL - original licence link has changed is not relivant.
21089  *
21090  * Fork - LGPL
21091  * <script type="text/javascript">
21092  */
21093  
21094
21095 /**
21096  * @class Roo.menu.ColorMenu
21097  * @extends Roo.menu.Menu
21098  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21099  * @constructor
21100  * Creates a new ColorMenu
21101  * @param {Object} config Configuration options
21102  */
21103 Roo.menu.ColorMenu = function(config){
21104     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21105     this.plain = true;
21106     var ci = new Roo.menu.ColorItem(config);
21107     this.add(ci);
21108     /**
21109      * The {@link Roo.ColorPalette} instance for this ColorMenu
21110      * @type ColorPalette
21111      */
21112     this.palette = ci.palette;
21113     /**
21114      * @event select
21115      * @param {ColorPalette} palette
21116      * @param {String} color
21117      */
21118     this.relayEvents(ci, ["select"]);
21119 };
21120 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21121  * Based on:
21122  * Ext JS Library 1.1.1
21123  * Copyright(c) 2006-2007, Ext JS, LLC.
21124  *
21125  * Originally Released Under LGPL - original licence link has changed is not relivant.
21126  *
21127  * Fork - LGPL
21128  * <script type="text/javascript">
21129  */
21130  
21131 /**
21132  * @class Roo.form.Field
21133  * @extends Roo.BoxComponent
21134  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21135  * @constructor
21136  * Creates a new Field
21137  * @param {Object} config Configuration options
21138  */
21139 Roo.form.Field = function(config){
21140     Roo.form.Field.superclass.constructor.call(this, config);
21141 };
21142
21143 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21144     /**
21145      * @cfg {String} fieldLabel Label to use when rendering a form.
21146      */
21147        /**
21148      * @cfg {String} qtip Mouse over tip
21149      */
21150      
21151     /**
21152      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21153      */
21154     invalidClass : "x-form-invalid",
21155     /**
21156      * @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")
21157      */
21158     invalidText : "The value in this field is invalid",
21159     /**
21160      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21161      */
21162     focusClass : "x-form-focus",
21163     /**
21164      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21165       automatic validation (defaults to "keyup").
21166      */
21167     validationEvent : "keyup",
21168     /**
21169      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21170      */
21171     validateOnBlur : true,
21172     /**
21173      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21174      */
21175     validationDelay : 250,
21176     /**
21177      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21178      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21179      */
21180     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21181     /**
21182      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21183      */
21184     fieldClass : "x-form-field",
21185     /**
21186      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21187      *<pre>
21188 Value         Description
21189 -----------   ----------------------------------------------------------------------
21190 qtip          Display a quick tip when the user hovers over the field
21191 title         Display a default browser title attribute popup
21192 under         Add a block div beneath the field containing the error text
21193 side          Add an error icon to the right of the field with a popup on hover
21194 [element id]  Add the error text directly to the innerHTML of the specified element
21195 </pre>
21196      */
21197     msgTarget : 'qtip',
21198     /**
21199      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21200      */
21201     msgFx : 'normal',
21202
21203     /**
21204      * @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.
21205      */
21206     readOnly : false,
21207
21208     /**
21209      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21210      */
21211     disabled : false,
21212
21213     /**
21214      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21215      */
21216     inputType : undefined,
21217     
21218     /**
21219      * @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).
21220          */
21221         tabIndex : undefined,
21222         
21223     // private
21224     isFormField : true,
21225
21226     // private
21227     hasFocus : false,
21228     /**
21229      * @property {Roo.Element} fieldEl
21230      * Element Containing the rendered Field (with label etc.)
21231      */
21232     /**
21233      * @cfg {Mixed} value A value to initialize this field with.
21234      */
21235     value : undefined,
21236
21237     /**
21238      * @cfg {String} name The field's HTML name attribute.
21239      */
21240     /**
21241      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21242      */
21243
21244         // private ??
21245         initComponent : function(){
21246         Roo.form.Field.superclass.initComponent.call(this);
21247         this.addEvents({
21248             /**
21249              * @event focus
21250              * Fires when this field receives input focus.
21251              * @param {Roo.form.Field} this
21252              */
21253             focus : true,
21254             /**
21255              * @event blur
21256              * Fires when this field loses input focus.
21257              * @param {Roo.form.Field} this
21258              */
21259             blur : true,
21260             /**
21261              * @event specialkey
21262              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21263              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21264              * @param {Roo.form.Field} this
21265              * @param {Roo.EventObject} e The event object
21266              */
21267             specialkey : true,
21268             /**
21269              * @event change
21270              * Fires just before the field blurs if the field value has changed.
21271              * @param {Roo.form.Field} this
21272              * @param {Mixed} newValue The new value
21273              * @param {Mixed} oldValue The original value
21274              */
21275             change : true,
21276             /**
21277              * @event invalid
21278              * Fires after the field has been marked as invalid.
21279              * @param {Roo.form.Field} this
21280              * @param {String} msg The validation message
21281              */
21282             invalid : true,
21283             /**
21284              * @event valid
21285              * Fires after the field has been validated with no errors.
21286              * @param {Roo.form.Field} this
21287              */
21288             valid : true,
21289              /**
21290              * @event keyup
21291              * Fires after the key up
21292              * @param {Roo.form.Field} this
21293              * @param {Roo.EventObject}  e The event Object
21294              */
21295             keyup : true
21296         });
21297     },
21298
21299     /**
21300      * Returns the name attribute of the field if available
21301      * @return {String} name The field name
21302      */
21303     getName: function(){
21304          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21305     },
21306
21307     // private
21308     onRender : function(ct, position){
21309         Roo.form.Field.superclass.onRender.call(this, ct, position);
21310         if(!this.el){
21311             var cfg = this.getAutoCreate();
21312             if(!cfg.name){
21313                 cfg.name = this.name || this.id;
21314             }
21315             if(this.inputType){
21316                 cfg.type = this.inputType;
21317             }
21318             this.el = ct.createChild(cfg, position);
21319         }
21320         var type = this.el.dom.type;
21321         if(type){
21322             if(type == 'password'){
21323                 type = 'text';
21324             }
21325             this.el.addClass('x-form-'+type);
21326         }
21327         if(this.readOnly){
21328             this.el.dom.readOnly = true;
21329         }
21330         if(this.tabIndex !== undefined){
21331             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21332         }
21333
21334         this.el.addClass([this.fieldClass, this.cls]);
21335         this.initValue();
21336     },
21337
21338     /**
21339      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21340      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21341      * @return {Roo.form.Field} this
21342      */
21343     applyTo : function(target){
21344         this.allowDomMove = false;
21345         this.el = Roo.get(target);
21346         this.render(this.el.dom.parentNode);
21347         return this;
21348     },
21349
21350     // private
21351     initValue : function(){
21352         if(this.value !== undefined){
21353             this.setValue(this.value);
21354         }else if(this.el.dom.value.length > 0){
21355             this.setValue(this.el.dom.value);
21356         }
21357     },
21358
21359     /**
21360      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21361      */
21362     isDirty : function() {
21363         if(this.disabled) {
21364             return false;
21365         }
21366         return String(this.getValue()) !== String(this.originalValue);
21367     },
21368
21369     // private
21370     afterRender : function(){
21371         Roo.form.Field.superclass.afterRender.call(this);
21372         this.initEvents();
21373     },
21374
21375     // private
21376     fireKey : function(e){
21377         //Roo.log('field ' + e.getKey());
21378         if(e.isNavKeyPress()){
21379             this.fireEvent("specialkey", this, e);
21380         }
21381     },
21382
21383     /**
21384      * Resets the current field value to the originally loaded value and clears any validation messages
21385      */
21386     reset : function(){
21387         this.setValue(this.originalValue);
21388         this.clearInvalid();
21389     },
21390
21391     // private
21392     initEvents : function(){
21393         // safari killled keypress - so keydown is now used..
21394         this.el.on("keydown" , this.fireKey,  this);
21395         this.el.on("focus", this.onFocus,  this);
21396         this.el.on("blur", this.onBlur,  this);
21397         this.el.relayEvent('keyup', this);
21398
21399         // reference to original value for reset
21400         this.originalValue = this.getValue();
21401     },
21402
21403     // private
21404     onFocus : function(){
21405         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21406             this.el.addClass(this.focusClass);
21407         }
21408         if(!this.hasFocus){
21409             this.hasFocus = true;
21410             this.startValue = this.getValue();
21411             this.fireEvent("focus", this);
21412         }
21413     },
21414
21415     beforeBlur : Roo.emptyFn,
21416
21417     // private
21418     onBlur : function(){
21419         this.beforeBlur();
21420         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21421             this.el.removeClass(this.focusClass);
21422         }
21423         this.hasFocus = false;
21424         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21425             this.validate();
21426         }
21427         var v = this.getValue();
21428         if(String(v) !== String(this.startValue)){
21429             this.fireEvent('change', this, v, this.startValue);
21430         }
21431         this.fireEvent("blur", this);
21432     },
21433
21434     /**
21435      * Returns whether or not the field value is currently valid
21436      * @param {Boolean} preventMark True to disable marking the field invalid
21437      * @return {Boolean} True if the value is valid, else false
21438      */
21439     isValid : function(preventMark){
21440         if(this.disabled){
21441             return true;
21442         }
21443         var restore = this.preventMark;
21444         this.preventMark = preventMark === true;
21445         var v = this.validateValue(this.processValue(this.getRawValue()));
21446         this.preventMark = restore;
21447         return v;
21448     },
21449
21450     /**
21451      * Validates the field value
21452      * @return {Boolean} True if the value is valid, else false
21453      */
21454     validate : function(){
21455         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21456             this.clearInvalid();
21457             return true;
21458         }
21459         return false;
21460     },
21461
21462     processValue : function(value){
21463         return value;
21464     },
21465
21466     // private
21467     // Subclasses should provide the validation implementation by overriding this
21468     validateValue : function(value){
21469         return true;
21470     },
21471
21472     /**
21473      * Mark this field as invalid
21474      * @param {String} msg The validation message
21475      */
21476     markInvalid : function(msg){
21477         if(!this.rendered || this.preventMark){ // not rendered
21478             return;
21479         }
21480         this.el.addClass(this.invalidClass);
21481         msg = msg || this.invalidText;
21482         switch(this.msgTarget){
21483             case 'qtip':
21484                 this.el.dom.qtip = msg;
21485                 this.el.dom.qclass = 'x-form-invalid-tip';
21486                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21487                     Roo.QuickTips.enable();
21488                 }
21489                 break;
21490             case 'title':
21491                 this.el.dom.title = msg;
21492                 break;
21493             case 'under':
21494                 if(!this.errorEl){
21495                     var elp = this.el.findParent('.x-form-element', 5, true);
21496                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21497                     this.errorEl.setWidth(elp.getWidth(true)-20);
21498                 }
21499                 this.errorEl.update(msg);
21500                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21501                 break;
21502             case 'side':
21503                 if(!this.errorIcon){
21504                     var elp = this.el.findParent('.x-form-element', 5, true);
21505                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21506                 }
21507                 this.alignErrorIcon();
21508                 this.errorIcon.dom.qtip = msg;
21509                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21510                 this.errorIcon.show();
21511                 this.on('resize', this.alignErrorIcon, this);
21512                 break;
21513             default:
21514                 var t = Roo.getDom(this.msgTarget);
21515                 t.innerHTML = msg;
21516                 t.style.display = this.msgDisplay;
21517                 break;
21518         }
21519         this.fireEvent('invalid', this, msg);
21520     },
21521
21522     // private
21523     alignErrorIcon : function(){
21524         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21525     },
21526
21527     /**
21528      * Clear any invalid styles/messages for this field
21529      */
21530     clearInvalid : function(){
21531         if(!this.rendered || this.preventMark){ // not rendered
21532             return;
21533         }
21534         this.el.removeClass(this.invalidClass);
21535         switch(this.msgTarget){
21536             case 'qtip':
21537                 this.el.dom.qtip = '';
21538                 break;
21539             case 'title':
21540                 this.el.dom.title = '';
21541                 break;
21542             case 'under':
21543                 if(this.errorEl){
21544                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21545                 }
21546                 break;
21547             case 'side':
21548                 if(this.errorIcon){
21549                     this.errorIcon.dom.qtip = '';
21550                     this.errorIcon.hide();
21551                     this.un('resize', this.alignErrorIcon, this);
21552                 }
21553                 break;
21554             default:
21555                 var t = Roo.getDom(this.msgTarget);
21556                 t.innerHTML = '';
21557                 t.style.display = 'none';
21558                 break;
21559         }
21560         this.fireEvent('valid', this);
21561     },
21562
21563     /**
21564      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21565      * @return {Mixed} value The field value
21566      */
21567     getRawValue : function(){
21568         var v = this.el.getValue();
21569         if(v === this.emptyText){
21570             v = '';
21571         }
21572         return v;
21573     },
21574
21575     /**
21576      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21577      * @return {Mixed} value The field value
21578      */
21579     getValue : function(){
21580         var v = this.el.getValue();
21581         if(v === this.emptyText || v === undefined){
21582             v = '';
21583         }
21584         return v;
21585     },
21586
21587     /**
21588      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21589      * @param {Mixed} value The value to set
21590      */
21591     setRawValue : function(v){
21592         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21593     },
21594
21595     /**
21596      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21597      * @param {Mixed} value The value to set
21598      */
21599     setValue : function(v){
21600         this.value = v;
21601         if(this.rendered){
21602             this.el.dom.value = (v === null || v === undefined ? '' : v);
21603              this.validate();
21604         }
21605     },
21606
21607     adjustSize : function(w, h){
21608         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21609         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21610         return s;
21611     },
21612
21613     adjustWidth : function(tag, w){
21614         tag = tag.toLowerCase();
21615         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21616             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21617                 if(tag == 'input'){
21618                     return w + 2;
21619                 }
21620                 if(tag = 'textarea'){
21621                     return w-2;
21622                 }
21623             }else if(Roo.isOpera){
21624                 if(tag == 'input'){
21625                     return w + 2;
21626                 }
21627                 if(tag = 'textarea'){
21628                     return w-2;
21629                 }
21630             }
21631         }
21632         return w;
21633     }
21634 });
21635
21636
21637 // anything other than normal should be considered experimental
21638 Roo.form.Field.msgFx = {
21639     normal : {
21640         show: function(msgEl, f){
21641             msgEl.setDisplayed('block');
21642         },
21643
21644         hide : function(msgEl, f){
21645             msgEl.setDisplayed(false).update('');
21646         }
21647     },
21648
21649     slide : {
21650         show: function(msgEl, f){
21651             msgEl.slideIn('t', {stopFx:true});
21652         },
21653
21654         hide : function(msgEl, f){
21655             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21656         }
21657     },
21658
21659     slideRight : {
21660         show: function(msgEl, f){
21661             msgEl.fixDisplay();
21662             msgEl.alignTo(f.el, 'tl-tr');
21663             msgEl.slideIn('l', {stopFx:true});
21664         },
21665
21666         hide : function(msgEl, f){
21667             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21668         }
21669     }
21670 };/*
21671  * Based on:
21672  * Ext JS Library 1.1.1
21673  * Copyright(c) 2006-2007, Ext JS, LLC.
21674  *
21675  * Originally Released Under LGPL - original licence link has changed is not relivant.
21676  *
21677  * Fork - LGPL
21678  * <script type="text/javascript">
21679  */
21680  
21681
21682 /**
21683  * @class Roo.form.TextField
21684  * @extends Roo.form.Field
21685  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21686  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21687  * @constructor
21688  * Creates a new TextField
21689  * @param {Object} config Configuration options
21690  */
21691 Roo.form.TextField = function(config){
21692     Roo.form.TextField.superclass.constructor.call(this, config);
21693     this.addEvents({
21694         /**
21695          * @event autosize
21696          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21697          * according to the default logic, but this event provides a hook for the developer to apply additional
21698          * logic at runtime to resize the field if needed.
21699              * @param {Roo.form.Field} this This text field
21700              * @param {Number} width The new field width
21701              */
21702         autosize : true
21703     });
21704 };
21705
21706 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21707     /**
21708      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21709      */
21710     grow : false,
21711     /**
21712      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21713      */
21714     growMin : 30,
21715     /**
21716      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21717      */
21718     growMax : 800,
21719     /**
21720      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21721      */
21722     vtype : null,
21723     /**
21724      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21725      */
21726     maskRe : null,
21727     /**
21728      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21729      */
21730     disableKeyFilter : false,
21731     /**
21732      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21733      */
21734     allowBlank : true,
21735     /**
21736      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21737      */
21738     minLength : 0,
21739     /**
21740      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21741      */
21742     maxLength : Number.MAX_VALUE,
21743     /**
21744      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21745      */
21746     minLengthText : "The minimum length for this field is {0}",
21747     /**
21748      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21749      */
21750     maxLengthText : "The maximum length for this field is {0}",
21751     /**
21752      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21753      */
21754     selectOnFocus : false,
21755     /**
21756      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21757      */
21758     blankText : "This field is required",
21759     /**
21760      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21761      * If available, this function will be called only after the basic validators all return true, and will be passed the
21762      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21763      */
21764     validator : null,
21765     /**
21766      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21767      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21768      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21769      */
21770     regex : null,
21771     /**
21772      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21773      */
21774     regexText : "",
21775     /**
21776      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21777      */
21778     emptyText : null,
21779     /**
21780      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21781      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21782      */
21783     emptyClass : 'x-form-empty-field',
21784
21785     // private
21786     initEvents : function(){
21787         Roo.form.TextField.superclass.initEvents.call(this);
21788         if(this.validationEvent == 'keyup'){
21789             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21790             this.el.on('keyup', this.filterValidation, this);
21791         }
21792         else if(this.validationEvent !== false){
21793             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21794         }
21795         if(this.selectOnFocus || this.emptyText){
21796             this.on("focus", this.preFocus, this);
21797             if(this.emptyText){
21798                 this.on('blur', this.postBlur, this);
21799                 this.applyEmptyText();
21800             }
21801         }
21802         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21803             this.el.on("keypress", this.filterKeys, this);
21804         }
21805         if(this.grow){
21806             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21807             this.el.on("click", this.autoSize,  this);
21808         }
21809     },
21810
21811     processValue : function(value){
21812         if(this.stripCharsRe){
21813             var newValue = value.replace(this.stripCharsRe, '');
21814             if(newValue !== value){
21815                 this.setRawValue(newValue);
21816                 return newValue;
21817             }
21818         }
21819         return value;
21820     },
21821
21822     filterValidation : function(e){
21823         if(!e.isNavKeyPress()){
21824             this.validationTask.delay(this.validationDelay);
21825         }
21826     },
21827
21828     // private
21829     onKeyUp : function(e){
21830         if(!e.isNavKeyPress()){
21831             this.autoSize();
21832         }
21833     },
21834
21835     /**
21836      * Resets the current field value to the originally-loaded value and clears any validation messages.
21837      * Also adds emptyText and emptyClass if the original value was blank.
21838      */
21839     reset : function(){
21840         Roo.form.TextField.superclass.reset.call(this);
21841         this.applyEmptyText();
21842     },
21843
21844     applyEmptyText : function(){
21845         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21846             this.setRawValue(this.emptyText);
21847             this.el.addClass(this.emptyClass);
21848         }
21849     },
21850
21851     // private
21852     preFocus : function(){
21853         if(this.emptyText){
21854             if(this.el.dom.value == this.emptyText){
21855                 this.setRawValue('');
21856             }
21857             this.el.removeClass(this.emptyClass);
21858         }
21859         if(this.selectOnFocus){
21860             this.el.dom.select();
21861         }
21862     },
21863
21864     // private
21865     postBlur : function(){
21866         this.applyEmptyText();
21867     },
21868
21869     // private
21870     filterKeys : function(e){
21871         var k = e.getKey();
21872         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21873             return;
21874         }
21875         var c = e.getCharCode(), cc = String.fromCharCode(c);
21876         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21877             return;
21878         }
21879         if(!this.maskRe.test(cc)){
21880             e.stopEvent();
21881         }
21882     },
21883
21884     setValue : function(v){
21885         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21886             this.el.removeClass(this.emptyClass);
21887         }
21888         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21889         this.applyEmptyText();
21890         this.autoSize();
21891     },
21892
21893     /**
21894      * Validates a value according to the field's validation rules and marks the field as invalid
21895      * if the validation fails
21896      * @param {Mixed} value The value to validate
21897      * @return {Boolean} True if the value is valid, else false
21898      */
21899     validateValue : function(value){
21900         if(value.length < 1 || value === this.emptyText){ // if it's blank
21901              if(this.allowBlank){
21902                 this.clearInvalid();
21903                 return true;
21904              }else{
21905                 this.markInvalid(this.blankText);
21906                 return false;
21907              }
21908         }
21909         if(value.length < this.minLength){
21910             this.markInvalid(String.format(this.minLengthText, this.minLength));
21911             return false;
21912         }
21913         if(value.length > this.maxLength){
21914             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21915             return false;
21916         }
21917         if(this.vtype){
21918             var vt = Roo.form.VTypes;
21919             if(!vt[this.vtype](value, this)){
21920                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21921                 return false;
21922             }
21923         }
21924         if(typeof this.validator == "function"){
21925             var msg = this.validator(value);
21926             if(msg !== true){
21927                 this.markInvalid(msg);
21928                 return false;
21929             }
21930         }
21931         if(this.regex && !this.regex.test(value)){
21932             this.markInvalid(this.regexText);
21933             return false;
21934         }
21935         return true;
21936     },
21937
21938     /**
21939      * Selects text in this field
21940      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21941      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21942      */
21943     selectText : function(start, end){
21944         var v = this.getRawValue();
21945         if(v.length > 0){
21946             start = start === undefined ? 0 : start;
21947             end = end === undefined ? v.length : end;
21948             var d = this.el.dom;
21949             if(d.setSelectionRange){
21950                 d.setSelectionRange(start, end);
21951             }else if(d.createTextRange){
21952                 var range = d.createTextRange();
21953                 range.moveStart("character", start);
21954                 range.moveEnd("character", v.length-end);
21955                 range.select();
21956             }
21957         }
21958     },
21959
21960     /**
21961      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21962      * This only takes effect if grow = true, and fires the autosize event.
21963      */
21964     autoSize : function(){
21965         if(!this.grow || !this.rendered){
21966             return;
21967         }
21968         if(!this.metrics){
21969             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21970         }
21971         var el = this.el;
21972         var v = el.dom.value;
21973         var d = document.createElement('div');
21974         d.appendChild(document.createTextNode(v));
21975         v = d.innerHTML;
21976         d = null;
21977         v += "&#160;";
21978         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21979         this.el.setWidth(w);
21980         this.fireEvent("autosize", this, w);
21981     }
21982 });/*
21983  * Based on:
21984  * Ext JS Library 1.1.1
21985  * Copyright(c) 2006-2007, Ext JS, LLC.
21986  *
21987  * Originally Released Under LGPL - original licence link has changed is not relivant.
21988  *
21989  * Fork - LGPL
21990  * <script type="text/javascript">
21991  */
21992  
21993 /**
21994  * @class Roo.form.Hidden
21995  * @extends Roo.form.TextField
21996  * Simple Hidden element used on forms 
21997  * 
21998  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21999  * 
22000  * @constructor
22001  * Creates a new Hidden form element.
22002  * @param {Object} config Configuration options
22003  */
22004
22005
22006
22007 // easy hidden field...
22008 Roo.form.Hidden = function(config){
22009     Roo.form.Hidden.superclass.constructor.call(this, config);
22010 };
22011   
22012 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22013     fieldLabel:      '',
22014     inputType:      'hidden',
22015     width:          50,
22016     allowBlank:     true,
22017     labelSeparator: '',
22018     hidden:         true,
22019     itemCls :       'x-form-item-display-none'
22020
22021
22022 });
22023
22024
22025 /*
22026  * Based on:
22027  * Ext JS Library 1.1.1
22028  * Copyright(c) 2006-2007, Ext JS, LLC.
22029  *
22030  * Originally Released Under LGPL - original licence link has changed is not relivant.
22031  *
22032  * Fork - LGPL
22033  * <script type="text/javascript">
22034  */
22035  
22036 /**
22037  * @class Roo.form.TriggerField
22038  * @extends Roo.form.TextField
22039  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22040  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22041  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22042  * for which you can provide a custom implementation.  For example:
22043  * <pre><code>
22044 var trigger = new Roo.form.TriggerField();
22045 trigger.onTriggerClick = myTriggerFn;
22046 trigger.applyTo('my-field');
22047 </code></pre>
22048  *
22049  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22050  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22051  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22052  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22053  * @constructor
22054  * Create a new TriggerField.
22055  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22056  * to the base TextField)
22057  */
22058 Roo.form.TriggerField = function(config){
22059     this.mimicing = false;
22060     Roo.form.TriggerField.superclass.constructor.call(this, config);
22061 };
22062
22063 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22064     /**
22065      * @cfg {String} triggerClass A CSS class to apply to the trigger
22066      */
22067     /**
22068      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22069      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22070      */
22071     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22072     /**
22073      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22074      */
22075     hideTrigger:false,
22076
22077     /** @cfg {Boolean} grow @hide */
22078     /** @cfg {Number} growMin @hide */
22079     /** @cfg {Number} growMax @hide */
22080
22081     /**
22082      * @hide 
22083      * @method
22084      */
22085     autoSize: Roo.emptyFn,
22086     // private
22087     monitorTab : true,
22088     // private
22089     deferHeight : true,
22090
22091     
22092     actionMode : 'wrap',
22093     // private
22094     onResize : function(w, h){
22095         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22096         if(typeof w == 'number'){
22097             var x = w - this.trigger.getWidth();
22098             this.el.setWidth(this.adjustWidth('input', x));
22099             this.trigger.setStyle('left', x+'px');
22100         }
22101     },
22102
22103     // private
22104     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22105
22106     // private
22107     getResizeEl : function(){
22108         return this.wrap;
22109     },
22110
22111     // private
22112     getPositionEl : function(){
22113         return this.wrap;
22114     },
22115
22116     // private
22117     alignErrorIcon : function(){
22118         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22119     },
22120
22121     // private
22122     onRender : function(ct, position){
22123         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22124         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22125         this.trigger = this.wrap.createChild(this.triggerConfig ||
22126                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22127         if(this.hideTrigger){
22128             this.trigger.setDisplayed(false);
22129         }
22130         this.initTrigger();
22131         if(!this.width){
22132             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22133         }
22134     },
22135
22136     // private
22137     initTrigger : function(){
22138         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22139         this.trigger.addClassOnOver('x-form-trigger-over');
22140         this.trigger.addClassOnClick('x-form-trigger-click');
22141     },
22142
22143     // private
22144     onDestroy : function(){
22145         if(this.trigger){
22146             this.trigger.removeAllListeners();
22147             this.trigger.remove();
22148         }
22149         if(this.wrap){
22150             this.wrap.remove();
22151         }
22152         Roo.form.TriggerField.superclass.onDestroy.call(this);
22153     },
22154
22155     // private
22156     onFocus : function(){
22157         Roo.form.TriggerField.superclass.onFocus.call(this);
22158         if(!this.mimicing){
22159             this.wrap.addClass('x-trigger-wrap-focus');
22160             this.mimicing = true;
22161             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22162             if(this.monitorTab){
22163                 this.el.on("keydown", this.checkTab, this);
22164             }
22165         }
22166     },
22167
22168     // private
22169     checkTab : function(e){
22170         if(e.getKey() == e.TAB){
22171             this.triggerBlur();
22172         }
22173     },
22174
22175     // private
22176     onBlur : function(){
22177         // do nothing
22178     },
22179
22180     // private
22181     mimicBlur : function(e, t){
22182         if(!this.wrap.contains(t) && this.validateBlur()){
22183             this.triggerBlur();
22184         }
22185     },
22186
22187     // private
22188     triggerBlur : function(){
22189         this.mimicing = false;
22190         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22191         if(this.monitorTab){
22192             this.el.un("keydown", this.checkTab, this);
22193         }
22194         this.wrap.removeClass('x-trigger-wrap-focus');
22195         Roo.form.TriggerField.superclass.onBlur.call(this);
22196     },
22197
22198     // private
22199     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22200     validateBlur : function(e, t){
22201         return true;
22202     },
22203
22204     // private
22205     onDisable : function(){
22206         Roo.form.TriggerField.superclass.onDisable.call(this);
22207         if(this.wrap){
22208             this.wrap.addClass('x-item-disabled');
22209         }
22210     },
22211
22212     // private
22213     onEnable : function(){
22214         Roo.form.TriggerField.superclass.onEnable.call(this);
22215         if(this.wrap){
22216             this.wrap.removeClass('x-item-disabled');
22217         }
22218     },
22219
22220     // private
22221     onShow : function(){
22222         var ae = this.getActionEl();
22223         
22224         if(ae){
22225             ae.dom.style.display = '';
22226             ae.dom.style.visibility = 'visible';
22227         }
22228     },
22229
22230     // private
22231     
22232     onHide : function(){
22233         var ae = this.getActionEl();
22234         ae.dom.style.display = 'none';
22235     },
22236
22237     /**
22238      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22239      * by an implementing function.
22240      * @method
22241      * @param {EventObject} e
22242      */
22243     onTriggerClick : Roo.emptyFn
22244 });
22245
22246 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22247 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22248 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22249 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22250     initComponent : function(){
22251         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22252
22253         this.triggerConfig = {
22254             tag:'span', cls:'x-form-twin-triggers', cn:[
22255             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22256             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22257         ]};
22258     },
22259
22260     getTrigger : function(index){
22261         return this.triggers[index];
22262     },
22263
22264     initTrigger : function(){
22265         var ts = this.trigger.select('.x-form-trigger', true);
22266         this.wrap.setStyle('overflow', 'hidden');
22267         var triggerField = this;
22268         ts.each(function(t, all, index){
22269             t.hide = function(){
22270                 var w = triggerField.wrap.getWidth();
22271                 this.dom.style.display = 'none';
22272                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22273             };
22274             t.show = function(){
22275                 var w = triggerField.wrap.getWidth();
22276                 this.dom.style.display = '';
22277                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22278             };
22279             var triggerIndex = 'Trigger'+(index+1);
22280
22281             if(this['hide'+triggerIndex]){
22282                 t.dom.style.display = 'none';
22283             }
22284             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22285             t.addClassOnOver('x-form-trigger-over');
22286             t.addClassOnClick('x-form-trigger-click');
22287         }, this);
22288         this.triggers = ts.elements;
22289     },
22290
22291     onTrigger1Click : Roo.emptyFn,
22292     onTrigger2Click : Roo.emptyFn
22293 });/*
22294  * Based on:
22295  * Ext JS Library 1.1.1
22296  * Copyright(c) 2006-2007, Ext JS, LLC.
22297  *
22298  * Originally Released Under LGPL - original licence link has changed is not relivant.
22299  *
22300  * Fork - LGPL
22301  * <script type="text/javascript">
22302  */
22303  
22304 /**
22305  * @class Roo.form.TextArea
22306  * @extends Roo.form.TextField
22307  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22308  * support for auto-sizing.
22309  * @constructor
22310  * Creates a new TextArea
22311  * @param {Object} config Configuration options
22312  */
22313 Roo.form.TextArea = function(config){
22314     Roo.form.TextArea.superclass.constructor.call(this, config);
22315     // these are provided exchanges for backwards compat
22316     // minHeight/maxHeight were replaced by growMin/growMax to be
22317     // compatible with TextField growing config values
22318     if(this.minHeight !== undefined){
22319         this.growMin = this.minHeight;
22320     }
22321     if(this.maxHeight !== undefined){
22322         this.growMax = this.maxHeight;
22323     }
22324 };
22325
22326 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22327     /**
22328      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22329      */
22330     growMin : 60,
22331     /**
22332      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22333      */
22334     growMax: 1000,
22335     /**
22336      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22337      * in the field (equivalent to setting overflow: hidden, defaults to false)
22338      */
22339     preventScrollbars: false,
22340     /**
22341      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22342      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22343      */
22344
22345     // private
22346     onRender : function(ct, position){
22347         if(!this.el){
22348             this.defaultAutoCreate = {
22349                 tag: "textarea",
22350                 style:"width:300px;height:60px;",
22351                 autocomplete: "off"
22352             };
22353         }
22354         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22355         if(this.grow){
22356             this.textSizeEl = Roo.DomHelper.append(document.body, {
22357                 tag: "pre", cls: "x-form-grow-sizer"
22358             });
22359             if(this.preventScrollbars){
22360                 this.el.setStyle("overflow", "hidden");
22361             }
22362             this.el.setHeight(this.growMin);
22363         }
22364     },
22365
22366     onDestroy : function(){
22367         if(this.textSizeEl){
22368             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22369         }
22370         Roo.form.TextArea.superclass.onDestroy.call(this);
22371     },
22372
22373     // private
22374     onKeyUp : function(e){
22375         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22376             this.autoSize();
22377         }
22378     },
22379
22380     /**
22381      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22382      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22383      */
22384     autoSize : function(){
22385         if(!this.grow || !this.textSizeEl){
22386             return;
22387         }
22388         var el = this.el;
22389         var v = el.dom.value;
22390         var ts = this.textSizeEl;
22391
22392         ts.innerHTML = '';
22393         ts.appendChild(document.createTextNode(v));
22394         v = ts.innerHTML;
22395
22396         Roo.fly(ts).setWidth(this.el.getWidth());
22397         if(v.length < 1){
22398             v = "&#160;&#160;";
22399         }else{
22400             if(Roo.isIE){
22401                 v = v.replace(/\n/g, '<p>&#160;</p>');
22402             }
22403             v += "&#160;\n&#160;";
22404         }
22405         ts.innerHTML = v;
22406         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22407         if(h != this.lastHeight){
22408             this.lastHeight = h;
22409             this.el.setHeight(h);
22410             this.fireEvent("autosize", this, h);
22411         }
22412     }
22413 });/*
22414  * Based on:
22415  * Ext JS Library 1.1.1
22416  * Copyright(c) 2006-2007, Ext JS, LLC.
22417  *
22418  * Originally Released Under LGPL - original licence link has changed is not relivant.
22419  *
22420  * Fork - LGPL
22421  * <script type="text/javascript">
22422  */
22423  
22424
22425 /**
22426  * @class Roo.form.NumberField
22427  * @extends Roo.form.TextField
22428  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22429  * @constructor
22430  * Creates a new NumberField
22431  * @param {Object} config Configuration options
22432  */
22433 Roo.form.NumberField = function(config){
22434     Roo.form.NumberField.superclass.constructor.call(this, config);
22435 };
22436
22437 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22438     /**
22439      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22440      */
22441     fieldClass: "x-form-field x-form-num-field",
22442     /**
22443      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22444      */
22445     allowDecimals : true,
22446     /**
22447      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22448      */
22449     decimalSeparator : ".",
22450     /**
22451      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22452      */
22453     decimalPrecision : 2,
22454     /**
22455      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22456      */
22457     allowNegative : true,
22458     /**
22459      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22460      */
22461     minValue : Number.NEGATIVE_INFINITY,
22462     /**
22463      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22464      */
22465     maxValue : Number.MAX_VALUE,
22466     /**
22467      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22468      */
22469     minText : "The minimum value for this field is {0}",
22470     /**
22471      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22472      */
22473     maxText : "The maximum value for this field is {0}",
22474     /**
22475      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22476      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22477      */
22478     nanText : "{0} is not a valid number",
22479
22480     // private
22481     initEvents : function(){
22482         Roo.form.NumberField.superclass.initEvents.call(this);
22483         var allowed = "0123456789";
22484         if(this.allowDecimals){
22485             allowed += this.decimalSeparator;
22486         }
22487         if(this.allowNegative){
22488             allowed += "-";
22489         }
22490         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22491         var keyPress = function(e){
22492             var k = e.getKey();
22493             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22494                 return;
22495             }
22496             var c = e.getCharCode();
22497             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22498                 e.stopEvent();
22499             }
22500         };
22501         this.el.on("keypress", keyPress, this);
22502     },
22503
22504     // private
22505     validateValue : function(value){
22506         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22507             return false;
22508         }
22509         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22510              return true;
22511         }
22512         var num = this.parseValue(value);
22513         if(isNaN(num)){
22514             this.markInvalid(String.format(this.nanText, value));
22515             return false;
22516         }
22517         if(num < this.minValue){
22518             this.markInvalid(String.format(this.minText, this.minValue));
22519             return false;
22520         }
22521         if(num > this.maxValue){
22522             this.markInvalid(String.format(this.maxText, this.maxValue));
22523             return false;
22524         }
22525         return true;
22526     },
22527
22528     getValue : function(){
22529         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22530     },
22531
22532     // private
22533     parseValue : function(value){
22534         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22535         return isNaN(value) ? '' : value;
22536     },
22537
22538     // private
22539     fixPrecision : function(value){
22540         var nan = isNaN(value);
22541         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22542             return nan ? '' : value;
22543         }
22544         return parseFloat(value).toFixed(this.decimalPrecision);
22545     },
22546
22547     setValue : function(v){
22548         v = this.fixPrecision(v);
22549         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22550     },
22551
22552     // private
22553     decimalPrecisionFcn : function(v){
22554         return Math.floor(v);
22555     },
22556
22557     beforeBlur : function(){
22558         var v = this.parseValue(this.getRawValue());
22559         if(v){
22560             this.setValue(v);
22561         }
22562     }
22563 });/*
22564  * Based on:
22565  * Ext JS Library 1.1.1
22566  * Copyright(c) 2006-2007, Ext JS, LLC.
22567  *
22568  * Originally Released Under LGPL - original licence link has changed is not relivant.
22569  *
22570  * Fork - LGPL
22571  * <script type="text/javascript">
22572  */
22573  
22574 /**
22575  * @class Roo.form.DateField
22576  * @extends Roo.form.TriggerField
22577  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22578 * @constructor
22579 * Create a new DateField
22580 * @param {Object} config
22581  */
22582 Roo.form.DateField = function(config){
22583     Roo.form.DateField.superclass.constructor.call(this, config);
22584     
22585       this.addEvents({
22586          
22587         /**
22588          * @event select
22589          * Fires when a date is selected
22590              * @param {Roo.form.DateField} combo This combo box
22591              * @param {Date} date The date selected
22592              */
22593         'select' : true
22594          
22595     });
22596     
22597     
22598     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22599     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22600     this.ddMatch = null;
22601     if(this.disabledDates){
22602         var dd = this.disabledDates;
22603         var re = "(?:";
22604         for(var i = 0; i < dd.length; i++){
22605             re += dd[i];
22606             if(i != dd.length-1) re += "|";
22607         }
22608         this.ddMatch = new RegExp(re + ")");
22609     }
22610 };
22611
22612 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22613     /**
22614      * @cfg {String} format
22615      * The default date format string which can be overriden for localization support.  The format must be
22616      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22617      */
22618     format : "m/d/y",
22619     /**
22620      * @cfg {String} altFormats
22621      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22622      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22623      */
22624     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22625     /**
22626      * @cfg {Array} disabledDays
22627      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22628      */
22629     disabledDays : null,
22630     /**
22631      * @cfg {String} disabledDaysText
22632      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22633      */
22634     disabledDaysText : "Disabled",
22635     /**
22636      * @cfg {Array} disabledDates
22637      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22638      * expression so they are very powerful. Some examples:
22639      * <ul>
22640      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22641      * <li>["03/08", "09/16"] would disable those days for every year</li>
22642      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22643      * <li>["03/../2006"] would disable every day in March 2006</li>
22644      * <li>["^03"] would disable every day in every March</li>
22645      * </ul>
22646      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22647      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22648      */
22649     disabledDates : null,
22650     /**
22651      * @cfg {String} disabledDatesText
22652      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22653      */
22654     disabledDatesText : "Disabled",
22655     /**
22656      * @cfg {Date/String} minValue
22657      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22658      * valid format (defaults to null).
22659      */
22660     minValue : null,
22661     /**
22662      * @cfg {Date/String} maxValue
22663      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22664      * valid format (defaults to null).
22665      */
22666     maxValue : null,
22667     /**
22668      * @cfg {String} minText
22669      * The error text to display when the date in the cell is before minValue (defaults to
22670      * 'The date in this field must be after {minValue}').
22671      */
22672     minText : "The date in this field must be equal to or after {0}",
22673     /**
22674      * @cfg {String} maxText
22675      * The error text to display when the date in the cell is after maxValue (defaults to
22676      * 'The date in this field must be before {maxValue}').
22677      */
22678     maxText : "The date in this field must be equal to or before {0}",
22679     /**
22680      * @cfg {String} invalidText
22681      * The error text to display when the date in the field is invalid (defaults to
22682      * '{value} is not a valid date - it must be in the format {format}').
22683      */
22684     invalidText : "{0} is not a valid date - it must be in the format {1}",
22685     /**
22686      * @cfg {String} triggerClass
22687      * An additional CSS class used to style the trigger button.  The trigger will always get the
22688      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22689      * which displays a calendar icon).
22690      */
22691     triggerClass : 'x-form-date-trigger',
22692     
22693
22694     /**
22695      * @cfg {Boolean} useIso
22696      * if enabled, then the date field will use a hidden field to store the 
22697      * real value as iso formated date. default (false)
22698      */ 
22699     useIso : false,
22700     /**
22701      * @cfg {String/Object} autoCreate
22702      * A DomHelper element spec, or true for a default element spec (defaults to
22703      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22704      */ 
22705     // private
22706     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22707     
22708     // private
22709     hiddenField: false,
22710     
22711     onRender : function(ct, position)
22712     {
22713         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22714         if (this.useIso) {
22715             this.el.dom.removeAttribute('name'); 
22716             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22717                     'before', true);
22718             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22719             // prevent input submission
22720             this.hiddenName = this.name;
22721         }
22722             
22723             
22724     },
22725     
22726     // private
22727     validateValue : function(value)
22728     {
22729         value = this.formatDate(value);
22730         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22731             Roo.log('super failed');
22732             return false;
22733         }
22734         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22735              return true;
22736         }
22737         var svalue = value;
22738         value = this.parseDate(value);
22739         if(!value){
22740             Roo.log('parse date failed' + svalue);
22741             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22742             return false;
22743         }
22744         var time = value.getTime();
22745         if(this.minValue && time < this.minValue.getTime()){
22746             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22747             return false;
22748         }
22749         if(this.maxValue && time > this.maxValue.getTime()){
22750             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22751             return false;
22752         }
22753         if(this.disabledDays){
22754             var day = value.getDay();
22755             for(var i = 0; i < this.disabledDays.length; i++) {
22756                 if(day === this.disabledDays[i]){
22757                     this.markInvalid(this.disabledDaysText);
22758                     return false;
22759                 }
22760             }
22761         }
22762         var fvalue = this.formatDate(value);
22763         if(this.ddMatch && this.ddMatch.test(fvalue)){
22764             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22765             return false;
22766         }
22767         return true;
22768     },
22769
22770     // private
22771     // Provides logic to override the default TriggerField.validateBlur which just returns true
22772     validateBlur : function(){
22773         return !this.menu || !this.menu.isVisible();
22774     },
22775
22776     /**
22777      * Returns the current date value of the date field.
22778      * @return {Date} The date value
22779      */
22780     getValue : function(){
22781         
22782         return  this.hiddenField ?
22783                 this.hiddenField.value :
22784                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22785     },
22786
22787     /**
22788      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22789      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22790      * (the default format used is "m/d/y").
22791      * <br />Usage:
22792      * <pre><code>
22793 //All of these calls set the same date value (May 4, 2006)
22794
22795 //Pass a date object:
22796 var dt = new Date('5/4/06');
22797 dateField.setValue(dt);
22798
22799 //Pass a date string (default format):
22800 dateField.setValue('5/4/06');
22801
22802 //Pass a date string (custom format):
22803 dateField.format = 'Y-m-d';
22804 dateField.setValue('2006-5-4');
22805 </code></pre>
22806      * @param {String/Date} date The date or valid date string
22807      */
22808     setValue : function(date){
22809         if (this.hiddenField) {
22810             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22811         }
22812         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22813         // make sure the value field is always stored as a date..
22814         this.value = this.parseDate(date);
22815         
22816         
22817     },
22818
22819     // private
22820     parseDate : function(value){
22821         if(!value || value instanceof Date){
22822             return value;
22823         }
22824         var v = Date.parseDate(value, this.format);
22825          if (!v && this.useIso) {
22826             v = Date.parseDate(value, 'Y-m-d');
22827         }
22828         if(!v && this.altFormats){
22829             if(!this.altFormatsArray){
22830                 this.altFormatsArray = this.altFormats.split("|");
22831             }
22832             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22833                 v = Date.parseDate(value, this.altFormatsArray[i]);
22834             }
22835         }
22836         return v;
22837     },
22838
22839     // private
22840     formatDate : function(date, fmt){
22841         return (!date || !(date instanceof Date)) ?
22842                date : date.dateFormat(fmt || this.format);
22843     },
22844
22845     // private
22846     menuListeners : {
22847         select: function(m, d){
22848             
22849             this.setValue(d);
22850             this.fireEvent('select', this, d);
22851         },
22852         show : function(){ // retain focus styling
22853             this.onFocus();
22854         },
22855         hide : function(){
22856             this.focus.defer(10, this);
22857             var ml = this.menuListeners;
22858             this.menu.un("select", ml.select,  this);
22859             this.menu.un("show", ml.show,  this);
22860             this.menu.un("hide", ml.hide,  this);
22861         }
22862     },
22863
22864     // private
22865     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22866     onTriggerClick : function(){
22867         if(this.disabled){
22868             return;
22869         }
22870         if(this.menu == null){
22871             this.menu = new Roo.menu.DateMenu();
22872         }
22873         Roo.apply(this.menu.picker,  {
22874             showClear: this.allowBlank,
22875             minDate : this.minValue,
22876             maxDate : this.maxValue,
22877             disabledDatesRE : this.ddMatch,
22878             disabledDatesText : this.disabledDatesText,
22879             disabledDays : this.disabledDays,
22880             disabledDaysText : this.disabledDaysText,
22881             format : this.useIso ? 'Y-m-d' : this.format,
22882             minText : String.format(this.minText, this.formatDate(this.minValue)),
22883             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22884         });
22885         this.menu.on(Roo.apply({}, this.menuListeners, {
22886             scope:this
22887         }));
22888         this.menu.picker.setValue(this.getValue() || new Date());
22889         this.menu.show(this.el, "tl-bl?");
22890     },
22891
22892     beforeBlur : function(){
22893         var v = this.parseDate(this.getRawValue());
22894         if(v){
22895             this.setValue(v);
22896         }
22897     }
22898
22899     /** @cfg {Boolean} grow @hide */
22900     /** @cfg {Number} growMin @hide */
22901     /** @cfg {Number} growMax @hide */
22902     /**
22903      * @hide
22904      * @method autoSize
22905      */
22906 });/*
22907  * Based on:
22908  * Ext JS Library 1.1.1
22909  * Copyright(c) 2006-2007, Ext JS, LLC.
22910  *
22911  * Originally Released Under LGPL - original licence link has changed is not relivant.
22912  *
22913  * Fork - LGPL
22914  * <script type="text/javascript">
22915  */
22916  
22917 /**
22918  * @class Roo.form.MonthField
22919  * @extends Roo.form.TriggerField
22920  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22921 * @constructor
22922 * Create a new MonthField
22923 * @param {Object} config
22924  */
22925 Roo.form.MonthField = function(config){
22926     
22927     Roo.form.MonthField.superclass.constructor.call(this, config);
22928     
22929       this.addEvents({
22930          
22931         /**
22932          * @event select
22933          * Fires when a date is selected
22934              * @param {Roo.form.MonthFieeld} combo This combo box
22935              * @param {Date} date The date selected
22936              */
22937         'select' : true
22938          
22939     });
22940     
22941     
22942     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22943     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22944     this.ddMatch = null;
22945     if(this.disabledDates){
22946         var dd = this.disabledDates;
22947         var re = "(?:";
22948         for(var i = 0; i < dd.length; i++){
22949             re += dd[i];
22950             if(i != dd.length-1) re += "|";
22951         }
22952         this.ddMatch = new RegExp(re + ")");
22953     }
22954 };
22955
22956 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22957     /**
22958      * @cfg {String} format
22959      * The default date format string which can be overriden for localization support.  The format must be
22960      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22961      */
22962     format : "M Y",
22963     /**
22964      * @cfg {String} altFormats
22965      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22966      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22967      */
22968     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22969     /**
22970      * @cfg {Array} disabledDays
22971      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22972      */
22973     disabledDays : [0,1,2,3,4,5,6],
22974     /**
22975      * @cfg {String} disabledDaysText
22976      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22977      */
22978     disabledDaysText : "Disabled",
22979     /**
22980      * @cfg {Array} disabledDates
22981      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22982      * expression so they are very powerful. Some examples:
22983      * <ul>
22984      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22985      * <li>["03/08", "09/16"] would disable those days for every year</li>
22986      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22987      * <li>["03/../2006"] would disable every day in March 2006</li>
22988      * <li>["^03"] would disable every day in every March</li>
22989      * </ul>
22990      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22991      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22992      */
22993     disabledDates : null,
22994     /**
22995      * @cfg {String} disabledDatesText
22996      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22997      */
22998     disabledDatesText : "Disabled",
22999     /**
23000      * @cfg {Date/String} minValue
23001      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23002      * valid format (defaults to null).
23003      */
23004     minValue : null,
23005     /**
23006      * @cfg {Date/String} maxValue
23007      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23008      * valid format (defaults to null).
23009      */
23010     maxValue : null,
23011     /**
23012      * @cfg {String} minText
23013      * The error text to display when the date in the cell is before minValue (defaults to
23014      * 'The date in this field must be after {minValue}').
23015      */
23016     minText : "The date in this field must be equal to or after {0}",
23017     /**
23018      * @cfg {String} maxTextf
23019      * The error text to display when the date in the cell is after maxValue (defaults to
23020      * 'The date in this field must be before {maxValue}').
23021      */
23022     maxText : "The date in this field must be equal to or before {0}",
23023     /**
23024      * @cfg {String} invalidText
23025      * The error text to display when the date in the field is invalid (defaults to
23026      * '{value} is not a valid date - it must be in the format {format}').
23027      */
23028     invalidText : "{0} is not a valid date - it must be in the format {1}",
23029     /**
23030      * @cfg {String} triggerClass
23031      * An additional CSS class used to style the trigger button.  The trigger will always get the
23032      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23033      * which displays a calendar icon).
23034      */
23035     triggerClass : 'x-form-date-trigger',
23036     
23037
23038     /**
23039      * @cfg {Boolean} useIso
23040      * if enabled, then the date field will use a hidden field to store the 
23041      * real value as iso formated date. default (true)
23042      */ 
23043     useIso : true,
23044     /**
23045      * @cfg {String/Object} autoCreate
23046      * A DomHelper element spec, or true for a default element spec (defaults to
23047      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23048      */ 
23049     // private
23050     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23051     
23052     // private
23053     hiddenField: false,
23054     
23055     hideMonthPicker : false,
23056     
23057     onRender : function(ct, position)
23058     {
23059         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23060         if (this.useIso) {
23061             this.el.dom.removeAttribute('name'); 
23062             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23063                     'before', true);
23064             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23065             // prevent input submission
23066             this.hiddenName = this.name;
23067         }
23068             
23069             
23070     },
23071     
23072     // private
23073     validateValue : function(value)
23074     {
23075         value = this.formatDate(value);
23076         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23077             return false;
23078         }
23079         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23080              return true;
23081         }
23082         var svalue = value;
23083         value = this.parseDate(value);
23084         if(!value){
23085             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23086             return false;
23087         }
23088         var time = value.getTime();
23089         if(this.minValue && time < this.minValue.getTime()){
23090             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23091             return false;
23092         }
23093         if(this.maxValue && time > this.maxValue.getTime()){
23094             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23095             return false;
23096         }
23097         /*if(this.disabledDays){
23098             var day = value.getDay();
23099             for(var i = 0; i < this.disabledDays.length; i++) {
23100                 if(day === this.disabledDays[i]){
23101                     this.markInvalid(this.disabledDaysText);
23102                     return false;
23103                 }
23104             }
23105         }
23106         */
23107         var fvalue = this.formatDate(value);
23108         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23109             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23110             return false;
23111         }
23112         */
23113         return true;
23114     },
23115
23116     // private
23117     // Provides logic to override the default TriggerField.validateBlur which just returns true
23118     validateBlur : function(){
23119         return !this.menu || !this.menu.isVisible();
23120     },
23121
23122     /**
23123      * Returns the current date value of the date field.
23124      * @return {Date} The date value
23125      */
23126     getValue : function(){
23127         
23128         
23129         
23130         return  this.hiddenField ?
23131                 this.hiddenField.value :
23132                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23133     },
23134
23135     /**
23136      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23137      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23138      * (the default format used is "m/d/y").
23139      * <br />Usage:
23140      * <pre><code>
23141 //All of these calls set the same date value (May 4, 2006)
23142
23143 //Pass a date object:
23144 var dt = new Date('5/4/06');
23145 monthField.setValue(dt);
23146
23147 //Pass a date string (default format):
23148 monthField.setValue('5/4/06');
23149
23150 //Pass a date string (custom format):
23151 monthField.format = 'Y-m-d';
23152 monthField.setValue('2006-5-4');
23153 </code></pre>
23154      * @param {String/Date} date The date or valid date string
23155      */
23156     setValue : function(date){
23157         Roo.log('month setValue' + date);
23158         // can only be first of month..
23159         
23160         var val = this.parseDate(date);
23161         
23162         if (this.hiddenField) {
23163             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23164         }
23165         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23166         this.value = this.parseDate(date);
23167     },
23168
23169     // private
23170     parseDate : function(value){
23171         if(!value || value instanceof Date){
23172             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23173             return value;
23174         }
23175         var v = Date.parseDate(value, this.format);
23176         if (!v && this.useIso) {
23177             v = Date.parseDate(value, 'Y-m-d');
23178         }
23179         if (v) {
23180             // 
23181             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23182         }
23183         
23184         
23185         if(!v && this.altFormats){
23186             if(!this.altFormatsArray){
23187                 this.altFormatsArray = this.altFormats.split("|");
23188             }
23189             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23190                 v = Date.parseDate(value, this.altFormatsArray[i]);
23191             }
23192         }
23193         return v;
23194     },
23195
23196     // private
23197     formatDate : function(date, fmt){
23198         return (!date || !(date instanceof Date)) ?
23199                date : date.dateFormat(fmt || this.format);
23200     },
23201
23202     // private
23203     menuListeners : {
23204         select: function(m, d){
23205             this.setValue(d);
23206             this.fireEvent('select', this, d);
23207         },
23208         show : function(){ // retain focus styling
23209             this.onFocus();
23210         },
23211         hide : function(){
23212             this.focus.defer(10, this);
23213             var ml = this.menuListeners;
23214             this.menu.un("select", ml.select,  this);
23215             this.menu.un("show", ml.show,  this);
23216             this.menu.un("hide", ml.hide,  this);
23217         }
23218     },
23219     // private
23220     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23221     onTriggerClick : function(){
23222         if(this.disabled){
23223             return;
23224         }
23225         if(this.menu == null){
23226             this.menu = new Roo.menu.DateMenu();
23227            
23228         }
23229         
23230         Roo.apply(this.menu.picker,  {
23231             
23232             showClear: this.allowBlank,
23233             minDate : this.minValue,
23234             maxDate : this.maxValue,
23235             disabledDatesRE : this.ddMatch,
23236             disabledDatesText : this.disabledDatesText,
23237             
23238             format : this.useIso ? 'Y-m-d' : this.format,
23239             minText : String.format(this.minText, this.formatDate(this.minValue)),
23240             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23241             
23242         });
23243          this.menu.on(Roo.apply({}, this.menuListeners, {
23244             scope:this
23245         }));
23246        
23247         
23248         var m = this.menu;
23249         var p = m.picker;
23250         
23251         // hide month picker get's called when we called by 'before hide';
23252         
23253         var ignorehide = true;
23254         p.hideMonthPicker  = function(disableAnim){
23255             if (ignorehide) {
23256                 return;
23257             }
23258              if(this.monthPicker){
23259                 Roo.log("hideMonthPicker called");
23260                 if(disableAnim === true){
23261                     this.monthPicker.hide();
23262                 }else{
23263                     this.monthPicker.slideOut('t', {duration:.2});
23264                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23265                     p.fireEvent("select", this, this.value);
23266                     m.hide();
23267                 }
23268             }
23269         }
23270         
23271         Roo.log('picker set value');
23272         Roo.log(this.getValue());
23273         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23274         m.show(this.el, 'tl-bl?');
23275         ignorehide  = false;
23276         // this will trigger hideMonthPicker..
23277         
23278         
23279         // hidden the day picker
23280         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23281         
23282         
23283         
23284       
23285         
23286         p.showMonthPicker.defer(100, p);
23287     
23288         
23289        
23290     },
23291
23292     beforeBlur : function(){
23293         var v = this.parseDate(this.getRawValue());
23294         if(v){
23295             this.setValue(v);
23296         }
23297     }
23298
23299     /** @cfg {Boolean} grow @hide */
23300     /** @cfg {Number} growMin @hide */
23301     /** @cfg {Number} growMax @hide */
23302     /**
23303      * @hide
23304      * @method autoSize
23305      */
23306 });/*
23307  * Based on:
23308  * Ext JS Library 1.1.1
23309  * Copyright(c) 2006-2007, Ext JS, LLC.
23310  *
23311  * Originally Released Under LGPL - original licence link has changed is not relivant.
23312  *
23313  * Fork - LGPL
23314  * <script type="text/javascript">
23315  */
23316  
23317
23318 /**
23319  * @class Roo.form.ComboBox
23320  * @extends Roo.form.TriggerField
23321  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23322  * @constructor
23323  * Create a new ComboBox.
23324  * @param {Object} config Configuration options
23325  */
23326 Roo.form.ComboBox = function(config){
23327     Roo.form.ComboBox.superclass.constructor.call(this, config);
23328     this.addEvents({
23329         /**
23330          * @event expand
23331          * Fires when the dropdown list is expanded
23332              * @param {Roo.form.ComboBox} combo This combo box
23333              */
23334         'expand' : true,
23335         /**
23336          * @event collapse
23337          * Fires when the dropdown list is collapsed
23338              * @param {Roo.form.ComboBox} combo This combo box
23339              */
23340         'collapse' : true,
23341         /**
23342          * @event beforeselect
23343          * Fires before a list item is selected. Return false to cancel the selection.
23344              * @param {Roo.form.ComboBox} combo This combo box
23345              * @param {Roo.data.Record} record The data record returned from the underlying store
23346              * @param {Number} index The index of the selected item in the dropdown list
23347              */
23348         'beforeselect' : true,
23349         /**
23350          * @event select
23351          * Fires when a list item is selected
23352              * @param {Roo.form.ComboBox} combo This combo box
23353              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23354              * @param {Number} index The index of the selected item in the dropdown list
23355              */
23356         'select' : true,
23357         /**
23358          * @event beforequery
23359          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23360          * The event object passed has these properties:
23361              * @param {Roo.form.ComboBox} combo This combo box
23362              * @param {String} query The query
23363              * @param {Boolean} forceAll true to force "all" query
23364              * @param {Boolean} cancel true to cancel the query
23365              * @param {Object} e The query event object
23366              */
23367         'beforequery': true,
23368          /**
23369          * @event add
23370          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23371              * @param {Roo.form.ComboBox} combo This combo box
23372              */
23373         'add' : true,
23374         /**
23375          * @event edit
23376          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23377              * @param {Roo.form.ComboBox} combo This combo box
23378              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23379              */
23380         'edit' : true
23381         
23382         
23383     });
23384     if(this.transform){
23385         this.allowDomMove = false;
23386         var s = Roo.getDom(this.transform);
23387         if(!this.hiddenName){
23388             this.hiddenName = s.name;
23389         }
23390         if(!this.store){
23391             this.mode = 'local';
23392             var d = [], opts = s.options;
23393             for(var i = 0, len = opts.length;i < len; i++){
23394                 var o = opts[i];
23395                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23396                 if(o.selected) {
23397                     this.value = value;
23398                 }
23399                 d.push([value, o.text]);
23400             }
23401             this.store = new Roo.data.SimpleStore({
23402                 'id': 0,
23403                 fields: ['value', 'text'],
23404                 data : d
23405             });
23406             this.valueField = 'value';
23407             this.displayField = 'text';
23408         }
23409         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23410         if(!this.lazyRender){
23411             this.target = true;
23412             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23413             s.parentNode.removeChild(s); // remove it
23414             this.render(this.el.parentNode);
23415         }else{
23416             s.parentNode.removeChild(s); // remove it
23417         }
23418
23419     }
23420     if (this.store) {
23421         this.store = Roo.factory(this.store, Roo.data);
23422     }
23423     
23424     this.selectedIndex = -1;
23425     if(this.mode == 'local'){
23426         if(config.queryDelay === undefined){
23427             this.queryDelay = 10;
23428         }
23429         if(config.minChars === undefined){
23430             this.minChars = 0;
23431         }
23432     }
23433 };
23434
23435 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23436     /**
23437      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23438      */
23439     /**
23440      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23441      * rendering into an Roo.Editor, defaults to false)
23442      */
23443     /**
23444      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23445      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23446      */
23447     /**
23448      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23449      */
23450     /**
23451      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23452      * the dropdown list (defaults to undefined, with no header element)
23453      */
23454
23455      /**
23456      * @cfg {String/Roo.Template} tpl The template to use to render the output
23457      */
23458      
23459     // private
23460     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23461     /**
23462      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23463      */
23464     listWidth: undefined,
23465     /**
23466      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23467      * mode = 'remote' or 'text' if mode = 'local')
23468      */
23469     displayField: undefined,
23470     /**
23471      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23472      * mode = 'remote' or 'value' if mode = 'local'). 
23473      * Note: use of a valueField requires the user make a selection
23474      * in order for a value to be mapped.
23475      */
23476     valueField: undefined,
23477     
23478     
23479     /**
23480      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23481      * field's data value (defaults to the underlying DOM element's name)
23482      */
23483     hiddenName: undefined,
23484     /**
23485      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23486      */
23487     listClass: '',
23488     /**
23489      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23490      */
23491     selectedClass: 'x-combo-selected',
23492     /**
23493      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23494      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23495      * which displays a downward arrow icon).
23496      */
23497     triggerClass : 'x-form-arrow-trigger',
23498     /**
23499      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23500      */
23501     shadow:'sides',
23502     /**
23503      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23504      * anchor positions (defaults to 'tl-bl')
23505      */
23506     listAlign: 'tl-bl?',
23507     /**
23508      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23509      */
23510     maxHeight: 300,
23511     /**
23512      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23513      * query specified by the allQuery config option (defaults to 'query')
23514      */
23515     triggerAction: 'query',
23516     /**
23517      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23518      * (defaults to 4, does not apply if editable = false)
23519      */
23520     minChars : 4,
23521     /**
23522      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23523      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23524      */
23525     typeAhead: false,
23526     /**
23527      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23528      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23529      */
23530     queryDelay: 500,
23531     /**
23532      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23533      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23534      */
23535     pageSize: 0,
23536     /**
23537      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23538      * when editable = true (defaults to false)
23539      */
23540     selectOnFocus:false,
23541     /**
23542      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23543      */
23544     queryParam: 'query',
23545     /**
23546      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23547      * when mode = 'remote' (defaults to 'Loading...')
23548      */
23549     loadingText: 'Loading...',
23550     /**
23551      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23552      */
23553     resizable: false,
23554     /**
23555      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23556      */
23557     handleHeight : 8,
23558     /**
23559      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23560      * traditional select (defaults to true)
23561      */
23562     editable: true,
23563     /**
23564      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23565      */
23566     allQuery: '',
23567     /**
23568      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23569      */
23570     mode: 'remote',
23571     /**
23572      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23573      * listWidth has a higher value)
23574      */
23575     minListWidth : 70,
23576     /**
23577      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23578      * allow the user to set arbitrary text into the field (defaults to false)
23579      */
23580     forceSelection:false,
23581     /**
23582      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23583      * if typeAhead = true (defaults to 250)
23584      */
23585     typeAheadDelay : 250,
23586     /**
23587      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23588      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23589      */
23590     valueNotFoundText : undefined,
23591     /**
23592      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23593      */
23594     blockFocus : false,
23595     
23596     /**
23597      * @cfg {Boolean} disableClear Disable showing of clear button.
23598      */
23599     disableClear : false,
23600     /**
23601      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23602      */
23603     alwaysQuery : false,
23604     
23605     //private
23606     addicon : false,
23607     editicon: false,
23608     
23609     // element that contains real text value.. (when hidden is used..)
23610      
23611     // private
23612     onRender : function(ct, position){
23613         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23614         if(this.hiddenName){
23615             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23616                     'before', true);
23617             this.hiddenField.value =
23618                 this.hiddenValue !== undefined ? this.hiddenValue :
23619                 this.value !== undefined ? this.value : '';
23620
23621             // prevent input submission
23622             this.el.dom.removeAttribute('name');
23623              
23624              
23625         }
23626         if(Roo.isGecko){
23627             this.el.dom.setAttribute('autocomplete', 'off');
23628         }
23629
23630         var cls = 'x-combo-list';
23631
23632         this.list = new Roo.Layer({
23633             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23634         });
23635
23636         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23637         this.list.setWidth(lw);
23638         this.list.swallowEvent('mousewheel');
23639         this.assetHeight = 0;
23640
23641         if(this.title){
23642             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23643             this.assetHeight += this.header.getHeight();
23644         }
23645
23646         this.innerList = this.list.createChild({cls:cls+'-inner'});
23647         this.innerList.on('mouseover', this.onViewOver, this);
23648         this.innerList.on('mousemove', this.onViewMove, this);
23649         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23650         
23651         if(this.allowBlank && !this.pageSize && !this.disableClear){
23652             this.footer = this.list.createChild({cls:cls+'-ft'});
23653             this.pageTb = new Roo.Toolbar(this.footer);
23654            
23655         }
23656         if(this.pageSize){
23657             this.footer = this.list.createChild({cls:cls+'-ft'});
23658             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23659                     {pageSize: this.pageSize});
23660             
23661         }
23662         
23663         if (this.pageTb && this.allowBlank && !this.disableClear) {
23664             var _this = this;
23665             this.pageTb.add(new Roo.Toolbar.Fill(), {
23666                 cls: 'x-btn-icon x-btn-clear',
23667                 text: '&#160;',
23668                 handler: function()
23669                 {
23670                     _this.collapse();
23671                     _this.clearValue();
23672                     _this.onSelect(false, -1);
23673                 }
23674             });
23675         }
23676         if (this.footer) {
23677             this.assetHeight += this.footer.getHeight();
23678         }
23679         
23680
23681         if(!this.tpl){
23682             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23683         }
23684
23685         this.view = new Roo.View(this.innerList, this.tpl, {
23686             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23687         });
23688
23689         this.view.on('click', this.onViewClick, this);
23690
23691         this.store.on('beforeload', this.onBeforeLoad, this);
23692         this.store.on('load', this.onLoad, this);
23693         this.store.on('loadexception', this.onLoadException, this);
23694
23695         if(this.resizable){
23696             this.resizer = new Roo.Resizable(this.list,  {
23697                pinned:true, handles:'se'
23698             });
23699             this.resizer.on('resize', function(r, w, h){
23700                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23701                 this.listWidth = w;
23702                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23703                 this.restrictHeight();
23704             }, this);
23705             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23706         }
23707         if(!this.editable){
23708             this.editable = true;
23709             this.setEditable(false);
23710         }  
23711         
23712         
23713         if (typeof(this.events.add.listeners) != 'undefined') {
23714             
23715             this.addicon = this.wrap.createChild(
23716                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23717        
23718             this.addicon.on('click', function(e) {
23719                 this.fireEvent('add', this);
23720             }, this);
23721         }
23722         if (typeof(this.events.edit.listeners) != 'undefined') {
23723             
23724             this.editicon = this.wrap.createChild(
23725                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23726             if (this.addicon) {
23727                 this.editicon.setStyle('margin-left', '40px');
23728             }
23729             this.editicon.on('click', function(e) {
23730                 
23731                 // we fire even  if inothing is selected..
23732                 this.fireEvent('edit', this, this.lastData );
23733                 
23734             }, this);
23735         }
23736         
23737         
23738         
23739     },
23740
23741     // private
23742     initEvents : function(){
23743         Roo.form.ComboBox.superclass.initEvents.call(this);
23744
23745         this.keyNav = new Roo.KeyNav(this.el, {
23746             "up" : function(e){
23747                 this.inKeyMode = true;
23748                 this.selectPrev();
23749             },
23750
23751             "down" : function(e){
23752                 if(!this.isExpanded()){
23753                     this.onTriggerClick();
23754                 }else{
23755                     this.inKeyMode = true;
23756                     this.selectNext();
23757                 }
23758             },
23759
23760             "enter" : function(e){
23761                 this.onViewClick();
23762                 //return true;
23763             },
23764
23765             "esc" : function(e){
23766                 this.collapse();
23767             },
23768
23769             "tab" : function(e){
23770                 this.onViewClick(false);
23771                 this.fireEvent("specialkey", this, e);
23772                 return true;
23773             },
23774
23775             scope : this,
23776
23777             doRelay : function(foo, bar, hname){
23778                 if(hname == 'down' || this.scope.isExpanded()){
23779                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23780                 }
23781                 return true;
23782             },
23783
23784             forceKeyDown: true
23785         });
23786         this.queryDelay = Math.max(this.queryDelay || 10,
23787                 this.mode == 'local' ? 10 : 250);
23788         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23789         if(this.typeAhead){
23790             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23791         }
23792         if(this.editable !== false){
23793             this.el.on("keyup", this.onKeyUp, this);
23794         }
23795         if(this.forceSelection){
23796             this.on('blur', this.doForce, this);
23797         }
23798     },
23799
23800     onDestroy : function(){
23801         if(this.view){
23802             this.view.setStore(null);
23803             this.view.el.removeAllListeners();
23804             this.view.el.remove();
23805             this.view.purgeListeners();
23806         }
23807         if(this.list){
23808             this.list.destroy();
23809         }
23810         if(this.store){
23811             this.store.un('beforeload', this.onBeforeLoad, this);
23812             this.store.un('load', this.onLoad, this);
23813             this.store.un('loadexception', this.onLoadException, this);
23814         }
23815         Roo.form.ComboBox.superclass.onDestroy.call(this);
23816     },
23817
23818     // private
23819     fireKey : function(e){
23820         if(e.isNavKeyPress() && !this.list.isVisible()){
23821             this.fireEvent("specialkey", this, e);
23822         }
23823     },
23824
23825     // private
23826     onResize: function(w, h){
23827         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23828         
23829         if(typeof w != 'number'){
23830             // we do not handle it!?!?
23831             return;
23832         }
23833         var tw = this.trigger.getWidth();
23834         tw += this.addicon ? this.addicon.getWidth() : 0;
23835         tw += this.editicon ? this.editicon.getWidth() : 0;
23836         var x = w - tw;
23837         this.el.setWidth( this.adjustWidth('input', x));
23838             
23839         this.trigger.setStyle('left', x+'px');
23840         
23841         if(this.list && this.listWidth === undefined){
23842             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23843             this.list.setWidth(lw);
23844             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23845         }
23846         
23847     
23848         
23849     },
23850
23851     /**
23852      * Allow or prevent the user from directly editing the field text.  If false is passed,
23853      * the user will only be able to select from the items defined in the dropdown list.  This method
23854      * is the runtime equivalent of setting the 'editable' config option at config time.
23855      * @param {Boolean} value True to allow the user to directly edit the field text
23856      */
23857     setEditable : function(value){
23858         if(value == this.editable){
23859             return;
23860         }
23861         this.editable = value;
23862         if(!value){
23863             this.el.dom.setAttribute('readOnly', true);
23864             this.el.on('mousedown', this.onTriggerClick,  this);
23865             this.el.addClass('x-combo-noedit');
23866         }else{
23867             this.el.dom.setAttribute('readOnly', false);
23868             this.el.un('mousedown', this.onTriggerClick,  this);
23869             this.el.removeClass('x-combo-noedit');
23870         }
23871     },
23872
23873     // private
23874     onBeforeLoad : function(){
23875         if(!this.hasFocus){
23876             return;
23877         }
23878         this.innerList.update(this.loadingText ?
23879                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23880         this.restrictHeight();
23881         this.selectedIndex = -1;
23882     },
23883
23884     // private
23885     onLoad : function(){
23886         if(!this.hasFocus){
23887             return;
23888         }
23889         if(this.store.getCount() > 0){
23890             this.expand();
23891             this.restrictHeight();
23892             if(this.lastQuery == this.allQuery){
23893                 if(this.editable){
23894                     this.el.dom.select();
23895                 }
23896                 if(!this.selectByValue(this.value, true)){
23897                     this.select(0, true);
23898                 }
23899             }else{
23900                 this.selectNext();
23901                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23902                     this.taTask.delay(this.typeAheadDelay);
23903                 }
23904             }
23905         }else{
23906             this.onEmptyResults();
23907         }
23908         //this.el.focus();
23909     },
23910     // private
23911     onLoadException : function()
23912     {
23913         this.collapse();
23914         Roo.log(this.store.reader.jsonData);
23915         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23916             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23917         }
23918         
23919         
23920     },
23921     // private
23922     onTypeAhead : function(){
23923         if(this.store.getCount() > 0){
23924             var r = this.store.getAt(0);
23925             var newValue = r.data[this.displayField];
23926             var len = newValue.length;
23927             var selStart = this.getRawValue().length;
23928             if(selStart != len){
23929                 this.setRawValue(newValue);
23930                 this.selectText(selStart, newValue.length);
23931             }
23932         }
23933     },
23934
23935     // private
23936     onSelect : function(record, index){
23937         if(this.fireEvent('beforeselect', this, record, index) !== false){
23938             this.setFromData(index > -1 ? record.data : false);
23939             this.collapse();
23940             this.fireEvent('select', this, record, index);
23941         }
23942     },
23943
23944     /**
23945      * Returns the currently selected field value or empty string if no value is set.
23946      * @return {String} value The selected value
23947      */
23948     getValue : function(){
23949         if(this.valueField){
23950             return typeof this.value != 'undefined' ? this.value : '';
23951         }else{
23952             return Roo.form.ComboBox.superclass.getValue.call(this);
23953         }
23954     },
23955
23956     /**
23957      * Clears any text/value currently set in the field
23958      */
23959     clearValue : function(){
23960         if(this.hiddenField){
23961             this.hiddenField.value = '';
23962         }
23963         this.value = '';
23964         this.setRawValue('');
23965         this.lastSelectionText = '';
23966         this.applyEmptyText();
23967     },
23968
23969     /**
23970      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23971      * will be displayed in the field.  If the value does not match the data value of an existing item,
23972      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23973      * Otherwise the field will be blank (although the value will still be set).
23974      * @param {String} value The value to match
23975      */
23976     setValue : function(v){
23977         var text = v;
23978         if(this.valueField){
23979             var r = this.findRecord(this.valueField, v);
23980             if(r){
23981                 text = r.data[this.displayField];
23982             }else if(this.valueNotFoundText !== undefined){
23983                 text = this.valueNotFoundText;
23984             }
23985         }
23986         this.lastSelectionText = text;
23987         if(this.hiddenField){
23988             this.hiddenField.value = v;
23989         }
23990         Roo.form.ComboBox.superclass.setValue.call(this, text);
23991         this.value = v;
23992     },
23993     /**
23994      * @property {Object} the last set data for the element
23995      */
23996     
23997     lastData : false,
23998     /**
23999      * Sets the value of the field based on a object which is related to the record format for the store.
24000      * @param {Object} value the value to set as. or false on reset?
24001      */
24002     setFromData : function(o){
24003         var dv = ''; // display value
24004         var vv = ''; // value value..
24005         this.lastData = o;
24006         if (this.displayField) {
24007             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24008         } else {
24009             // this is an error condition!!!
24010             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24011         }
24012         
24013         if(this.valueField){
24014             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24015         }
24016         if(this.hiddenField){
24017             this.hiddenField.value = vv;
24018             
24019             this.lastSelectionText = dv;
24020             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24021             this.value = vv;
24022             return;
24023         }
24024         // no hidden field.. - we store the value in 'value', but still display
24025         // display field!!!!
24026         this.lastSelectionText = dv;
24027         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24028         this.value = vv;
24029         
24030         
24031     },
24032     // private
24033     reset : function(){
24034         // overridden so that last data is reset..
24035         this.setValue(this.originalValue);
24036         this.clearInvalid();
24037         this.lastData = false;
24038         if (this.view) {
24039             this.view.clearSelections();
24040         }
24041     },
24042     // private
24043     findRecord : function(prop, value){
24044         var record;
24045         if(this.store.getCount() > 0){
24046             this.store.each(function(r){
24047                 if(r.data[prop] == value){
24048                     record = r;
24049                     return false;
24050                 }
24051                 return true;
24052             });
24053         }
24054         return record;
24055     },
24056     
24057     getName: function()
24058     {
24059         // returns hidden if it's set..
24060         if (!this.rendered) {return ''};
24061         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24062         
24063     },
24064     // private
24065     onViewMove : function(e, t){
24066         this.inKeyMode = false;
24067     },
24068
24069     // private
24070     onViewOver : function(e, t){
24071         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24072             return;
24073         }
24074         var item = this.view.findItemFromChild(t);
24075         if(item){
24076             var index = this.view.indexOf(item);
24077             this.select(index, false);
24078         }
24079     },
24080
24081     // private
24082     onViewClick : function(doFocus)
24083     {
24084         var index = this.view.getSelectedIndexes()[0];
24085         var r = this.store.getAt(index);
24086         if(r){
24087             this.onSelect(r, index);
24088         }
24089         if(doFocus !== false && !this.blockFocus){
24090             this.el.focus();
24091         }
24092     },
24093
24094     // private
24095     restrictHeight : function(){
24096         this.innerList.dom.style.height = '';
24097         var inner = this.innerList.dom;
24098         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24099         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24100         this.list.beginUpdate();
24101         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24102         this.list.alignTo(this.el, this.listAlign);
24103         this.list.endUpdate();
24104     },
24105
24106     // private
24107     onEmptyResults : function(){
24108         this.collapse();
24109     },
24110
24111     /**
24112      * Returns true if the dropdown list is expanded, else false.
24113      */
24114     isExpanded : function(){
24115         return this.list.isVisible();
24116     },
24117
24118     /**
24119      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24120      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24121      * @param {String} value The data value of the item to select
24122      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24123      * selected item if it is not currently in view (defaults to true)
24124      * @return {Boolean} True if the value matched an item in the list, else false
24125      */
24126     selectByValue : function(v, scrollIntoView){
24127         if(v !== undefined && v !== null){
24128             var r = this.findRecord(this.valueField || this.displayField, v);
24129             if(r){
24130                 this.select(this.store.indexOf(r), scrollIntoView);
24131                 return true;
24132             }
24133         }
24134         return false;
24135     },
24136
24137     /**
24138      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24139      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24140      * @param {Number} index The zero-based index of the list item to select
24141      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24142      * selected item if it is not currently in view (defaults to true)
24143      */
24144     select : function(index, scrollIntoView){
24145         this.selectedIndex = index;
24146         this.view.select(index);
24147         if(scrollIntoView !== false){
24148             var el = this.view.getNode(index);
24149             if(el){
24150                 this.innerList.scrollChildIntoView(el, false);
24151             }
24152         }
24153     },
24154
24155     // private
24156     selectNext : function(){
24157         var ct = this.store.getCount();
24158         if(ct > 0){
24159             if(this.selectedIndex == -1){
24160                 this.select(0);
24161             }else if(this.selectedIndex < ct-1){
24162                 this.select(this.selectedIndex+1);
24163             }
24164         }
24165     },
24166
24167     // private
24168     selectPrev : function(){
24169         var ct = this.store.getCount();
24170         if(ct > 0){
24171             if(this.selectedIndex == -1){
24172                 this.select(0);
24173             }else if(this.selectedIndex != 0){
24174                 this.select(this.selectedIndex-1);
24175             }
24176         }
24177     },
24178
24179     // private
24180     onKeyUp : function(e){
24181         if(this.editable !== false && !e.isSpecialKey()){
24182             this.lastKey = e.getKey();
24183             this.dqTask.delay(this.queryDelay);
24184         }
24185     },
24186
24187     // private
24188     validateBlur : function(){
24189         return !this.list || !this.list.isVisible();   
24190     },
24191
24192     // private
24193     initQuery : function(){
24194         this.doQuery(this.getRawValue());
24195     },
24196
24197     // private
24198     doForce : function(){
24199         if(this.el.dom.value.length > 0){
24200             this.el.dom.value =
24201                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24202             this.applyEmptyText();
24203         }
24204     },
24205
24206     /**
24207      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24208      * query allowing the query action to be canceled if needed.
24209      * @param {String} query The SQL query to execute
24210      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24211      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24212      * saved in the current store (defaults to false)
24213      */
24214     doQuery : function(q, forceAll){
24215         if(q === undefined || q === null){
24216             q = '';
24217         }
24218         var qe = {
24219             query: q,
24220             forceAll: forceAll,
24221             combo: this,
24222             cancel:false
24223         };
24224         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24225             return false;
24226         }
24227         q = qe.query;
24228         forceAll = qe.forceAll;
24229         if(forceAll === true || (q.length >= this.minChars)){
24230             if(this.lastQuery != q || this.alwaysQuery){
24231                 this.lastQuery = q;
24232                 if(this.mode == 'local'){
24233                     this.selectedIndex = -1;
24234                     if(forceAll){
24235                         this.store.clearFilter();
24236                     }else{
24237                         this.store.filter(this.displayField, q);
24238                     }
24239                     this.onLoad();
24240                 }else{
24241                     this.store.baseParams[this.queryParam] = q;
24242                     this.store.load({
24243                         params: this.getParams(q)
24244                     });
24245                     this.expand();
24246                 }
24247             }else{
24248                 this.selectedIndex = -1;
24249                 this.onLoad();   
24250             }
24251         }
24252     },
24253
24254     // private
24255     getParams : function(q){
24256         var p = {};
24257         //p[this.queryParam] = q;
24258         if(this.pageSize){
24259             p.start = 0;
24260             p.limit = this.pageSize;
24261         }
24262         return p;
24263     },
24264
24265     /**
24266      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24267      */
24268     collapse : function(){
24269         if(!this.isExpanded()){
24270             return;
24271         }
24272         this.list.hide();
24273         Roo.get(document).un('mousedown', this.collapseIf, this);
24274         Roo.get(document).un('mousewheel', this.collapseIf, this);
24275         if (!this.editable) {
24276             Roo.get(document).un('keydown', this.listKeyPress, this);
24277         }
24278         this.fireEvent('collapse', this);
24279     },
24280
24281     // private
24282     collapseIf : function(e){
24283         if(!e.within(this.wrap) && !e.within(this.list)){
24284             this.collapse();
24285         }
24286     },
24287
24288     /**
24289      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24290      */
24291     expand : function(){
24292         if(this.isExpanded() || !this.hasFocus){
24293             return;
24294         }
24295         this.list.alignTo(this.el, this.listAlign);
24296         this.list.show();
24297         Roo.get(document).on('mousedown', this.collapseIf, this);
24298         Roo.get(document).on('mousewheel', this.collapseIf, this);
24299         if (!this.editable) {
24300             Roo.get(document).on('keydown', this.listKeyPress, this);
24301         }
24302         
24303         this.fireEvent('expand', this);
24304     },
24305
24306     // private
24307     // Implements the default empty TriggerField.onTriggerClick function
24308     onTriggerClick : function(){
24309         if(this.disabled){
24310             return;
24311         }
24312         if(this.isExpanded()){
24313             this.collapse();
24314             if (!this.blockFocus) {
24315                 this.el.focus();
24316             }
24317             
24318         }else {
24319             this.hasFocus = true;
24320             if(this.triggerAction == 'all') {
24321                 this.doQuery(this.allQuery, true);
24322             } else {
24323                 this.doQuery(this.getRawValue());
24324             }
24325             if (!this.blockFocus) {
24326                 this.el.focus();
24327             }
24328         }
24329     },
24330     listKeyPress : function(e)
24331     {
24332         //Roo.log('listkeypress');
24333         // scroll to first matching element based on key pres..
24334         if (e.isSpecialKey()) {
24335             return false;
24336         }
24337         var k = String.fromCharCode(e.getKey()).toUpperCase();
24338         //Roo.log(k);
24339         var match  = false;
24340         var csel = this.view.getSelectedNodes();
24341         var cselitem = false;
24342         if (csel.length) {
24343             var ix = this.view.indexOf(csel[0]);
24344             cselitem  = this.store.getAt(ix);
24345             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24346                 cselitem = false;
24347             }
24348             
24349         }
24350         
24351         this.store.each(function(v) { 
24352             if (cselitem) {
24353                 // start at existing selection.
24354                 if (cselitem.id == v.id) {
24355                     cselitem = false;
24356                 }
24357                 return;
24358             }
24359                 
24360             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24361                 match = this.store.indexOf(v);
24362                 return false;
24363             }
24364         }, this);
24365         
24366         if (match === false) {
24367             return true; // no more action?
24368         }
24369         // scroll to?
24370         this.view.select(match);
24371         var sn = Roo.get(this.view.getSelectedNodes()[0])
24372         sn.scrollIntoView(sn.dom.parentNode, false);
24373     }
24374
24375     /** 
24376     * @cfg {Boolean} grow 
24377     * @hide 
24378     */
24379     /** 
24380     * @cfg {Number} growMin 
24381     * @hide 
24382     */
24383     /** 
24384     * @cfg {Number} growMax 
24385     * @hide 
24386     */
24387     /**
24388      * @hide
24389      * @method autoSize
24390      */
24391 });/*
24392  * Copyright(c) 2010-2012, Roo J Solutions Limited
24393  *
24394  * Licence LGPL
24395  *
24396  */
24397
24398 /**
24399  * @class Roo.form.ComboBoxArray
24400  * @extends Roo.form.TextField
24401  * A facebook style adder... for lists of email / people / countries  etc...
24402  * pick multiple items from a combo box, and shows each one.
24403  *
24404  *  Fred [x]  Brian [x]  [Pick another |v]
24405  *
24406  *
24407  *  For this to work: it needs various extra information
24408  *    - normal combo problay has
24409  *      name, hiddenName
24410  *    + displayField, valueField
24411  *
24412  *    For our purpose...
24413  *
24414  *
24415  *   If we change from 'extends' to wrapping...
24416  *   
24417  *  
24418  *
24419  
24420  
24421  * @constructor
24422  * Create a new ComboBoxArray.
24423  * @param {Object} config Configuration options
24424  */
24425  
24426
24427 Roo.form.ComboBoxArray = function(config)
24428 {
24429     
24430     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24431     
24432     this.items = new Roo.util.MixedCollection(false);
24433     
24434     // construct the child combo...
24435     
24436     
24437     
24438     
24439    
24440     
24441 }
24442
24443  
24444 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24445
24446     /**
24447      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24448      */
24449     
24450     lastData : false,
24451     
24452     // behavies liek a hiddne field
24453     inputType:      'hidden',
24454     /**
24455      * @cfg {Number} width The width of the box that displays the selected element
24456      */ 
24457     width:          300,
24458
24459     
24460     
24461     /**
24462      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24463      */
24464     name : false,
24465     /**
24466      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24467      */
24468     hiddenName : false,
24469     
24470     
24471     // private the array of items that are displayed..
24472     items  : false,
24473     // private - the hidden field el.
24474     hiddenEl : false,
24475     // private - the filed el..
24476     el : false,
24477     
24478     //validateValue : function() { return true; }, // all values are ok!
24479     //onAddClick: function() { },
24480     
24481     onRender : function(ct, position) 
24482     {
24483         
24484         // create the standard hidden element
24485         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24486         
24487         
24488         // give fake names to child combo;
24489         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24490         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24491         
24492         this.combo = Roo.factory(this.combo, Roo.form);
24493         this.combo.onRender(ct, position);
24494         this.combo.initEvents();
24495         
24496         // assigned so form know we need to do this..
24497         this.store          = this.combo.store;
24498         this.valueField     = this.combo.valueField;
24499         this.displayField   = this.combo.displayField ;
24500         
24501         
24502         this.combo.wrap.addClass('x-cbarray-grp');
24503         
24504         var cbwrap = this.combo.wrap.createChild(
24505             {tag: 'div', cls: 'x-cbarray-cb'},
24506             this.combo.el.dom
24507         );
24508         
24509              
24510         this.hiddenEl = this.combo.wrap.createChild({
24511             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24512         });
24513         this.el = this.combo.wrap.createChild({
24514             tag: 'input',  type:'hidden' , name: this.name, value : ''
24515         });
24516          //   this.el.dom.removeAttribute("name");
24517         
24518         
24519         this.outerWrap = this.combo.wrap;
24520         this.wrap = cbwrap;
24521         
24522         this.outerWrap.setWidth(this.width);
24523         this.outerWrap.dom.removeChild(this.el.dom);
24524         
24525         this.wrap.dom.appendChild(this.el.dom);
24526         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24527         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24528         
24529         this.combo.trigger.setStyle('position','relative');
24530         this.combo.trigger.setStyle('left', '0px');
24531         this.combo.trigger.setStyle('top', '2px');
24532         
24533         this.combo.el.setStyle('vertical-align', 'text-bottom');
24534         
24535         //this.trigger.setStyle('vertical-align', 'top');
24536         
24537         // this should use the code from combo really... on('add' ....)
24538         if (this.adder) {
24539             
24540         
24541             this.adder = this.outerWrap.createChild(
24542                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24543             var _t = this;
24544             this.adder.on('click', function(e) {
24545                 _t.fireEvent('adderclick', this, e);
24546             }, _t);
24547         }
24548         //var _t = this;
24549         //this.adder.on('click', this.onAddClick, _t);
24550         
24551         
24552         this.combo.on('select', function(cb, rec, ix) {
24553             this.addItem(rec.data);
24554             
24555             cb.setValue('');
24556             cb.el.dom.value = '';
24557             //cb.lastData = rec.data;
24558             // add to list
24559             
24560         }, this);
24561         
24562         
24563     },
24564     
24565     
24566     getName: function()
24567     {
24568         // returns hidden if it's set..
24569         if (!this.rendered) {return ''};
24570         return  this.hiddenName ? this.hiddenName : this.name;
24571         
24572     },
24573     
24574     
24575     onResize: function(w, h){
24576         
24577         return;
24578         // not sure if this is needed..
24579         //this.combo.onResize(w,h);
24580         
24581         if(typeof w != 'number'){
24582             // we do not handle it!?!?
24583             return;
24584         }
24585         var tw = this.combo.trigger.getWidth();
24586         tw += this.addicon ? this.addicon.getWidth() : 0;
24587         tw += this.editicon ? this.editicon.getWidth() : 0;
24588         var x = w - tw;
24589         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24590             
24591         this.combo.trigger.setStyle('left', '0px');
24592         
24593         if(this.list && this.listWidth === undefined){
24594             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24595             this.list.setWidth(lw);
24596             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24597         }
24598         
24599     
24600         
24601     },
24602     
24603     addItem: function(rec)
24604     {
24605         var valueField = this.combo.valueField;
24606         var displayField = this.combo.displayField;
24607         if (this.items.indexOfKey(rec[valueField]) > -1) {
24608             //console.log("GOT " + rec.data.id);
24609             return;
24610         }
24611         
24612         var x = new Roo.form.ComboBoxArray.Item({
24613             //id : rec[this.idField],
24614             data : rec,
24615             displayField : displayField ,
24616             tipField : displayField ,
24617             cb : this
24618         });
24619         // use the 
24620         this.items.add(rec[valueField],x);
24621         // add it before the element..
24622         this.updateHiddenEl();
24623         x.render(this.outerWrap, this.wrap.dom);
24624         // add the image handler..
24625     },
24626     
24627     updateHiddenEl : function()
24628     {
24629         this.validate();
24630         if (!this.hiddenEl) {
24631             return;
24632         }
24633         var ar = [];
24634         var idField = this.combo.valueField;
24635         
24636         this.items.each(function(f) {
24637             ar.push(f.data[idField]);
24638            
24639         });
24640         this.hiddenEl.dom.value = ar.join(',');
24641         this.validate();
24642     },
24643     
24644     reset : function()
24645     {
24646         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24647         this.items.each(function(f) {
24648            f.remove(); 
24649         });
24650         this.el.dom.value = '';
24651         if (this.hiddenEl) {
24652             this.hiddenEl.dom.value = '';
24653         }
24654         
24655     },
24656     getValue: function()
24657     {
24658         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24659     },
24660     setValue: function(v) // not a valid action - must use addItems..
24661     {
24662          
24663         this.reset();
24664         
24665         
24666         
24667         if (this.store.isLocal && (typeof(v) == 'string')) {
24668             // then we can use the store to find the values..
24669             // comma seperated at present.. this needs to allow JSON based encoding..
24670             this.hiddenEl.value  = v;
24671             var v_ar = [];
24672             Roo.each(v.split(','), function(k) {
24673                 Roo.log("CHECK " + this.valueField + ',' + k);
24674                 var li = this.store.query(this.valueField, k);
24675                 if (!li.length) {
24676                     return;
24677                 }
24678                 add = {};
24679                 add[this.valueField] = k;
24680                 add[this.displayField] = li.item(0).data[this.displayField];
24681                 
24682                 this.addItem(add);
24683             }, this) 
24684              
24685         }
24686         if (typeof(v) == 'object') {
24687             // then let's assume it's an array of objects..
24688             Roo.each(v, function(l) {
24689                 this.addItem(l);
24690             }, this);
24691              
24692         }
24693         
24694         
24695     },
24696     setFromData: function(v)
24697     {
24698         // this recieves an object, if setValues is called.
24699         this.reset();
24700         this.el.dom.value = v[this.displayField];
24701         this.hiddenEl.dom.value = v[this.valueField];
24702         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24703             return;
24704         }
24705         var kv = v[this.valueField];
24706         var dv = v[this.displayField];
24707         kv = typeof(kv) != 'string' ? '' : kv;
24708         dv = typeof(dv) != 'string' ? '' : dv;
24709         
24710         
24711         var keys = kv.split(',');
24712         var display = dv.split(',');
24713         for (var i = 0 ; i < keys.length; i++) {
24714             
24715             add = {};
24716             add[this.valueField] = keys[i];
24717             add[this.displayField] = display[i];
24718             this.addItem(add);
24719         }
24720       
24721         
24722     },
24723     
24724     
24725     validateValue : function(value){
24726         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24727         
24728     }
24729     
24730 });
24731
24732
24733
24734 /**
24735  * @class Roo.form.ComboBoxArray.Item
24736  * @extends Roo.BoxComponent
24737  * A selected item in the list
24738  *  Fred [x]  Brian [x]  [Pick another |v]
24739  * 
24740  * @constructor
24741  * Create a new item.
24742  * @param {Object} config Configuration options
24743  */
24744  
24745 Roo.form.ComboBoxArray.Item = function(config) {
24746     config.id = Roo.id();
24747     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24748 }
24749
24750 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24751     data : {},
24752     cb: false,
24753     displayField : false,
24754     tipField : false,
24755     
24756     
24757     defaultAutoCreate : {
24758         tag: 'div',
24759         cls: 'x-cbarray-item',
24760         cn : [ 
24761             { tag: 'div' },
24762             {
24763                 tag: 'img',
24764                 width:16,
24765                 height : 16,
24766                 src : Roo.BLANK_IMAGE_URL ,
24767                 align: 'center'
24768             }
24769         ]
24770         
24771     },
24772     
24773  
24774     onRender : function(ct, position)
24775     {
24776         Roo.form.Field.superclass.onRender.call(this, ct, position);
24777         
24778         if(!this.el){
24779             var cfg = this.getAutoCreate();
24780             this.el = ct.createChild(cfg, position);
24781         }
24782         
24783         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24784         
24785         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24786             this.cb.renderer(this.data) :
24787             String.format('{0}',this.data[this.displayField]);
24788         
24789             
24790         this.el.child('div').dom.setAttribute('qtip',
24791                         String.format('{0}',this.data[this.tipField])
24792         );
24793         
24794         this.el.child('img').on('click', this.remove, this);
24795         
24796     },
24797    
24798     remove : function()
24799     {
24800         
24801         this.cb.items.remove(this);
24802         this.el.child('img').un('click', this.remove, this);
24803         this.el.remove();
24804         this.cb.updateHiddenEl();
24805     }
24806     
24807     
24808 });/*
24809  * Based on:
24810  * Ext JS Library 1.1.1
24811  * Copyright(c) 2006-2007, Ext JS, LLC.
24812  *
24813  * Originally Released Under LGPL - original licence link has changed is not relivant.
24814  *
24815  * Fork - LGPL
24816  * <script type="text/javascript">
24817  */
24818 /**
24819  * @class Roo.form.Checkbox
24820  * @extends Roo.form.Field
24821  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24822  * @constructor
24823  * Creates a new Checkbox
24824  * @param {Object} config Configuration options
24825  */
24826 Roo.form.Checkbox = function(config){
24827     Roo.form.Checkbox.superclass.constructor.call(this, config);
24828     this.addEvents({
24829         /**
24830          * @event check
24831          * Fires when the checkbox is checked or unchecked.
24832              * @param {Roo.form.Checkbox} this This checkbox
24833              * @param {Boolean} checked The new checked value
24834              */
24835         check : true
24836     });
24837 };
24838
24839 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24840     /**
24841      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24842      */
24843     focusClass : undefined,
24844     /**
24845      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24846      */
24847     fieldClass: "x-form-field",
24848     /**
24849      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24850      */
24851     checked: false,
24852     /**
24853      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24854      * {tag: "input", type: "checkbox", autocomplete: "off"})
24855      */
24856     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24857     /**
24858      * @cfg {String} boxLabel The text that appears beside the checkbox
24859      */
24860     boxLabel : "",
24861     /**
24862      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24863      */  
24864     inputValue : '1',
24865     /**
24866      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24867      */
24868      valueOff: '0', // value when not checked..
24869
24870     actionMode : 'viewEl', 
24871     //
24872     // private
24873     itemCls : 'x-menu-check-item x-form-item',
24874     groupClass : 'x-menu-group-item',
24875     inputType : 'hidden',
24876     
24877     
24878     inSetChecked: false, // check that we are not calling self...
24879     
24880     inputElement: false, // real input element?
24881     basedOn: false, // ????
24882     
24883     isFormField: true, // not sure where this is needed!!!!
24884
24885     onResize : function(){
24886         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24887         if(!this.boxLabel){
24888             this.el.alignTo(this.wrap, 'c-c');
24889         }
24890     },
24891
24892     initEvents : function(){
24893         Roo.form.Checkbox.superclass.initEvents.call(this);
24894         this.el.on("click", this.onClick,  this);
24895         this.el.on("change", this.onClick,  this);
24896     },
24897
24898
24899     getResizeEl : function(){
24900         return this.wrap;
24901     },
24902
24903     getPositionEl : function(){
24904         return this.wrap;
24905     },
24906
24907     // private
24908     onRender : function(ct, position){
24909         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24910         /*
24911         if(this.inputValue !== undefined){
24912             this.el.dom.value = this.inputValue;
24913         }
24914         */
24915         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24916         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24917         var viewEl = this.wrap.createChild({ 
24918             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24919         this.viewEl = viewEl;   
24920         this.wrap.on('click', this.onClick,  this); 
24921         
24922         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24923         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24924         
24925         
24926         
24927         if(this.boxLabel){
24928             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24929         //    viewEl.on('click', this.onClick,  this); 
24930         }
24931         //if(this.checked){
24932             this.setChecked(this.checked);
24933         //}else{
24934             //this.checked = this.el.dom;
24935         //}
24936
24937     },
24938
24939     // private
24940     initValue : Roo.emptyFn,
24941
24942     /**
24943      * Returns the checked state of the checkbox.
24944      * @return {Boolean} True if checked, else false
24945      */
24946     getValue : function(){
24947         if(this.el){
24948             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24949         }
24950         return this.valueOff;
24951         
24952     },
24953
24954         // private
24955     onClick : function(){ 
24956         this.setChecked(!this.checked);
24957
24958         //if(this.el.dom.checked != this.checked){
24959         //    this.setValue(this.el.dom.checked);
24960        // }
24961     },
24962
24963     /**
24964      * Sets the checked state of the checkbox.
24965      * On is always based on a string comparison between inputValue and the param.
24966      * @param {Boolean/String} value - the value to set 
24967      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24968      */
24969     setValue : function(v,suppressEvent){
24970         
24971         
24972         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24973         //if(this.el && this.el.dom){
24974         //    this.el.dom.checked = this.checked;
24975         //    this.el.dom.defaultChecked = this.checked;
24976         //}
24977         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24978         //this.fireEvent("check", this, this.checked);
24979     },
24980     // private..
24981     setChecked : function(state,suppressEvent)
24982     {
24983         if (this.inSetChecked) {
24984             this.checked = state;
24985             return;
24986         }
24987         
24988     
24989         if(this.wrap){
24990             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24991         }
24992         this.checked = state;
24993         if(suppressEvent !== true){
24994             this.fireEvent('check', this, state);
24995         }
24996         this.inSetChecked = true;
24997         this.el.dom.value = state ? this.inputValue : this.valueOff;
24998         this.inSetChecked = false;
24999         
25000     },
25001     // handle setting of hidden value by some other method!!?!?
25002     setFromHidden: function()
25003     {
25004         if(!this.el){
25005             return;
25006         }
25007         //console.log("SET FROM HIDDEN");
25008         //alert('setFrom hidden');
25009         this.setValue(this.el.dom.value);
25010     },
25011     
25012     onDestroy : function()
25013     {
25014         if(this.viewEl){
25015             Roo.get(this.viewEl).remove();
25016         }
25017          
25018         Roo.form.Checkbox.superclass.onDestroy.call(this);
25019     }
25020
25021 });/*
25022  * Based on:
25023  * Ext JS Library 1.1.1
25024  * Copyright(c) 2006-2007, Ext JS, LLC.
25025  *
25026  * Originally Released Under LGPL - original licence link has changed is not relivant.
25027  *
25028  * Fork - LGPL
25029  * <script type="text/javascript">
25030  */
25031  
25032 /**
25033  * @class Roo.form.Radio
25034  * @extends Roo.form.Checkbox
25035  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25036  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25037  * @constructor
25038  * Creates a new Radio
25039  * @param {Object} config Configuration options
25040  */
25041 Roo.form.Radio = function(){
25042     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25043 };
25044 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25045     inputType: 'radio',
25046
25047     /**
25048      * If this radio is part of a group, it will return the selected value
25049      * @return {String}
25050      */
25051     getGroupValue : function(){
25052         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25053     }
25054 });//<script type="text/javascript">
25055
25056 /*
25057  * Ext JS Library 1.1.1
25058  * Copyright(c) 2006-2007, Ext JS, LLC.
25059  * licensing@extjs.com
25060  * 
25061  * http://www.extjs.com/license
25062  */
25063  
25064  /*
25065   * 
25066   * Known bugs:
25067   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25068   * - IE ? - no idea how much works there.
25069   * 
25070   * 
25071   * 
25072   */
25073  
25074
25075 /**
25076  * @class Ext.form.HtmlEditor
25077  * @extends Ext.form.Field
25078  * Provides a lightweight HTML Editor component.
25079  *
25080  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25081  * 
25082  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25083  * supported by this editor.</b><br/><br/>
25084  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25085  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25086  */
25087 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25088       /**
25089      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25090      */
25091     toolbars : false,
25092     /**
25093      * @cfg {String} createLinkText The default text for the create link prompt
25094      */
25095     createLinkText : 'Please enter the URL for the link:',
25096     /**
25097      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25098      */
25099     defaultLinkValue : 'http:/'+'/',
25100    
25101      /**
25102      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25103      *                        Roo.resizable.
25104      */
25105     resizable : false,
25106      /**
25107      * @cfg {Number} height (in pixels)
25108      */   
25109     height: 300,
25110    /**
25111      * @cfg {Number} width (in pixels)
25112      */   
25113     width: 500,
25114     
25115     /**
25116      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25117      * 
25118      */
25119     stylesheets: false,
25120     
25121     // id of frame..
25122     frameId: false,
25123     
25124     // private properties
25125     validationEvent : false,
25126     deferHeight: true,
25127     initialized : false,
25128     activated : false,
25129     sourceEditMode : false,
25130     onFocus : Roo.emptyFn,
25131     iframePad:3,
25132     hideMode:'offsets',
25133     
25134     defaultAutoCreate : { // modified by initCompnoent..
25135         tag: "textarea",
25136         style:"width:500px;height:300px;",
25137         autocomplete: "off"
25138     },
25139
25140     // private
25141     initComponent : function(){
25142         this.addEvents({
25143             /**
25144              * @event initialize
25145              * Fires when the editor is fully initialized (including the iframe)
25146              * @param {HtmlEditor} this
25147              */
25148             initialize: true,
25149             /**
25150              * @event activate
25151              * Fires when the editor is first receives the focus. Any insertion must wait
25152              * until after this event.
25153              * @param {HtmlEditor} this
25154              */
25155             activate: true,
25156              /**
25157              * @event beforesync
25158              * Fires before the textarea is updated with content from the editor iframe. Return false
25159              * to cancel the sync.
25160              * @param {HtmlEditor} this
25161              * @param {String} html
25162              */
25163             beforesync: true,
25164              /**
25165              * @event beforepush
25166              * Fires before the iframe editor is updated with content from the textarea. Return false
25167              * to cancel the push.
25168              * @param {HtmlEditor} this
25169              * @param {String} html
25170              */
25171             beforepush: true,
25172              /**
25173              * @event sync
25174              * Fires when the textarea is updated with content from the editor iframe.
25175              * @param {HtmlEditor} this
25176              * @param {String} html
25177              */
25178             sync: true,
25179              /**
25180              * @event push
25181              * Fires when the iframe editor is updated with content from the textarea.
25182              * @param {HtmlEditor} this
25183              * @param {String} html
25184              */
25185             push: true,
25186              /**
25187              * @event editmodechange
25188              * Fires when the editor switches edit modes
25189              * @param {HtmlEditor} this
25190              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25191              */
25192             editmodechange: true,
25193             /**
25194              * @event editorevent
25195              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25196              * @param {HtmlEditor} this
25197              */
25198             editorevent: true
25199         });
25200         this.defaultAutoCreate =  {
25201             tag: "textarea",
25202             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25203             autocomplete: "off"
25204         };
25205     },
25206
25207     /**
25208      * Protected method that will not generally be called directly. It
25209      * is called when the editor creates its toolbar. Override this method if you need to
25210      * add custom toolbar buttons.
25211      * @param {HtmlEditor} editor
25212      */
25213     createToolbar : function(editor){
25214         if (!editor.toolbars || !editor.toolbars.length) {
25215             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25216         }
25217         
25218         for (var i =0 ; i < editor.toolbars.length;i++) {
25219             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
25220             editor.toolbars[i].init(editor);
25221         }
25222          
25223         
25224     },
25225
25226     /**
25227      * Protected method that will not generally be called directly. It
25228      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25229      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25230      */
25231     getDocMarkup : function(){
25232         // body styles..
25233         var st = '';
25234         if (this.stylesheets === false) {
25235             
25236             Roo.get(document.head).select('style').each(function(node) {
25237                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25238             });
25239             
25240             Roo.get(document.head).select('link').each(function(node) { 
25241                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25242             });
25243             
25244         } else if (!this.stylesheets.length) {
25245                 // simple..
25246                 st = '<style type="text/css">' +
25247                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25248                    '</style>';
25249         } else {
25250             Roo.each(this.stylesheets, function(s) {
25251                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25252             });
25253             
25254         }
25255         
25256         st +=  '<style type="text/css">' +
25257             'IMG { cursor: pointer } ' +
25258         '</style>';
25259
25260         
25261         return '<html><head>' + st  +
25262             //<style type="text/css">' +
25263             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25264             //'</style>' +
25265             ' </head><body class="roo-htmleditor-body"></body></html>';
25266     },
25267
25268     // private
25269     onRender : function(ct, position)
25270     {
25271         var _t = this;
25272         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25273         this.el.dom.style.border = '0 none';
25274         this.el.dom.setAttribute('tabIndex', -1);
25275         this.el.addClass('x-hidden');
25276         if(Roo.isIE){ // fix IE 1px bogus margin
25277             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25278         }
25279         this.wrap = this.el.wrap({
25280             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25281         });
25282         
25283         if (this.resizable) {
25284             this.resizeEl = new Roo.Resizable(this.wrap, {
25285                 pinned : true,
25286                 wrap: true,
25287                 dynamic : true,
25288                 minHeight : this.height,
25289                 height: this.height,
25290                 handles : this.resizable,
25291                 width: this.width,
25292                 listeners : {
25293                     resize : function(r, w, h) {
25294                         _t.onResize(w,h); // -something
25295                     }
25296                 }
25297             });
25298             
25299         }
25300
25301         this.frameId = Roo.id();
25302         
25303         this.createToolbar(this);
25304         
25305       
25306         
25307         var iframe = this.wrap.createChild({
25308             tag: 'iframe',
25309             id: this.frameId,
25310             name: this.frameId,
25311             frameBorder : 'no',
25312             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25313         }, this.el
25314         );
25315         
25316        // console.log(iframe);
25317         //this.wrap.dom.appendChild(iframe);
25318
25319         this.iframe = iframe.dom;
25320
25321          this.assignDocWin();
25322         
25323         this.doc.designMode = 'on';
25324        
25325         this.doc.open();
25326         this.doc.write(this.getDocMarkup());
25327         this.doc.close();
25328
25329         
25330         var task = { // must defer to wait for browser to be ready
25331             run : function(){
25332                 //console.log("run task?" + this.doc.readyState);
25333                 this.assignDocWin();
25334                 if(this.doc.body || this.doc.readyState == 'complete'){
25335                     try {
25336                         this.doc.designMode="on";
25337                     } catch (e) {
25338                         return;
25339                     }
25340                     Roo.TaskMgr.stop(task);
25341                     this.initEditor.defer(10, this);
25342                 }
25343             },
25344             interval : 10,
25345             duration:10000,
25346             scope: this
25347         };
25348         Roo.TaskMgr.start(task);
25349
25350         if(!this.width){
25351             this.setSize(this.wrap.getSize());
25352         }
25353         if (this.resizeEl) {
25354             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25355             // should trigger onReize..
25356         }
25357     },
25358
25359     // private
25360     onResize : function(w, h)
25361     {
25362         //Roo.log('resize: ' +w + ',' + h );
25363         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25364         if(this.el && this.iframe){
25365             if(typeof w == 'number'){
25366                 var aw = w - this.wrap.getFrameWidth('lr');
25367                 this.el.setWidth(this.adjustWidth('textarea', aw));
25368                 this.iframe.style.width = aw + 'px';
25369             }
25370             if(typeof h == 'number'){
25371                 var tbh = 0;
25372                 for (var i =0; i < this.toolbars.length;i++) {
25373                     // fixme - ask toolbars for heights?
25374                     tbh += this.toolbars[i].tb.el.getHeight();
25375                     if (this.toolbars[i].footer) {
25376                         tbh += this.toolbars[i].footer.el.getHeight();
25377                     }
25378                 }
25379                 
25380                 
25381                 
25382                 
25383                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25384                 ah -= 5; // knock a few pixes off for look..
25385                 this.el.setHeight(this.adjustWidth('textarea', ah));
25386                 this.iframe.style.height = ah + 'px';
25387                 if(this.doc){
25388                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25389                 }
25390             }
25391         }
25392     },
25393
25394     /**
25395      * Toggles the editor between standard and source edit mode.
25396      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25397      */
25398     toggleSourceEdit : function(sourceEditMode){
25399         
25400         this.sourceEditMode = sourceEditMode === true;
25401         
25402         if(this.sourceEditMode){
25403           
25404             this.syncValue();
25405             this.iframe.className = 'x-hidden';
25406             this.el.removeClass('x-hidden');
25407             this.el.dom.removeAttribute('tabIndex');
25408             this.el.focus();
25409         }else{
25410              
25411             this.pushValue();
25412             this.iframe.className = '';
25413             this.el.addClass('x-hidden');
25414             this.el.dom.setAttribute('tabIndex', -1);
25415             this.deferFocus();
25416         }
25417         this.setSize(this.wrap.getSize());
25418         this.fireEvent('editmodechange', this, this.sourceEditMode);
25419     },
25420
25421     // private used internally
25422     createLink : function(){
25423         var url = prompt(this.createLinkText, this.defaultLinkValue);
25424         if(url && url != 'http:/'+'/'){
25425             this.relayCmd('createlink', url);
25426         }
25427     },
25428
25429     // private (for BoxComponent)
25430     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25431
25432     // private (for BoxComponent)
25433     getResizeEl : function(){
25434         return this.wrap;
25435     },
25436
25437     // private (for BoxComponent)
25438     getPositionEl : function(){
25439         return this.wrap;
25440     },
25441
25442     // private
25443     initEvents : function(){
25444         this.originalValue = this.getValue();
25445     },
25446
25447     /**
25448      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25449      * @method
25450      */
25451     markInvalid : Roo.emptyFn,
25452     /**
25453      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25454      * @method
25455      */
25456     clearInvalid : Roo.emptyFn,
25457
25458     setValue : function(v){
25459         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25460         this.pushValue();
25461     },
25462
25463     /**
25464      * Protected method that will not generally be called directly. If you need/want
25465      * custom HTML cleanup, this is the method you should override.
25466      * @param {String} html The HTML to be cleaned
25467      * return {String} The cleaned HTML
25468      */
25469     cleanHtml : function(html){
25470         html = String(html);
25471         if(html.length > 5){
25472             if(Roo.isSafari){ // strip safari nonsense
25473                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25474             }
25475         }
25476         if(html == '&nbsp;'){
25477             html = '';
25478         }
25479         return html;
25480     },
25481
25482     /**
25483      * Protected method that will not generally be called directly. Syncs the contents
25484      * of the editor iframe with the textarea.
25485      */
25486     syncValue : function(){
25487         if(this.initialized){
25488             var bd = (this.doc.body || this.doc.documentElement);
25489             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25490             var html = bd.innerHTML;
25491             if(Roo.isSafari){
25492                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25493                 var m = bs.match(/text-align:(.*?);/i);
25494                 if(m && m[1]){
25495                     html = '<div style="'+m[0]+'">' + html + '</div>';
25496                 }
25497             }
25498             html = this.cleanHtml(html);
25499             // fix up the special chars..
25500             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25501                 return "&#"+b.charCodeAt()+";" 
25502             });
25503             if(this.fireEvent('beforesync', this, html) !== false){
25504                 this.el.dom.value = html;
25505                 this.fireEvent('sync', this, html);
25506             }
25507         }
25508     },
25509
25510     /**
25511      * Protected method that will not generally be called directly. Pushes the value of the textarea
25512      * into the iframe editor.
25513      */
25514     pushValue : function(){
25515         if(this.initialized){
25516             var v = this.el.dom.value;
25517             if(v.length < 1){
25518                 v = '&#160;';
25519             }
25520             
25521             if(this.fireEvent('beforepush', this, v) !== false){
25522                 var d = (this.doc.body || this.doc.documentElement);
25523                 d.innerHTML = v;
25524                 this.cleanUpPaste();
25525                 this.el.dom.value = d.innerHTML;
25526                 this.fireEvent('push', this, v);
25527             }
25528         }
25529     },
25530
25531     // private
25532     deferFocus : function(){
25533         this.focus.defer(10, this);
25534     },
25535
25536     // doc'ed in Field
25537     focus : function(){
25538         if(this.win && !this.sourceEditMode){
25539             this.win.focus();
25540         }else{
25541             this.el.focus();
25542         }
25543     },
25544     
25545     assignDocWin: function()
25546     {
25547         var iframe = this.iframe;
25548         
25549          if(Roo.isIE){
25550             this.doc = iframe.contentWindow.document;
25551             this.win = iframe.contentWindow;
25552         } else {
25553             if (!Roo.get(this.frameId)) {
25554                 return;
25555             }
25556             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25557             this.win = Roo.get(this.frameId).dom.contentWindow;
25558         }
25559     },
25560     
25561     // private
25562     initEditor : function(){
25563         //console.log("INIT EDITOR");
25564         this.assignDocWin();
25565         
25566         
25567         
25568         this.doc.designMode="on";
25569         this.doc.open();
25570         this.doc.write(this.getDocMarkup());
25571         this.doc.close();
25572         
25573         var dbody = (this.doc.body || this.doc.documentElement);
25574         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25575         // this copies styles from the containing element into thsi one..
25576         // not sure why we need all of this..
25577         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25578         ss['background-attachment'] = 'fixed'; // w3c
25579         dbody.bgProperties = 'fixed'; // ie
25580         Roo.DomHelper.applyStyles(dbody, ss);
25581         Roo.EventManager.on(this.doc, {
25582             //'mousedown': this.onEditorEvent,
25583             'mouseup': this.onEditorEvent,
25584             'dblclick': this.onEditorEvent,
25585             'click': this.onEditorEvent,
25586             'keyup': this.onEditorEvent,
25587             buffer:100,
25588             scope: this
25589         });
25590         if(Roo.isGecko){
25591             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25592         }
25593         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25594             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25595         }
25596         this.initialized = true;
25597
25598         this.fireEvent('initialize', this);
25599         this.pushValue();
25600     },
25601
25602     // private
25603     onDestroy : function(){
25604         
25605         
25606         
25607         if(this.rendered){
25608             
25609             for (var i =0; i < this.toolbars.length;i++) {
25610                 // fixme - ask toolbars for heights?
25611                 this.toolbars[i].onDestroy();
25612             }
25613             
25614             this.wrap.dom.innerHTML = '';
25615             this.wrap.remove();
25616         }
25617     },
25618
25619     // private
25620     onFirstFocus : function(){
25621         
25622         this.assignDocWin();
25623         
25624         
25625         this.activated = true;
25626         for (var i =0; i < this.toolbars.length;i++) {
25627             this.toolbars[i].onFirstFocus();
25628         }
25629        
25630         if(Roo.isGecko){ // prevent silly gecko errors
25631             this.win.focus();
25632             var s = this.win.getSelection();
25633             if(!s.focusNode || s.focusNode.nodeType != 3){
25634                 var r = s.getRangeAt(0);
25635                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25636                 r.collapse(true);
25637                 this.deferFocus();
25638             }
25639             try{
25640                 this.execCmd('useCSS', true);
25641                 this.execCmd('styleWithCSS', false);
25642             }catch(e){}
25643         }
25644         this.fireEvent('activate', this);
25645     },
25646
25647     // private
25648     adjustFont: function(btn){
25649         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25650         //if(Roo.isSafari){ // safari
25651         //    adjust *= 2;
25652        // }
25653         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25654         if(Roo.isSafari){ // safari
25655             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25656             v =  (v < 10) ? 10 : v;
25657             v =  (v > 48) ? 48 : v;
25658             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25659             
25660         }
25661         
25662         
25663         v = Math.max(1, v+adjust);
25664         
25665         this.execCmd('FontSize', v  );
25666     },
25667
25668     onEditorEvent : function(e){
25669         this.fireEvent('editorevent', this, e);
25670       //  this.updateToolbar();
25671         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25672     },
25673
25674     insertTag : function(tg)
25675     {
25676         // could be a bit smarter... -> wrap the current selected tRoo..
25677         
25678         this.execCmd("formatblock",   tg);
25679         
25680     },
25681     
25682     insertText : function(txt)
25683     {
25684         
25685         
25686         range = this.createRange();
25687         range.deleteContents();
25688                //alert(Sender.getAttribute('label'));
25689                
25690         range.insertNode(this.doc.createTextNode(txt));
25691     } ,
25692     
25693     // private
25694     relayBtnCmd : function(btn){
25695         this.relayCmd(btn.cmd);
25696     },
25697
25698     /**
25699      * Executes a Midas editor command on the editor document and performs necessary focus and
25700      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25701      * @param {String} cmd The Midas command
25702      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25703      */
25704     relayCmd : function(cmd, value){
25705         this.win.focus();
25706         this.execCmd(cmd, value);
25707         this.fireEvent('editorevent', this);
25708         //this.updateToolbar();
25709         this.deferFocus();
25710     },
25711
25712     /**
25713      * Executes a Midas editor command directly on the editor document.
25714      * For visual commands, you should use {@link #relayCmd} instead.
25715      * <b>This should only be called after the editor is initialized.</b>
25716      * @param {String} cmd The Midas command
25717      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25718      */
25719     execCmd : function(cmd, value){
25720         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25721         this.syncValue();
25722     },
25723  
25724  
25725    
25726     /**
25727      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25728      * to insert tRoo.
25729      * @param {String} text | dom node.. 
25730      */
25731     insertAtCursor : function(text)
25732     {
25733         
25734         
25735         
25736         if(!this.activated){
25737             return;
25738         }
25739         /*
25740         if(Roo.isIE){
25741             this.win.focus();
25742             var r = this.doc.selection.createRange();
25743             if(r){
25744                 r.collapse(true);
25745                 r.pasteHTML(text);
25746                 this.syncValue();
25747                 this.deferFocus();
25748             
25749             }
25750             return;
25751         }
25752         */
25753         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25754             this.win.focus();
25755             
25756             
25757             // from jquery ui (MIT licenced)
25758             var range, node;
25759             var win = this.win;
25760             
25761             if (win.getSelection && win.getSelection().getRangeAt) {
25762                 range = win.getSelection().getRangeAt(0);
25763                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25764                 range.insertNode(node);
25765             } else if (win.document.selection && win.document.selection.createRange) {
25766                 // no firefox support
25767                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25768                 win.document.selection.createRange().pasteHTML(txt);
25769             } else {
25770                 // no firefox support
25771                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25772                 this.execCmd('InsertHTML', txt);
25773             } 
25774             
25775             this.syncValue();
25776             
25777             this.deferFocus();
25778         }
25779     },
25780  // private
25781     mozKeyPress : function(e){
25782         if(e.ctrlKey){
25783             var c = e.getCharCode(), cmd;
25784           
25785             if(c > 0){
25786                 c = String.fromCharCode(c).toLowerCase();
25787                 switch(c){
25788                     case 'b':
25789                         cmd = 'bold';
25790                         break;
25791                     case 'i':
25792                         cmd = 'italic';
25793                         break;
25794                     
25795                     case 'u':
25796                         cmd = 'underline';
25797                         break;
25798                     
25799                     case 'v':
25800                         this.cleanUpPaste.defer(100, this);
25801                         return;
25802                         
25803                 }
25804                 if(cmd){
25805                     this.win.focus();
25806                     this.execCmd(cmd);
25807                     this.deferFocus();
25808                     e.preventDefault();
25809                 }
25810                 
25811             }
25812         }
25813     },
25814
25815     // private
25816     fixKeys : function(){ // load time branching for fastest keydown performance
25817         if(Roo.isIE){
25818             return function(e){
25819                 var k = e.getKey(), r;
25820                 if(k == e.TAB){
25821                     e.stopEvent();
25822                     r = this.doc.selection.createRange();
25823                     if(r){
25824                         r.collapse(true);
25825                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25826                         this.deferFocus();
25827                     }
25828                     return;
25829                 }
25830                 
25831                 if(k == e.ENTER){
25832                     r = this.doc.selection.createRange();
25833                     if(r){
25834                         var target = r.parentElement();
25835                         if(!target || target.tagName.toLowerCase() != 'li'){
25836                             e.stopEvent();
25837                             r.pasteHTML('<br />');
25838                             r.collapse(false);
25839                             r.select();
25840                         }
25841                     }
25842                 }
25843                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25844                     this.cleanUpPaste.defer(100, this);
25845                     return;
25846                 }
25847                 
25848                 
25849             };
25850         }else if(Roo.isOpera){
25851             return function(e){
25852                 var k = e.getKey();
25853                 if(k == e.TAB){
25854                     e.stopEvent();
25855                     this.win.focus();
25856                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25857                     this.deferFocus();
25858                 }
25859                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25860                     this.cleanUpPaste.defer(100, this);
25861                     return;
25862                 }
25863                 
25864             };
25865         }else if(Roo.isSafari){
25866             return function(e){
25867                 var k = e.getKey();
25868                 
25869                 if(k == e.TAB){
25870                     e.stopEvent();
25871                     this.execCmd('InsertText','\t');
25872                     this.deferFocus();
25873                     return;
25874                 }
25875                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25876                     this.cleanUpPaste.defer(100, this);
25877                     return;
25878                 }
25879                 
25880              };
25881         }
25882     }(),
25883     
25884     getAllAncestors: function()
25885     {
25886         var p = this.getSelectedNode();
25887         var a = [];
25888         if (!p) {
25889             a.push(p); // push blank onto stack..
25890             p = this.getParentElement();
25891         }
25892         
25893         
25894         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25895             a.push(p);
25896             p = p.parentNode;
25897         }
25898         a.push(this.doc.body);
25899         return a;
25900     },
25901     lastSel : false,
25902     lastSelNode : false,
25903     
25904     
25905     getSelection : function() 
25906     {
25907         this.assignDocWin();
25908         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25909     },
25910     
25911     getSelectedNode: function() 
25912     {
25913         // this may only work on Gecko!!!
25914         
25915         // should we cache this!!!!
25916         
25917         
25918         
25919          
25920         var range = this.createRange(this.getSelection()).cloneRange();
25921         
25922         if (Roo.isIE) {
25923             var parent = range.parentElement();
25924             while (true) {
25925                 var testRange = range.duplicate();
25926                 testRange.moveToElementText(parent);
25927                 if (testRange.inRange(range)) {
25928                     break;
25929                 }
25930                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25931                     break;
25932                 }
25933                 parent = parent.parentElement;
25934             }
25935             return parent;
25936         }
25937         
25938         // is ancestor a text element.
25939         var ac =  range.commonAncestorContainer;
25940         if (ac.nodeType == 3) {
25941             ac = ac.parentNode;
25942         }
25943         
25944         var ar = ac.childNodes;
25945          
25946         var nodes = [];
25947         var other_nodes = [];
25948         var has_other_nodes = false;
25949         for (var i=0;i<ar.length;i++) {
25950             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25951                 continue;
25952             }
25953             // fullly contained node.
25954             
25955             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25956                 nodes.push(ar[i]);
25957                 continue;
25958             }
25959             
25960             // probably selected..
25961             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25962                 other_nodes.push(ar[i]);
25963                 continue;
25964             }
25965             // outer..
25966             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25967                 continue;
25968             }
25969             
25970             
25971             has_other_nodes = true;
25972         }
25973         if (!nodes.length && other_nodes.length) {
25974             nodes= other_nodes;
25975         }
25976         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25977             return false;
25978         }
25979         
25980         return nodes[0];
25981     },
25982     createRange: function(sel)
25983     {
25984         // this has strange effects when using with 
25985         // top toolbar - not sure if it's a great idea.
25986         //this.editor.contentWindow.focus();
25987         if (typeof sel != "undefined") {
25988             try {
25989                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25990             } catch(e) {
25991                 return this.doc.createRange();
25992             }
25993         } else {
25994             return this.doc.createRange();
25995         }
25996     },
25997     getParentElement: function()
25998     {
25999         
26000         this.assignDocWin();
26001         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26002         
26003         var range = this.createRange(sel);
26004          
26005         try {
26006             var p = range.commonAncestorContainer;
26007             while (p.nodeType == 3) { // text node
26008                 p = p.parentNode;
26009             }
26010             return p;
26011         } catch (e) {
26012             return null;
26013         }
26014     
26015     },
26016     /***
26017      *
26018      * Range intersection.. the hard stuff...
26019      *  '-1' = before
26020      *  '0' = hits..
26021      *  '1' = after.
26022      *         [ -- selected range --- ]
26023      *   [fail]                        [fail]
26024      *
26025      *    basically..
26026      *      if end is before start or  hits it. fail.
26027      *      if start is after end or hits it fail.
26028      *
26029      *   if either hits (but other is outside. - then it's not 
26030      *   
26031      *    
26032      **/
26033     
26034     
26035     // @see http://www.thismuchiknow.co.uk/?p=64.
26036     rangeIntersectsNode : function(range, node)
26037     {
26038         var nodeRange = node.ownerDocument.createRange();
26039         try {
26040             nodeRange.selectNode(node);
26041         } catch (e) {
26042             nodeRange.selectNodeContents(node);
26043         }
26044     
26045         var rangeStartRange = range.cloneRange();
26046         rangeStartRange.collapse(true);
26047     
26048         var rangeEndRange = range.cloneRange();
26049         rangeEndRange.collapse(false);
26050     
26051         var nodeStartRange = nodeRange.cloneRange();
26052         nodeStartRange.collapse(true);
26053     
26054         var nodeEndRange = nodeRange.cloneRange();
26055         nodeEndRange.collapse(false);
26056     
26057         return rangeStartRange.compareBoundaryPoints(
26058                  Range.START_TO_START, nodeEndRange) == -1 &&
26059                rangeEndRange.compareBoundaryPoints(
26060                  Range.START_TO_START, nodeStartRange) == 1;
26061         
26062          
26063     },
26064     rangeCompareNode : function(range, node)
26065     {
26066         var nodeRange = node.ownerDocument.createRange();
26067         try {
26068             nodeRange.selectNode(node);
26069         } catch (e) {
26070             nodeRange.selectNodeContents(node);
26071         }
26072         
26073         
26074         range.collapse(true);
26075     
26076         nodeRange.collapse(true);
26077      
26078         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26079         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26080          
26081         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26082         
26083         var nodeIsBefore   =  ss == 1;
26084         var nodeIsAfter    = ee == -1;
26085         
26086         if (nodeIsBefore && nodeIsAfter)
26087             return 0; // outer
26088         if (!nodeIsBefore && nodeIsAfter)
26089             return 1; //right trailed.
26090         
26091         if (nodeIsBefore && !nodeIsAfter)
26092             return 2;  // left trailed.
26093         // fully contined.
26094         return 3;
26095     },
26096
26097     // private? - in a new class?
26098     cleanUpPaste :  function()
26099     {
26100         // cleans up the whole document..
26101          Roo.log('cleanuppaste');
26102         this.cleanUpChildren(this.doc.body);
26103         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26104         if (clean != this.doc.body.innerHTML) {
26105             this.doc.body.innerHTML = clean;
26106         }
26107         
26108     },
26109     
26110     cleanWordChars : function(input) {
26111         var he = Roo.form.HtmlEditor;
26112     
26113         var output = input;
26114         Roo.each(he.swapCodes, function(sw) { 
26115         
26116             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26117             output = output.replace(swapper, sw[1]);
26118         });
26119         return output;
26120     },
26121     
26122     
26123     cleanUpChildren : function (n)
26124     {
26125         if (!n.childNodes.length) {
26126             return;
26127         }
26128         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26129            this.cleanUpChild(n.childNodes[i]);
26130         }
26131     },
26132     
26133     
26134         
26135     
26136     cleanUpChild : function (node)
26137     {
26138         //console.log(node);
26139         if (node.nodeName == "#text") {
26140             // clean up silly Windows -- stuff?
26141             return; 
26142         }
26143         if (node.nodeName == "#comment") {
26144             node.parentNode.removeChild(node);
26145             // clean up silly Windows -- stuff?
26146             return; 
26147         }
26148         
26149         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26150             // remove node.
26151             node.parentNode.removeChild(node);
26152             return;
26153             
26154         }
26155         
26156         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26157         
26158         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26159         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26160         
26161         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26162         //    remove_keep_children = true;
26163         //}
26164         
26165         if (remove_keep_children) {
26166             this.cleanUpChildren(node);
26167             // inserts everything just before this node...
26168             while (node.childNodes.length) {
26169                 var cn = node.childNodes[0];
26170                 node.removeChild(cn);
26171                 node.parentNode.insertBefore(cn, node);
26172             }
26173             node.parentNode.removeChild(node);
26174             return;
26175         }
26176         
26177         if (!node.attributes || !node.attributes.length) {
26178             this.cleanUpChildren(node);
26179             return;
26180         }
26181         
26182         function cleanAttr(n,v)
26183         {
26184             
26185             if (v.match(/^\./) || v.match(/^\//)) {
26186                 return;
26187             }
26188             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26189                 return;
26190             }
26191             if (v.match(/^#/)) {
26192                 return;
26193             }
26194             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
26195             node.removeAttribute(n);
26196             
26197         }
26198         
26199         function cleanStyle(n,v)
26200         {
26201             if (v.match(/expression/)) { //XSS?? should we even bother..
26202                 node.removeAttribute(n);
26203                 return;
26204             }
26205             
26206             
26207             var parts = v.split(/;/);
26208             Roo.each(parts, function(p) {
26209                 p = p.replace(/\s+/g,'');
26210                 if (!p.length) {
26211                     return true;
26212                 }
26213                 var l = p.split(':').shift().replace(/\s+/g,'');
26214                 
26215                 // only allow 'c whitelisted system attributes'
26216                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
26217                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
26218                     node.removeAttribute(n);
26219                     return false;
26220                 }
26221                 return true;
26222             });
26223             
26224             
26225         }
26226         
26227         
26228         for (var i = node.attributes.length-1; i > -1 ; i--) {
26229             var a = node.attributes[i];
26230             //console.log(a);
26231             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26232                 node.removeAttribute(a.name);
26233                 continue;
26234             }
26235             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26236                 cleanAttr(a.name,a.value); // fixme..
26237                 continue;
26238             }
26239             if (a.name == 'style') {
26240                 cleanStyle(a.name,a.value);
26241                 continue;
26242             }
26243             /// clean up MS crap..
26244             // tecnically this should be a list of valid class'es..
26245             
26246             
26247             if (a.name == 'class') {
26248                 if (a.value.match(/^Mso/)) {
26249                     node.className = '';
26250                 }
26251                 
26252                 if (a.value.match(/body/)) {
26253                     node.className = '';
26254                 }
26255                 continue;
26256             }
26257             
26258             // style cleanup!?
26259             // class cleanup?
26260             
26261         }
26262         
26263         
26264         this.cleanUpChildren(node);
26265         
26266         
26267     }
26268     
26269     
26270     // hide stuff that is not compatible
26271     /**
26272      * @event blur
26273      * @hide
26274      */
26275     /**
26276      * @event change
26277      * @hide
26278      */
26279     /**
26280      * @event focus
26281      * @hide
26282      */
26283     /**
26284      * @event specialkey
26285      * @hide
26286      */
26287     /**
26288      * @cfg {String} fieldClass @hide
26289      */
26290     /**
26291      * @cfg {String} focusClass @hide
26292      */
26293     /**
26294      * @cfg {String} autoCreate @hide
26295      */
26296     /**
26297      * @cfg {String} inputType @hide
26298      */
26299     /**
26300      * @cfg {String} invalidClass @hide
26301      */
26302     /**
26303      * @cfg {String} invalidText @hide
26304      */
26305     /**
26306      * @cfg {String} msgFx @hide
26307      */
26308     /**
26309      * @cfg {String} validateOnBlur @hide
26310      */
26311 });
26312
26313 Roo.form.HtmlEditor.white = [
26314         'area', 'br', 'img', 'input', 'hr', 'wbr',
26315         
26316        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26317        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26318        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26319        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26320        'table',   'ul',         'xmp', 
26321        
26322        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26323       'thead',   'tr', 
26324      
26325       'dir', 'menu', 'ol', 'ul', 'dl',
26326        
26327       'embed',  'object'
26328 ];
26329
26330
26331 Roo.form.HtmlEditor.black = [
26332     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26333         'applet', // 
26334         'base',   'basefont', 'bgsound', 'blink',  'body', 
26335         'frame',  'frameset', 'head',    'html',   'ilayer', 
26336         'iframe', 'layer',  'link',     'meta',    'object',   
26337         'script', 'style' ,'title',  'xml' // clean later..
26338 ];
26339 Roo.form.HtmlEditor.clean = [
26340     'script', 'style', 'title', 'xml'
26341 ];
26342 Roo.form.HtmlEditor.remove = [
26343     'font'
26344 ];
26345 // attributes..
26346
26347 Roo.form.HtmlEditor.ablack = [
26348     'on'
26349 ];
26350     
26351 Roo.form.HtmlEditor.aclean = [ 
26352     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26353 ];
26354
26355 // protocols..
26356 Roo.form.HtmlEditor.pwhite= [
26357         'http',  'https',  'mailto'
26358 ];
26359
26360 // white listed style attributes.
26361 Roo.form.HtmlEditor.cwhite= [
26362         'text-align',
26363         'font-size'
26364 ];
26365
26366
26367 Roo.form.HtmlEditor.swapCodes   =[ 
26368     [    8211, "--" ], 
26369     [    8212, "--" ], 
26370     [    8216,  "'" ],  
26371     [    8217, "'" ],  
26372     [    8220, '"' ],  
26373     [    8221, '"' ],  
26374     [    8226, "*" ],  
26375     [    8230, "..." ]
26376 ]; 
26377
26378     // <script type="text/javascript">
26379 /*
26380  * Based on
26381  * Ext JS Library 1.1.1
26382  * Copyright(c) 2006-2007, Ext JS, LLC.
26383  *  
26384  
26385  */
26386
26387 /**
26388  * @class Roo.form.HtmlEditorToolbar1
26389  * Basic Toolbar
26390  * 
26391  * Usage:
26392  *
26393  new Roo.form.HtmlEditor({
26394     ....
26395     toolbars : [
26396         new Roo.form.HtmlEditorToolbar1({
26397             disable : { fonts: 1 , format: 1, ..., ... , ...],
26398             btns : [ .... ]
26399         })
26400     }
26401      
26402  * 
26403  * @cfg {Object} disable List of elements to disable..
26404  * @cfg {Array} btns List of additional buttons.
26405  * 
26406  * 
26407  * NEEDS Extra CSS? 
26408  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26409  */
26410  
26411 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26412 {
26413     
26414     Roo.apply(this, config);
26415     
26416     // default disabled, based on 'good practice'..
26417     this.disable = this.disable || {};
26418     Roo.applyIf(this.disable, {
26419         fontSize : true,
26420         colors : true,
26421         specialElements : true
26422     });
26423     
26424     
26425     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26426     // dont call parent... till later.
26427 }
26428
26429 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26430     
26431     tb: false,
26432     
26433     rendered: false,
26434     
26435     editor : false,
26436     /**
26437      * @cfg {Object} disable  List of toolbar elements to disable
26438          
26439      */
26440     disable : false,
26441       /**
26442      * @cfg {Array} fontFamilies An array of available font families
26443      */
26444     fontFamilies : [
26445         'Arial',
26446         'Courier New',
26447         'Tahoma',
26448         'Times New Roman',
26449         'Verdana'
26450     ],
26451     
26452     specialChars : [
26453            "&#169;",
26454           "&#174;",     
26455           "&#8482;",    
26456           "&#163;" ,    
26457          // "&#8212;",    
26458           "&#8230;",    
26459           "&#247;" ,    
26460         //  "&#225;" ,     ?? a acute?
26461            "&#8364;"    , //Euro
26462        //   "&#8220;"    ,
26463         //  "&#8221;"    ,
26464         //  "&#8226;"    ,
26465           "&#176;"  //   , // degrees
26466
26467          // "&#233;"     , // e ecute
26468          // "&#250;"     , // u ecute?
26469     ],
26470     
26471     specialElements : [
26472         {
26473             text: "Insert Table",
26474             xtype: 'MenuItem',
26475             xns : Roo.Menu,
26476             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26477                 
26478         },
26479         {    
26480             text: "Insert Image",
26481             xtype: 'MenuItem',
26482             xns : Roo.Menu,
26483             ihtml : '<img src="about:blank"/>'
26484             
26485         }
26486         
26487          
26488     ],
26489     
26490     
26491     inputElements : [ 
26492             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26493             "input:submit", "input:button", "select", "textarea", "label" ],
26494     formats : [
26495         ["p"] ,  
26496         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26497         ["pre"],[ "code"], 
26498         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26499     ],
26500      /**
26501      * @cfg {String} defaultFont default font to use.
26502      */
26503     defaultFont: 'tahoma',
26504    
26505     fontSelect : false,
26506     
26507     
26508     formatCombo : false,
26509     
26510     init : function(editor)
26511     {
26512         this.editor = editor;
26513         
26514         
26515         var fid = editor.frameId;
26516         var etb = this;
26517         function btn(id, toggle, handler){
26518             var xid = fid + '-'+ id ;
26519             return {
26520                 id : xid,
26521                 cmd : id,
26522                 cls : 'x-btn-icon x-edit-'+id,
26523                 enableToggle:toggle !== false,
26524                 scope: editor, // was editor...
26525                 handler:handler||editor.relayBtnCmd,
26526                 clickEvent:'mousedown',
26527                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26528                 tabIndex:-1
26529             };
26530         }
26531         
26532         
26533         
26534         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26535         this.tb = tb;
26536          // stop form submits
26537         tb.el.on('click', function(e){
26538             e.preventDefault(); // what does this do?
26539         });
26540
26541         if(!this.disable.font && !Roo.isSafari){
26542             /* why no safari for fonts
26543             editor.fontSelect = tb.el.createChild({
26544                 tag:'select',
26545                 tabIndex: -1,
26546                 cls:'x-font-select',
26547                 html: editor.createFontOptions()
26548             });
26549             editor.fontSelect.on('change', function(){
26550                 var font = editor.fontSelect.dom.value;
26551                 editor.relayCmd('fontname', font);
26552                 editor.deferFocus();
26553             }, editor);
26554             tb.add(
26555                 editor.fontSelect.dom,
26556                 '-'
26557             );
26558             */
26559         };
26560         if(!this.disable.formats){
26561             this.formatCombo = new Roo.form.ComboBox({
26562                 store: new Roo.data.SimpleStore({
26563                     id : 'tag',
26564                     fields: ['tag'],
26565                     data : this.formats // from states.js
26566                 }),
26567                 blockFocus : true,
26568                 //autoCreate : {tag: "div",  size: "20"},
26569                 displayField:'tag',
26570                 typeAhead: false,
26571                 mode: 'local',
26572                 editable : false,
26573                 triggerAction: 'all',
26574                 emptyText:'Add tag',
26575                 selectOnFocus:true,
26576                 width:135,
26577                 listeners : {
26578                     'select': function(c, r, i) {
26579                         editor.insertTag(r.get('tag'));
26580                         editor.focus();
26581                     }
26582                 }
26583
26584             });
26585             tb.addField(this.formatCombo);
26586             
26587         }
26588         
26589         if(!this.disable.format){
26590             tb.add(
26591                 btn('bold'),
26592                 btn('italic'),
26593                 btn('underline')
26594             );
26595         };
26596         if(!this.disable.fontSize){
26597             tb.add(
26598                 '-',
26599                 
26600                 
26601                 btn('increasefontsize', false, editor.adjustFont),
26602                 btn('decreasefontsize', false, editor.adjustFont)
26603             );
26604         };
26605         
26606         
26607         if(!this.disable.colors){
26608             tb.add(
26609                 '-', {
26610                     id:editor.frameId +'-forecolor',
26611                     cls:'x-btn-icon x-edit-forecolor',
26612                     clickEvent:'mousedown',
26613                     tooltip: this.buttonTips['forecolor'] || undefined,
26614                     tabIndex:-1,
26615                     menu : new Roo.menu.ColorMenu({
26616                         allowReselect: true,
26617                         focus: Roo.emptyFn,
26618                         value:'000000',
26619                         plain:true,
26620                         selectHandler: function(cp, color){
26621                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26622                             editor.deferFocus();
26623                         },
26624                         scope: editor,
26625                         clickEvent:'mousedown'
26626                     })
26627                 }, {
26628                     id:editor.frameId +'backcolor',
26629                     cls:'x-btn-icon x-edit-backcolor',
26630                     clickEvent:'mousedown',
26631                     tooltip: this.buttonTips['backcolor'] || undefined,
26632                     tabIndex:-1,
26633                     menu : new Roo.menu.ColorMenu({
26634                         focus: Roo.emptyFn,
26635                         value:'FFFFFF',
26636                         plain:true,
26637                         allowReselect: true,
26638                         selectHandler: function(cp, color){
26639                             if(Roo.isGecko){
26640                                 editor.execCmd('useCSS', false);
26641                                 editor.execCmd('hilitecolor', color);
26642                                 editor.execCmd('useCSS', true);
26643                                 editor.deferFocus();
26644                             }else{
26645                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26646                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26647                                 editor.deferFocus();
26648                             }
26649                         },
26650                         scope:editor,
26651                         clickEvent:'mousedown'
26652                     })
26653                 }
26654             );
26655         };
26656         // now add all the items...
26657         
26658
26659         if(!this.disable.alignments){
26660             tb.add(
26661                 '-',
26662                 btn('justifyleft'),
26663                 btn('justifycenter'),
26664                 btn('justifyright')
26665             );
26666         };
26667
26668         //if(!Roo.isSafari){
26669             if(!this.disable.links){
26670                 tb.add(
26671                     '-',
26672                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26673                 );
26674             };
26675
26676             if(!this.disable.lists){
26677                 tb.add(
26678                     '-',
26679                     btn('insertorderedlist'),
26680                     btn('insertunorderedlist')
26681                 );
26682             }
26683             if(!this.disable.sourceEdit){
26684                 tb.add(
26685                     '-',
26686                     btn('sourceedit', true, function(btn){
26687                         this.toggleSourceEdit(btn.pressed);
26688                     })
26689                 );
26690             }
26691         //}
26692         
26693         var smenu = { };
26694         // special menu.. - needs to be tidied up..
26695         if (!this.disable.special) {
26696             smenu = {
26697                 text: "&#169;",
26698                 cls: 'x-edit-none',
26699                 
26700                 menu : {
26701                     items : []
26702                 }
26703             };
26704             for (var i =0; i < this.specialChars.length; i++) {
26705                 smenu.menu.items.push({
26706                     
26707                     html: this.specialChars[i],
26708                     handler: function(a,b) {
26709                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26710                         //editor.insertAtCursor(a.html);
26711                         
26712                     },
26713                     tabIndex:-1
26714                 });
26715             }
26716             
26717             
26718             tb.add(smenu);
26719             
26720             
26721         }
26722          
26723         if (!this.disable.specialElements) {
26724             var semenu = {
26725                 text: "Other;",
26726                 cls: 'x-edit-none',
26727                 menu : {
26728                     items : []
26729                 }
26730             };
26731             for (var i =0; i < this.specialElements.length; i++) {
26732                 semenu.menu.items.push(
26733                     Roo.apply({ 
26734                         handler: function(a,b) {
26735                             editor.insertAtCursor(this.ihtml);
26736                         }
26737                     }, this.specialElements[i])
26738                 );
26739                     
26740             }
26741             
26742             tb.add(semenu);
26743             
26744             
26745         }
26746          
26747         
26748         if (this.btns) {
26749             for(var i =0; i< this.btns.length;i++) {
26750                 var b = Roo.factory(this.btns[i],Roo.form);
26751                 b.cls =  'x-edit-none';
26752                 b.scope = editor;
26753                 tb.add(b);
26754             }
26755         
26756         }
26757         
26758         
26759         
26760         // disable everything...
26761         
26762         this.tb.items.each(function(item){
26763            if(item.id != editor.frameId+ '-sourceedit'){
26764                 item.disable();
26765             }
26766         });
26767         this.rendered = true;
26768         
26769         // the all the btns;
26770         editor.on('editorevent', this.updateToolbar, this);
26771         // other toolbars need to implement this..
26772         //editor.on('editmodechange', this.updateToolbar, this);
26773     },
26774     
26775     
26776     
26777     /**
26778      * Protected method that will not generally be called directly. It triggers
26779      * a toolbar update by reading the markup state of the current selection in the editor.
26780      */
26781     updateToolbar: function(){
26782
26783         if(!this.editor.activated){
26784             this.editor.onFirstFocus();
26785             return;
26786         }
26787
26788         var btns = this.tb.items.map, 
26789             doc = this.editor.doc,
26790             frameId = this.editor.frameId;
26791
26792         if(!this.disable.font && !Roo.isSafari){
26793             /*
26794             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26795             if(name != this.fontSelect.dom.value){
26796                 this.fontSelect.dom.value = name;
26797             }
26798             */
26799         }
26800         if(!this.disable.format){
26801             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26802             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26803             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26804         }
26805         if(!this.disable.alignments){
26806             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26807             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26808             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26809         }
26810         if(!Roo.isSafari && !this.disable.lists){
26811             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26812             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26813         }
26814         
26815         var ans = this.editor.getAllAncestors();
26816         if (this.formatCombo) {
26817             
26818             
26819             var store = this.formatCombo.store;
26820             this.formatCombo.setValue("");
26821             for (var i =0; i < ans.length;i++) {
26822                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26823                     // select it..
26824                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26825                     break;
26826                 }
26827             }
26828         }
26829         
26830         
26831         
26832         // hides menus... - so this cant be on a menu...
26833         Roo.menu.MenuMgr.hideAll();
26834
26835         //this.editorsyncValue();
26836     },
26837    
26838     
26839     createFontOptions : function(){
26840         var buf = [], fs = this.fontFamilies, ff, lc;
26841         for(var i = 0, len = fs.length; i< len; i++){
26842             ff = fs[i];
26843             lc = ff.toLowerCase();
26844             buf.push(
26845                 '<option value="',lc,'" style="font-family:',ff,';"',
26846                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26847                     ff,
26848                 '</option>'
26849             );
26850         }
26851         return buf.join('');
26852     },
26853     
26854     toggleSourceEdit : function(sourceEditMode){
26855         if(sourceEditMode === undefined){
26856             sourceEditMode = !this.sourceEditMode;
26857         }
26858         this.sourceEditMode = sourceEditMode === true;
26859         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26860         // just toggle the button?
26861         if(btn.pressed !== this.editor.sourceEditMode){
26862             btn.toggle(this.editor.sourceEditMode);
26863             return;
26864         }
26865         
26866         if(this.sourceEditMode){
26867             this.tb.items.each(function(item){
26868                 if(item.cmd != 'sourceedit'){
26869                     item.disable();
26870                 }
26871             });
26872           
26873         }else{
26874             if(this.initialized){
26875                 this.tb.items.each(function(item){
26876                     item.enable();
26877                 });
26878             }
26879             
26880         }
26881         // tell the editor that it's been pressed..
26882         this.editor.toggleSourceEdit(sourceEditMode);
26883        
26884     },
26885      /**
26886      * Object collection of toolbar tooltips for the buttons in the editor. The key
26887      * is the command id associated with that button and the value is a valid QuickTips object.
26888      * For example:
26889 <pre><code>
26890 {
26891     bold : {
26892         title: 'Bold (Ctrl+B)',
26893         text: 'Make the selected text bold.',
26894         cls: 'x-html-editor-tip'
26895     },
26896     italic : {
26897         title: 'Italic (Ctrl+I)',
26898         text: 'Make the selected text italic.',
26899         cls: 'x-html-editor-tip'
26900     },
26901     ...
26902 </code></pre>
26903     * @type Object
26904      */
26905     buttonTips : {
26906         bold : {
26907             title: 'Bold (Ctrl+B)',
26908             text: 'Make the selected text bold.',
26909             cls: 'x-html-editor-tip'
26910         },
26911         italic : {
26912             title: 'Italic (Ctrl+I)',
26913             text: 'Make the selected text italic.',
26914             cls: 'x-html-editor-tip'
26915         },
26916         underline : {
26917             title: 'Underline (Ctrl+U)',
26918             text: 'Underline the selected text.',
26919             cls: 'x-html-editor-tip'
26920         },
26921         increasefontsize : {
26922             title: 'Grow Text',
26923             text: 'Increase the font size.',
26924             cls: 'x-html-editor-tip'
26925         },
26926         decreasefontsize : {
26927             title: 'Shrink Text',
26928             text: 'Decrease the font size.',
26929             cls: 'x-html-editor-tip'
26930         },
26931         backcolor : {
26932             title: 'Text Highlight Color',
26933             text: 'Change the background color of the selected text.',
26934             cls: 'x-html-editor-tip'
26935         },
26936         forecolor : {
26937             title: 'Font Color',
26938             text: 'Change the color of the selected text.',
26939             cls: 'x-html-editor-tip'
26940         },
26941         justifyleft : {
26942             title: 'Align Text Left',
26943             text: 'Align text to the left.',
26944             cls: 'x-html-editor-tip'
26945         },
26946         justifycenter : {
26947             title: 'Center Text',
26948             text: 'Center text in the editor.',
26949             cls: 'x-html-editor-tip'
26950         },
26951         justifyright : {
26952             title: 'Align Text Right',
26953             text: 'Align text to the right.',
26954             cls: 'x-html-editor-tip'
26955         },
26956         insertunorderedlist : {
26957             title: 'Bullet List',
26958             text: 'Start a bulleted list.',
26959             cls: 'x-html-editor-tip'
26960         },
26961         insertorderedlist : {
26962             title: 'Numbered List',
26963             text: 'Start a numbered list.',
26964             cls: 'x-html-editor-tip'
26965         },
26966         createlink : {
26967             title: 'Hyperlink',
26968             text: 'Make the selected text a hyperlink.',
26969             cls: 'x-html-editor-tip'
26970         },
26971         sourceedit : {
26972             title: 'Source Edit',
26973             text: 'Switch to source editing mode.',
26974             cls: 'x-html-editor-tip'
26975         }
26976     },
26977     // private
26978     onDestroy : function(){
26979         if(this.rendered){
26980             
26981             this.tb.items.each(function(item){
26982                 if(item.menu){
26983                     item.menu.removeAll();
26984                     if(item.menu.el){
26985                         item.menu.el.destroy();
26986                     }
26987                 }
26988                 item.destroy();
26989             });
26990              
26991         }
26992     },
26993     onFirstFocus: function() {
26994         this.tb.items.each(function(item){
26995            item.enable();
26996         });
26997     }
26998 });
26999
27000
27001
27002
27003 // <script type="text/javascript">
27004 /*
27005  * Based on
27006  * Ext JS Library 1.1.1
27007  * Copyright(c) 2006-2007, Ext JS, LLC.
27008  *  
27009  
27010  */
27011
27012  
27013 /**
27014  * @class Roo.form.HtmlEditor.ToolbarContext
27015  * Context Toolbar
27016  * 
27017  * Usage:
27018  *
27019  new Roo.form.HtmlEditor({
27020     ....
27021     toolbars : [
27022         { xtype: 'ToolbarStandard', styles : {} }
27023         { xtype: 'ToolbarContext', disable : {} }
27024     ]
27025 })
27026
27027      
27028  * 
27029  * @config : {Object} disable List of elements to disable.. (not done yet.)
27030  * @config : {Object} styles  Map of styles available.
27031  * 
27032  */
27033
27034 Roo.form.HtmlEditor.ToolbarContext = function(config)
27035 {
27036     
27037     Roo.apply(this, config);
27038     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27039     // dont call parent... till later.
27040     this.styles = this.styles || {};
27041 }
27042 Roo.form.HtmlEditor.ToolbarContext.types = {
27043     'IMG' : {
27044         width : {
27045             title: "Width",
27046             width: 40
27047         },
27048         height:  {
27049             title: "Height",
27050             width: 40
27051         },
27052         align: {
27053             title: "Align",
27054             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27055             width : 80
27056             
27057         },
27058         border: {
27059             title: "Border",
27060             width: 40
27061         },
27062         alt: {
27063             title: "Alt",
27064             width: 120
27065         },
27066         src : {
27067             title: "Src",
27068             width: 220
27069         }
27070         
27071     },
27072     'A' : {
27073         name : {
27074             title: "Name",
27075             width: 50
27076         },
27077         href:  {
27078             title: "Href",
27079             width: 220
27080         } // border?
27081         
27082     },
27083     'TABLE' : {
27084         rows : {
27085             title: "Rows",
27086             width: 20
27087         },
27088         cols : {
27089             title: "Cols",
27090             width: 20
27091         },
27092         width : {
27093             title: "Width",
27094             width: 40
27095         },
27096         height : {
27097             title: "Height",
27098             width: 40
27099         },
27100         border : {
27101             title: "Border",
27102             width: 20
27103         }
27104     },
27105     'TD' : {
27106         width : {
27107             title: "Width",
27108             width: 40
27109         },
27110         height : {
27111             title: "Height",
27112             width: 40
27113         },   
27114         align: {
27115             title: "Align",
27116             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27117             width: 80
27118         },
27119         valign: {
27120             title: "Valign",
27121             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27122             width: 80
27123         },
27124         colspan: {
27125             title: "Colspan",
27126             width: 20
27127             
27128         }
27129     },
27130     'INPUT' : {
27131         name : {
27132             title: "name",
27133             width: 120
27134         },
27135         value : {
27136             title: "Value",
27137             width: 120
27138         },
27139         width : {
27140             title: "Width",
27141             width: 40
27142         }
27143     },
27144     'LABEL' : {
27145         'for' : {
27146             title: "For",
27147             width: 120
27148         }
27149     },
27150     'TEXTAREA' : {
27151           name : {
27152             title: "name",
27153             width: 120
27154         },
27155         rows : {
27156             title: "Rows",
27157             width: 20
27158         },
27159         cols : {
27160             title: "Cols",
27161             width: 20
27162         }
27163     },
27164     'SELECT' : {
27165         name : {
27166             title: "name",
27167             width: 120
27168         },
27169         selectoptions : {
27170             title: "Options",
27171             width: 200
27172         }
27173     },
27174     
27175     // should we really allow this??
27176     // should this just be 
27177     'BODY' : {
27178         title : {
27179             title: "title",
27180             width: 200,
27181             disabled : true
27182         }
27183     },
27184     '*' : {
27185         // empty..
27186     }
27187 };
27188
27189
27190
27191 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27192     
27193     tb: false,
27194     
27195     rendered: false,
27196     
27197     editor : false,
27198     /**
27199      * @cfg {Object} disable  List of toolbar elements to disable
27200          
27201      */
27202     disable : false,
27203     /**
27204      * @cfg {Object} styles List of styles 
27205      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27206      *
27207      * These must be defined in the page, so they get rendered correctly..
27208      * .headline { }
27209      * TD.underline { }
27210      * 
27211      */
27212     styles : false,
27213     
27214     
27215     
27216     toolbars : false,
27217     
27218     init : function(editor)
27219     {
27220         this.editor = editor;
27221         
27222         
27223         var fid = editor.frameId;
27224         var etb = this;
27225         function btn(id, toggle, handler){
27226             var xid = fid + '-'+ id ;
27227             return {
27228                 id : xid,
27229                 cmd : id,
27230                 cls : 'x-btn-icon x-edit-'+id,
27231                 enableToggle:toggle !== false,
27232                 scope: editor, // was editor...
27233                 handler:handler||editor.relayBtnCmd,
27234                 clickEvent:'mousedown',
27235                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27236                 tabIndex:-1
27237             };
27238         }
27239         // create a new element.
27240         var wdiv = editor.wrap.createChild({
27241                 tag: 'div'
27242             }, editor.wrap.dom.firstChild.nextSibling, true);
27243         
27244         // can we do this more than once??
27245         
27246          // stop form submits
27247       
27248  
27249         // disable everything...
27250         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27251         this.toolbars = {};
27252            
27253         for (var i in  ty) {
27254           
27255             this.toolbars[i] = this.buildToolbar(ty[i],i);
27256         }
27257         this.tb = this.toolbars.BODY;
27258         this.tb.el.show();
27259         this.buildFooter();
27260         this.footer.show();
27261         editor.on('hide', function( ) { this.footer.hide() }, this);
27262         editor.on('show', function( ) { this.footer.show() }, this);
27263         
27264          
27265         this.rendered = true;
27266         
27267         // the all the btns;
27268         editor.on('editorevent', this.updateToolbar, this);
27269         // other toolbars need to implement this..
27270         //editor.on('editmodechange', this.updateToolbar, this);
27271     },
27272     
27273     
27274     
27275     /**
27276      * Protected method that will not generally be called directly. It triggers
27277      * a toolbar update by reading the markup state of the current selection in the editor.
27278      */
27279     updateToolbar: function(editor,ev,sel){
27280
27281         //Roo.log(ev);
27282         // capture mouse up - this is handy for selecting images..
27283         // perhaps should go somewhere else...
27284         if(!this.editor.activated){
27285              this.editor.onFirstFocus();
27286             return;
27287         }
27288         
27289         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27290         // selectNode - might want to handle IE?
27291         if (ev &&
27292             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27293             ev.target && ev.target.tagName == 'IMG') {
27294             // they have click on an image...
27295             // let's see if we can change the selection...
27296             sel = ev.target;
27297          
27298               var nodeRange = sel.ownerDocument.createRange();
27299             try {
27300                 nodeRange.selectNode(sel);
27301             } catch (e) {
27302                 nodeRange.selectNodeContents(sel);
27303             }
27304             //nodeRange.collapse(true);
27305             var s = editor.win.getSelection();
27306             s.removeAllRanges();
27307             s.addRange(nodeRange);
27308         }  
27309         
27310       
27311         var updateFooter = sel ? false : true;
27312         
27313         
27314         var ans = this.editor.getAllAncestors();
27315         
27316         // pick
27317         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27318         
27319         if (!sel) { 
27320             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27321             sel = sel ? sel : this.editor.doc.body;
27322             sel = sel.tagName.length ? sel : this.editor.doc.body;
27323             
27324         }
27325         // pick a menu that exists..
27326         var tn = sel.tagName.toUpperCase();
27327         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27328         
27329         tn = sel.tagName.toUpperCase();
27330         
27331         var lastSel = this.tb.selectedNode
27332         
27333         this.tb.selectedNode = sel;
27334         
27335         // if current menu does not match..
27336         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27337                 
27338             this.tb.el.hide();
27339             ///console.log("show: " + tn);
27340             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27341             this.tb.el.show();
27342             // update name
27343             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27344             
27345             
27346             // update attributes
27347             if (this.tb.fields) {
27348                 this.tb.fields.each(function(e) {
27349                    e.setValue(sel.getAttribute(e.attrname));
27350                 });
27351             }
27352             
27353             var hasStyles = false;
27354             for(var i in this.styles) {
27355                 hasStyles = true;
27356                 break;
27357             }
27358             
27359             // update styles
27360             if (hasStyles) { 
27361                 var st = this.tb.fields.item(0);
27362                 
27363                 st.store.removeAll();
27364                
27365                 
27366                 var cn = sel.className.split(/\s+/);
27367                 
27368                 var avs = [];
27369                 if (this.styles['*']) {
27370                     
27371                     Roo.each(this.styles['*'], function(v) {
27372                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27373                     });
27374                 }
27375                 if (this.styles[tn]) { 
27376                     Roo.each(this.styles[tn], function(v) {
27377                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27378                     });
27379                 }
27380                 
27381                 st.store.loadData(avs);
27382                 st.collapse();
27383                 st.setValue(cn);
27384             }
27385             // flag our selected Node.
27386             this.tb.selectedNode = sel;
27387            
27388            
27389             Roo.menu.MenuMgr.hideAll();
27390
27391         }
27392         
27393         if (!updateFooter) {
27394             //this.footDisp.dom.innerHTML = ''; 
27395             return;
27396         }
27397         // update the footer
27398         //
27399         var html = '';
27400         
27401         this.footerEls = ans.reverse();
27402         Roo.each(this.footerEls, function(a,i) {
27403             if (!a) { return; }
27404             html += html.length ? ' &gt; '  :  '';
27405             
27406             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27407             
27408         });
27409        
27410         // 
27411         var sz = this.footDisp.up('td').getSize();
27412         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27413         this.footDisp.dom.style.marginLeft = '5px';
27414         
27415         this.footDisp.dom.style.overflow = 'hidden';
27416         
27417         this.footDisp.dom.innerHTML = html;
27418             
27419         //this.editorsyncValue();
27420     },
27421      
27422     
27423    
27424        
27425     // private
27426     onDestroy : function(){
27427         if(this.rendered){
27428             
27429             this.tb.items.each(function(item){
27430                 if(item.menu){
27431                     item.menu.removeAll();
27432                     if(item.menu.el){
27433                         item.menu.el.destroy();
27434                     }
27435                 }
27436                 item.destroy();
27437             });
27438              
27439         }
27440     },
27441     onFirstFocus: function() {
27442         // need to do this for all the toolbars..
27443         this.tb.items.each(function(item){
27444            item.enable();
27445         });
27446     },
27447     buildToolbar: function(tlist, nm)
27448     {
27449         var editor = this.editor;
27450          // create a new element.
27451         var wdiv = editor.wrap.createChild({
27452                 tag: 'div'
27453             }, editor.wrap.dom.firstChild.nextSibling, true);
27454         
27455        
27456         var tb = new Roo.Toolbar(wdiv);
27457         // add the name..
27458         
27459         tb.add(nm+ ":&nbsp;");
27460         
27461         var styles = [];
27462         for(var i in this.styles) {
27463             styles.push(i);
27464         }
27465         
27466         // styles...
27467         if (styles && styles.length) {
27468             
27469             // this needs a multi-select checkbox...
27470             tb.addField( new Roo.form.ComboBox({
27471                 store: new Roo.data.SimpleStore({
27472                     id : 'val',
27473                     fields: ['val', 'selected'],
27474                     data : [] 
27475                 }),
27476                 name : '-roo-edit-className',
27477                 attrname : 'className',
27478                 displayField:'val',
27479                 typeAhead: false,
27480                 mode: 'local',
27481                 editable : false,
27482                 triggerAction: 'all',
27483                 emptyText:'Select Style',
27484                 selectOnFocus:true,
27485                 width: 130,
27486                 listeners : {
27487                     'select': function(c, r, i) {
27488                         // initial support only for on class per el..
27489                         tb.selectedNode.className =  r ? r.get('val') : '';
27490                         editor.syncValue();
27491                     }
27492                 }
27493     
27494             }));
27495         }
27496             
27497         
27498         
27499         for (var i in tlist) {
27500             
27501             var item = tlist[i];
27502             tb.add(item.title + ":&nbsp;");
27503             
27504             
27505             
27506             
27507             if (item.opts) {
27508                 // opts == pulldown..
27509                 tb.addField( new Roo.form.ComboBox({
27510                     store: new Roo.data.SimpleStore({
27511                         id : 'val',
27512                         fields: ['val'],
27513                         data : item.opts  
27514                     }),
27515                     name : '-roo-edit-' + i,
27516                     attrname : i,
27517                     displayField:'val',
27518                     typeAhead: false,
27519                     mode: 'local',
27520                     editable : false,
27521                     triggerAction: 'all',
27522                     emptyText:'Select',
27523                     selectOnFocus:true,
27524                     width: item.width ? item.width  : 130,
27525                     listeners : {
27526                         'select': function(c, r, i) {
27527                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27528                         }
27529                     }
27530
27531                 }));
27532                 continue;
27533                     
27534                  
27535                 
27536                 tb.addField( new Roo.form.TextField({
27537                     name: i,
27538                     width: 100,
27539                     //allowBlank:false,
27540                     value: ''
27541                 }));
27542                 continue;
27543             }
27544             tb.addField( new Roo.form.TextField({
27545                 name: '-roo-edit-' + i,
27546                 attrname : i,
27547                 
27548                 width: item.width,
27549                 //allowBlank:true,
27550                 value: '',
27551                 listeners: {
27552                     'change' : function(f, nv, ov) {
27553                         tb.selectedNode.setAttribute(f.attrname, nv);
27554                     }
27555                 }
27556             }));
27557              
27558         }
27559         tb.addFill();
27560         var _this = this;
27561         tb.addButton( {
27562             text: 'Remove Tag',
27563     
27564             listeners : {
27565                 click : function ()
27566                 {
27567                     // remove
27568                     // undo does not work.
27569                      
27570                     var sn = tb.selectedNode;
27571                     Roo.log(sn);
27572                     var pn = sn.parentNode;
27573                     
27574                     var stn =  sn.childNodes[0];
27575                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27576                     while (sn.childNodes.length) {
27577                         var node = sn.childNodes[0];
27578                         sn.removeChild(node);
27579                         Roo.log(node);
27580                         pn.insertBefore(node, sn);
27581                         
27582                     }
27583                     pn.removeChild(sn);
27584                     var range = editor.createRange();
27585         
27586                     range.setStart(stn,0);
27587                     range.setEnd(en,0); //????
27588                     //range.selectNode(sel);
27589                     
27590                     
27591                     var selection = editor.getSelection();
27592                     selection.removeAllRanges();
27593                     selection.addRange(range);
27594                     
27595                     
27596                     
27597                     //_this.updateToolbar(null, null, pn);
27598                     _this.updateToolbar(null, null, null);
27599                     this.footDisp.dom.innerHTML = ''; 
27600                 }
27601             }
27602             
27603                     
27604                 
27605             
27606         });
27607         
27608         
27609         tb.el.on('click', function(e){
27610             e.preventDefault(); // what does this do?
27611         });
27612         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27613         tb.el.hide();
27614         tb.name = nm;
27615         // dont need to disable them... as they will get hidden
27616         return tb;
27617          
27618         
27619     },
27620     buildFooter : function()
27621     {
27622         
27623         var fel = this.editor.wrap.createChild();
27624         this.footer = new Roo.Toolbar(fel);
27625         // toolbar has scrolly on left / right?
27626         var footDisp= new Roo.Toolbar.Fill();
27627         var _t = this;
27628         this.footer.add(
27629             {
27630                 text : '&lt;',
27631                 xtype: 'Button',
27632                 handler : function() {
27633                     _t.footDisp.scrollTo('left',0,true)
27634                 }
27635             }
27636         );
27637         this.footer.add( footDisp );
27638         this.footer.add( 
27639             {
27640                 text : '&gt;',
27641                 xtype: 'Button',
27642                 handler : function() {
27643                     // no animation..
27644                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27645                 }
27646             }
27647         );
27648         var fel = Roo.get(footDisp.el);
27649         fel.addClass('x-editor-context');
27650         this.footDispWrap = fel; 
27651         this.footDispWrap.overflow  = 'hidden';
27652         
27653         this.footDisp = fel.createChild();
27654         this.footDispWrap.on('click', this.onContextClick, this)
27655         
27656         
27657     },
27658     onContextClick : function (ev,dom)
27659     {
27660         ev.preventDefault();
27661         var  cn = dom.className;
27662         Roo.log(cn);
27663         if (!cn.match(/x-ed-loc-/)) {
27664             return;
27665         }
27666         var n = cn.split('-').pop();
27667         var ans = this.footerEls;
27668         var sel = ans[n];
27669         
27670          // pick
27671         var range = this.editor.createRange();
27672         
27673         range.selectNodeContents(sel);
27674         //range.selectNode(sel);
27675         
27676         
27677         var selection = this.editor.getSelection();
27678         selection.removeAllRanges();
27679         selection.addRange(range);
27680         
27681         
27682         
27683         this.updateToolbar(null, null, sel);
27684         
27685         
27686     }
27687     
27688     
27689     
27690     
27691     
27692 });
27693
27694
27695
27696
27697
27698 /*
27699  * Based on:
27700  * Ext JS Library 1.1.1
27701  * Copyright(c) 2006-2007, Ext JS, LLC.
27702  *
27703  * Originally Released Under LGPL - original licence link has changed is not relivant.
27704  *
27705  * Fork - LGPL
27706  * <script type="text/javascript">
27707  */
27708  
27709 /**
27710  * @class Roo.form.BasicForm
27711  * @extends Roo.util.Observable
27712  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27713  * @constructor
27714  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27715  * @param {Object} config Configuration options
27716  */
27717 Roo.form.BasicForm = function(el, config){
27718     this.allItems = [];
27719     this.childForms = [];
27720     Roo.apply(this, config);
27721     /*
27722      * The Roo.form.Field items in this form.
27723      * @type MixedCollection
27724      */
27725      
27726      
27727     this.items = new Roo.util.MixedCollection(false, function(o){
27728         return o.id || (o.id = Roo.id());
27729     });
27730     this.addEvents({
27731         /**
27732          * @event beforeaction
27733          * Fires before any action is performed. Return false to cancel the action.
27734          * @param {Form} this
27735          * @param {Action} action The action to be performed
27736          */
27737         beforeaction: true,
27738         /**
27739          * @event actionfailed
27740          * Fires when an action fails.
27741          * @param {Form} this
27742          * @param {Action} action The action that failed
27743          */
27744         actionfailed : true,
27745         /**
27746          * @event actioncomplete
27747          * Fires when an action is completed.
27748          * @param {Form} this
27749          * @param {Action} action The action that completed
27750          */
27751         actioncomplete : true
27752     });
27753     if(el){
27754         this.initEl(el);
27755     }
27756     Roo.form.BasicForm.superclass.constructor.call(this);
27757 };
27758
27759 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27760     /**
27761      * @cfg {String} method
27762      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27763      */
27764     /**
27765      * @cfg {DataReader} reader
27766      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27767      * This is optional as there is built-in support for processing JSON.
27768      */
27769     /**
27770      * @cfg {DataReader} errorReader
27771      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27772      * This is completely optional as there is built-in support for processing JSON.
27773      */
27774     /**
27775      * @cfg {String} url
27776      * The URL to use for form actions if one isn't supplied in the action options.
27777      */
27778     /**
27779      * @cfg {Boolean} fileUpload
27780      * Set to true if this form is a file upload.
27781      */
27782      
27783     /**
27784      * @cfg {Object} baseParams
27785      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27786      */
27787      /**
27788      
27789     /**
27790      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27791      */
27792     timeout: 30,
27793
27794     // private
27795     activeAction : null,
27796
27797     /**
27798      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27799      * or setValues() data instead of when the form was first created.
27800      */
27801     trackResetOnLoad : false,
27802     
27803     
27804     /**
27805      * childForms - used for multi-tab forms
27806      * @type {Array}
27807      */
27808     childForms : false,
27809     
27810     /**
27811      * allItems - full list of fields.
27812      * @type {Array}
27813      */
27814     allItems : false,
27815     
27816     /**
27817      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27818      * element by passing it or its id or mask the form itself by passing in true.
27819      * @type Mixed
27820      */
27821     waitMsgTarget : false,
27822
27823     // private
27824     initEl : function(el){
27825         this.el = Roo.get(el);
27826         this.id = this.el.id || Roo.id();
27827         this.el.on('submit', this.onSubmit, this);
27828         this.el.addClass('x-form');
27829     },
27830
27831     // private
27832     onSubmit : function(e){
27833         e.stopEvent();
27834     },
27835
27836     /**
27837      * Returns true if client-side validation on the form is successful.
27838      * @return Boolean
27839      */
27840     isValid : function(){
27841         var valid = true;
27842         this.items.each(function(f){
27843            if(!f.validate()){
27844                valid = false;
27845            }
27846         });
27847         return valid;
27848     },
27849
27850     /**
27851      * Returns true if any fields in this form have changed since their original load.
27852      * @return Boolean
27853      */
27854     isDirty : function(){
27855         var dirty = false;
27856         this.items.each(function(f){
27857            if(f.isDirty()){
27858                dirty = true;
27859                return false;
27860            }
27861         });
27862         return dirty;
27863     },
27864
27865     /**
27866      * Performs a predefined action (submit or load) or custom actions you define on this form.
27867      * @param {String} actionName The name of the action type
27868      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27869      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27870      * accept other config options):
27871      * <pre>
27872 Property          Type             Description
27873 ----------------  ---------------  ----------------------------------------------------------------------------------
27874 url               String           The url for the action (defaults to the form's url)
27875 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27876 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27877 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27878                                    validate the form on the client (defaults to false)
27879      * </pre>
27880      * @return {BasicForm} this
27881      */
27882     doAction : function(action, options){
27883         if(typeof action == 'string'){
27884             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27885         }
27886         if(this.fireEvent('beforeaction', this, action) !== false){
27887             this.beforeAction(action);
27888             action.run.defer(100, action);
27889         }
27890         return this;
27891     },
27892
27893     /**
27894      * Shortcut to do a submit action.
27895      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27896      * @return {BasicForm} this
27897      */
27898     submit : function(options){
27899         this.doAction('submit', options);
27900         return this;
27901     },
27902
27903     /**
27904      * Shortcut to do a load action.
27905      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27906      * @return {BasicForm} this
27907      */
27908     load : function(options){
27909         this.doAction('load', options);
27910         return this;
27911     },
27912
27913     /**
27914      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27915      * @param {Record} record The record to edit
27916      * @return {BasicForm} this
27917      */
27918     updateRecord : function(record){
27919         record.beginEdit();
27920         var fs = record.fields;
27921         fs.each(function(f){
27922             var field = this.findField(f.name);
27923             if(field){
27924                 record.set(f.name, field.getValue());
27925             }
27926         }, this);
27927         record.endEdit();
27928         return this;
27929     },
27930
27931     /**
27932      * Loads an Roo.data.Record into this form.
27933      * @param {Record} record The record to load
27934      * @return {BasicForm} this
27935      */
27936     loadRecord : function(record){
27937         this.setValues(record.data);
27938         return this;
27939     },
27940
27941     // private
27942     beforeAction : function(action){
27943         var o = action.options;
27944         
27945        
27946         if(this.waitMsgTarget === true){
27947             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27948         }else if(this.waitMsgTarget){
27949             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27950             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27951         }else {
27952             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27953         }
27954          
27955     },
27956
27957     // private
27958     afterAction : function(action, success){
27959         this.activeAction = null;
27960         var o = action.options;
27961         
27962         if(this.waitMsgTarget === true){
27963             this.el.unmask();
27964         }else if(this.waitMsgTarget){
27965             this.waitMsgTarget.unmask();
27966         }else{
27967             Roo.MessageBox.updateProgress(1);
27968             Roo.MessageBox.hide();
27969         }
27970          
27971         if(success){
27972             if(o.reset){
27973                 this.reset();
27974             }
27975             Roo.callback(o.success, o.scope, [this, action]);
27976             this.fireEvent('actioncomplete', this, action);
27977             
27978         }else{
27979             
27980             // failure condition..
27981             // we have a scenario where updates need confirming.
27982             // eg. if a locking scenario exists..
27983             // we look for { errors : { needs_confirm : true }} in the response.
27984             if (
27985                 (typeof(action.result) != 'undefined')  &&
27986                 (typeof(action.result.errors) != 'undefined')  &&
27987                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27988            ){
27989                 var _t = this;
27990                 Roo.MessageBox.confirm(
27991                     "Change requires confirmation",
27992                     action.result.errorMsg,
27993                     function(r) {
27994                         if (r != 'yes') {
27995                             return;
27996                         }
27997                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27998                     }
27999                     
28000                 );
28001                 
28002                 
28003                 
28004                 return;
28005             }
28006             
28007             Roo.callback(o.failure, o.scope, [this, action]);
28008             // show an error message if no failed handler is set..
28009             if (!this.hasListener('actionfailed')) {
28010                 Roo.MessageBox.alert("Error",
28011                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28012                         action.result.errorMsg :
28013                         "Saving Failed, please check your entries or try again"
28014                 );
28015             }
28016             
28017             this.fireEvent('actionfailed', this, action);
28018         }
28019         
28020     },
28021
28022     /**
28023      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28024      * @param {String} id The value to search for
28025      * @return Field
28026      */
28027     findField : function(id){
28028         var field = this.items.get(id);
28029         if(!field){
28030             this.items.each(function(f){
28031                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28032                     field = f;
28033                     return false;
28034                 }
28035             });
28036         }
28037         return field || null;
28038     },
28039
28040     /**
28041      * Add a secondary form to this one, 
28042      * Used to provide tabbed forms. One form is primary, with hidden values 
28043      * which mirror the elements from the other forms.
28044      * 
28045      * @param {Roo.form.Form} form to add.
28046      * 
28047      */
28048     addForm : function(form)
28049     {
28050        
28051         if (this.childForms.indexOf(form) > -1) {
28052             // already added..
28053             return;
28054         }
28055         this.childForms.push(form);
28056         var n = '';
28057         Roo.each(form.allItems, function (fe) {
28058             
28059             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28060             if (this.findField(n)) { // already added..
28061                 return;
28062             }
28063             var add = new Roo.form.Hidden({
28064                 name : n
28065             });
28066             add.render(this.el);
28067             
28068             this.add( add );
28069         }, this);
28070         
28071     },
28072     /**
28073      * Mark fields in this form invalid in bulk.
28074      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28075      * @return {BasicForm} this
28076      */
28077     markInvalid : function(errors){
28078         if(errors instanceof Array){
28079             for(var i = 0, len = errors.length; i < len; i++){
28080                 var fieldError = errors[i];
28081                 var f = this.findField(fieldError.id);
28082                 if(f){
28083                     f.markInvalid(fieldError.msg);
28084                 }
28085             }
28086         }else{
28087             var field, id;
28088             for(id in errors){
28089                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28090                     field.markInvalid(errors[id]);
28091                 }
28092             }
28093         }
28094         Roo.each(this.childForms || [], function (f) {
28095             f.markInvalid(errors);
28096         });
28097         
28098         return this;
28099     },
28100
28101     /**
28102      * Set values for fields in this form in bulk.
28103      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28104      * @return {BasicForm} this
28105      */
28106     setValues : function(values){
28107         if(values instanceof Array){ // array of objects
28108             for(var i = 0, len = values.length; i < len; i++){
28109                 var v = values[i];
28110                 var f = this.findField(v.id);
28111                 if(f){
28112                     f.setValue(v.value);
28113                     if(this.trackResetOnLoad){
28114                         f.originalValue = f.getValue();
28115                     }
28116                 }
28117             }
28118         }else{ // object hash
28119             var field, id;
28120             for(id in values){
28121                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28122                     
28123                     if (field.setFromData && 
28124                         field.valueField && 
28125                         field.displayField &&
28126                         // combos' with local stores can 
28127                         // be queried via setValue()
28128                         // to set their value..
28129                         (field.store && !field.store.isLocal)
28130                         ) {
28131                         // it's a combo
28132                         var sd = { };
28133                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28134                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28135                         field.setFromData(sd);
28136                         
28137                     } else {
28138                         field.setValue(values[id]);
28139                     }
28140                     
28141                     
28142                     if(this.trackResetOnLoad){
28143                         field.originalValue = field.getValue();
28144                     }
28145                 }
28146             }
28147         }
28148          
28149         Roo.each(this.childForms || [], function (f) {
28150             f.setValues(values);
28151         });
28152                 
28153         return this;
28154     },
28155
28156     /**
28157      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28158      * they are returned as an array.
28159      * @param {Boolean} asString
28160      * @return {Object}
28161      */
28162     getValues : function(asString){
28163         if (this.childForms) {
28164             // copy values from the child forms
28165             Roo.each(this.childForms, function (f) {
28166                 this.setValues(f.getValues());
28167             }, this);
28168         }
28169         
28170         
28171         
28172         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28173         if(asString === true){
28174             return fs;
28175         }
28176         return Roo.urlDecode(fs);
28177     },
28178     
28179     /**
28180      * Returns the fields in this form as an object with key/value pairs. 
28181      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28182      * @return {Object}
28183      */
28184     getFieldValues : function(with_hidden)
28185     {
28186         if (this.childForms) {
28187             // copy values from the child forms
28188             // should this call getFieldValues - probably not as we do not currently copy
28189             // hidden fields when we generate..
28190             Roo.each(this.childForms, function (f) {
28191                 this.setValues(f.getValues());
28192             }, this);
28193         }
28194         
28195         var ret = {};
28196         this.items.each(function(f){
28197             if (!f.getName()) {
28198                 return;
28199             }
28200             var v = f.getValue();
28201             // not sure if this supported any more..
28202             if ((typeof(v) == 'object') && f.getRawValue) {
28203                 v = f.getRawValue() ; // dates..
28204             }
28205             // combo boxes where name != hiddenName...
28206             if (f.name != f.getName()) {
28207                 ret[f.name] = f.getRawValue();
28208             }
28209             ret[f.getName()] = v;
28210         });
28211         
28212         return ret;
28213     },
28214
28215     /**
28216      * Clears all invalid messages in this form.
28217      * @return {BasicForm} this
28218      */
28219     clearInvalid : function(){
28220         this.items.each(function(f){
28221            f.clearInvalid();
28222         });
28223         
28224         Roo.each(this.childForms || [], function (f) {
28225             f.clearInvalid();
28226         });
28227         
28228         
28229         return this;
28230     },
28231
28232     /**
28233      * Resets this form.
28234      * @return {BasicForm} this
28235      */
28236     reset : function(){
28237         this.items.each(function(f){
28238             f.reset();
28239         });
28240         
28241         Roo.each(this.childForms || [], function (f) {
28242             f.reset();
28243         });
28244        
28245         
28246         return this;
28247     },
28248
28249     /**
28250      * Add Roo.form components to this form.
28251      * @param {Field} field1
28252      * @param {Field} field2 (optional)
28253      * @param {Field} etc (optional)
28254      * @return {BasicForm} this
28255      */
28256     add : function(){
28257         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28258         return this;
28259     },
28260
28261
28262     /**
28263      * Removes a field from the items collection (does NOT remove its markup).
28264      * @param {Field} field
28265      * @return {BasicForm} this
28266      */
28267     remove : function(field){
28268         this.items.remove(field);
28269         return this;
28270     },
28271
28272     /**
28273      * Looks at the fields in this form, checks them for an id attribute,
28274      * and calls applyTo on the existing dom element with that id.
28275      * @return {BasicForm} this
28276      */
28277     render : function(){
28278         this.items.each(function(f){
28279             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28280                 f.applyTo(f.id);
28281             }
28282         });
28283         return this;
28284     },
28285
28286     /**
28287      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28288      * @param {Object} values
28289      * @return {BasicForm} this
28290      */
28291     applyToFields : function(o){
28292         this.items.each(function(f){
28293            Roo.apply(f, o);
28294         });
28295         return this;
28296     },
28297
28298     /**
28299      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28300      * @param {Object} values
28301      * @return {BasicForm} this
28302      */
28303     applyIfToFields : function(o){
28304         this.items.each(function(f){
28305            Roo.applyIf(f, o);
28306         });
28307         return this;
28308     }
28309 });
28310
28311 // back compat
28312 Roo.BasicForm = Roo.form.BasicForm;/*
28313  * Based on:
28314  * Ext JS Library 1.1.1
28315  * Copyright(c) 2006-2007, Ext JS, LLC.
28316  *
28317  * Originally Released Under LGPL - original licence link has changed is not relivant.
28318  *
28319  * Fork - LGPL
28320  * <script type="text/javascript">
28321  */
28322
28323 /**
28324  * @class Roo.form.Form
28325  * @extends Roo.form.BasicForm
28326  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28327  * @constructor
28328  * @param {Object} config Configuration options
28329  */
28330 Roo.form.Form = function(config){
28331     var xitems =  [];
28332     if (config.items) {
28333         xitems = config.items;
28334         delete config.items;
28335     }
28336    
28337     
28338     Roo.form.Form.superclass.constructor.call(this, null, config);
28339     this.url = this.url || this.action;
28340     if(!this.root){
28341         this.root = new Roo.form.Layout(Roo.applyIf({
28342             id: Roo.id()
28343         }, config));
28344     }
28345     this.active = this.root;
28346     /**
28347      * Array of all the buttons that have been added to this form via {@link addButton}
28348      * @type Array
28349      */
28350     this.buttons = [];
28351     this.allItems = [];
28352     this.addEvents({
28353         /**
28354          * @event clientvalidation
28355          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28356          * @param {Form} this
28357          * @param {Boolean} valid true if the form has passed client-side validation
28358          */
28359         clientvalidation: true,
28360         /**
28361          * @event rendered
28362          * Fires when the form is rendered
28363          * @param {Roo.form.Form} form
28364          */
28365         rendered : true
28366     });
28367     
28368     if (this.progressUrl) {
28369             // push a hidden field onto the list of fields..
28370             this.addxtype( {
28371                     xns: Roo.form, 
28372                     xtype : 'Hidden', 
28373                     name : 'UPLOAD_IDENTIFIER' 
28374             });
28375         }
28376         
28377     
28378     Roo.each(xitems, this.addxtype, this);
28379     
28380     
28381     
28382 };
28383
28384 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28385     /**
28386      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28387      */
28388     /**
28389      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28390      */
28391     /**
28392      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28393      */
28394     buttonAlign:'center',
28395
28396     /**
28397      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28398      */
28399     minButtonWidth:75,
28400
28401     /**
28402      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28403      * This property cascades to child containers if not set.
28404      */
28405     labelAlign:'left',
28406
28407     /**
28408      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28409      * fires a looping event with that state. This is required to bind buttons to the valid
28410      * state using the config value formBind:true on the button.
28411      */
28412     monitorValid : false,
28413
28414     /**
28415      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28416      */
28417     monitorPoll : 200,
28418     
28419     /**
28420      * @cfg {String} progressUrl - Url to return progress data 
28421      */
28422     
28423     progressUrl : false,
28424   
28425     /**
28426      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28427      * fields are added and the column is closed. If no fields are passed the column remains open
28428      * until end() is called.
28429      * @param {Object} config The config to pass to the column
28430      * @param {Field} field1 (optional)
28431      * @param {Field} field2 (optional)
28432      * @param {Field} etc (optional)
28433      * @return Column The column container object
28434      */
28435     column : function(c){
28436         var col = new Roo.form.Column(c);
28437         this.start(col);
28438         if(arguments.length > 1){ // duplicate code required because of Opera
28439             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28440             this.end();
28441         }
28442         return col;
28443     },
28444
28445     /**
28446      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28447      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28448      * until end() is called.
28449      * @param {Object} config The config to pass to the fieldset
28450      * @param {Field} field1 (optional)
28451      * @param {Field} field2 (optional)
28452      * @param {Field} etc (optional)
28453      * @return FieldSet The fieldset container object
28454      */
28455     fieldset : function(c){
28456         var fs = new Roo.form.FieldSet(c);
28457         this.start(fs);
28458         if(arguments.length > 1){ // duplicate code required because of Opera
28459             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28460             this.end();
28461         }
28462         return fs;
28463     },
28464
28465     /**
28466      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28467      * fields are added and the container is closed. If no fields are passed the container remains open
28468      * until end() is called.
28469      * @param {Object} config The config to pass to the Layout
28470      * @param {Field} field1 (optional)
28471      * @param {Field} field2 (optional)
28472      * @param {Field} etc (optional)
28473      * @return Layout The container object
28474      */
28475     container : function(c){
28476         var l = new Roo.form.Layout(c);
28477         this.start(l);
28478         if(arguments.length > 1){ // duplicate code required because of Opera
28479             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28480             this.end();
28481         }
28482         return l;
28483     },
28484
28485     /**
28486      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28487      * @param {Object} container A Roo.form.Layout or subclass of Layout
28488      * @return {Form} this
28489      */
28490     start : function(c){
28491         // cascade label info
28492         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28493         this.active.stack.push(c);
28494         c.ownerCt = this.active;
28495         this.active = c;
28496         return this;
28497     },
28498
28499     /**
28500      * Closes the current open container
28501      * @return {Form} this
28502      */
28503     end : function(){
28504         if(this.active == this.root){
28505             return this;
28506         }
28507         this.active = this.active.ownerCt;
28508         return this;
28509     },
28510
28511     /**
28512      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28513      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28514      * as the label of the field.
28515      * @param {Field} field1
28516      * @param {Field} field2 (optional)
28517      * @param {Field} etc. (optional)
28518      * @return {Form} this
28519      */
28520     add : function(){
28521         this.active.stack.push.apply(this.active.stack, arguments);
28522         this.allItems.push.apply(this.allItems,arguments);
28523         var r = [];
28524         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28525             if(a[i].isFormField){
28526                 r.push(a[i]);
28527             }
28528         }
28529         if(r.length > 0){
28530             Roo.form.Form.superclass.add.apply(this, r);
28531         }
28532         return this;
28533     },
28534     
28535
28536     
28537     
28538     
28539      /**
28540      * Find any element that has been added to a form, using it's ID or name
28541      * This can include framesets, columns etc. along with regular fields..
28542      * @param {String} id - id or name to find.
28543      
28544      * @return {Element} e - or false if nothing found.
28545      */
28546     findbyId : function(id)
28547     {
28548         var ret = false;
28549         if (!id) {
28550             return ret;
28551         }
28552         Roo.each(this.allItems, function(f){
28553             if (f.id == id || f.name == id ){
28554                 ret = f;
28555                 return false;
28556             }
28557         });
28558         return ret;
28559     },
28560
28561     
28562     
28563     /**
28564      * Render this form into the passed container. This should only be called once!
28565      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28566      * @return {Form} this
28567      */
28568     render : function(ct)
28569     {
28570         
28571         
28572         
28573         ct = Roo.get(ct);
28574         var o = this.autoCreate || {
28575             tag: 'form',
28576             method : this.method || 'POST',
28577             id : this.id || Roo.id()
28578         };
28579         this.initEl(ct.createChild(o));
28580
28581         this.root.render(this.el);
28582         
28583        
28584              
28585         this.items.each(function(f){
28586             f.render('x-form-el-'+f.id);
28587         });
28588
28589         if(this.buttons.length > 0){
28590             // tables are required to maintain order and for correct IE layout
28591             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28592                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28593                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28594             }}, null, true);
28595             var tr = tb.getElementsByTagName('tr')[0];
28596             for(var i = 0, len = this.buttons.length; i < len; i++) {
28597                 var b = this.buttons[i];
28598                 var td = document.createElement('td');
28599                 td.className = 'x-form-btn-td';
28600                 b.render(tr.appendChild(td));
28601             }
28602         }
28603         if(this.monitorValid){ // initialize after render
28604             this.startMonitoring();
28605         }
28606         this.fireEvent('rendered', this);
28607         return this;
28608     },
28609
28610     /**
28611      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28612      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28613      * object or a valid Roo.DomHelper element config
28614      * @param {Function} handler The function called when the button is clicked
28615      * @param {Object} scope (optional) The scope of the handler function
28616      * @return {Roo.Button}
28617      */
28618     addButton : function(config, handler, scope){
28619         var bc = {
28620             handler: handler,
28621             scope: scope,
28622             minWidth: this.minButtonWidth,
28623             hideParent:true
28624         };
28625         if(typeof config == "string"){
28626             bc.text = config;
28627         }else{
28628             Roo.apply(bc, config);
28629         }
28630         var btn = new Roo.Button(null, bc);
28631         this.buttons.push(btn);
28632         return btn;
28633     },
28634
28635      /**
28636      * Adds a series of form elements (using the xtype property as the factory method.
28637      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28638      * @param {Object} config 
28639      */
28640     
28641     addxtype : function()
28642     {
28643         var ar = Array.prototype.slice.call(arguments, 0);
28644         var ret = false;
28645         for(var i = 0; i < ar.length; i++) {
28646             if (!ar[i]) {
28647                 continue; // skip -- if this happends something invalid got sent, we 
28648                 // should ignore it, as basically that interface element will not show up
28649                 // and that should be pretty obvious!!
28650             }
28651             
28652             if (Roo.form[ar[i].xtype]) {
28653                 ar[i].form = this;
28654                 var fe = Roo.factory(ar[i], Roo.form);
28655                 if (!ret) {
28656                     ret = fe;
28657                 }
28658                 fe.form = this;
28659                 if (fe.store) {
28660                     fe.store.form = this;
28661                 }
28662                 if (fe.isLayout) {  
28663                          
28664                     this.start(fe);
28665                     this.allItems.push(fe);
28666                     if (fe.items && fe.addxtype) {
28667                         fe.addxtype.apply(fe, fe.items);
28668                         delete fe.items;
28669                     }
28670                      this.end();
28671                     continue;
28672                 }
28673                 
28674                 
28675                  
28676                 this.add(fe);
28677               //  console.log('adding ' + ar[i].xtype);
28678             }
28679             if (ar[i].xtype == 'Button') {  
28680                 //console.log('adding button');
28681                 //console.log(ar[i]);
28682                 this.addButton(ar[i]);
28683                 this.allItems.push(fe);
28684                 continue;
28685             }
28686             
28687             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28688                 alert('end is not supported on xtype any more, use items');
28689             //    this.end();
28690             //    //console.log('adding end');
28691             }
28692             
28693         }
28694         return ret;
28695     },
28696     
28697     /**
28698      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28699      * option "monitorValid"
28700      */
28701     startMonitoring : function(){
28702         if(!this.bound){
28703             this.bound = true;
28704             Roo.TaskMgr.start({
28705                 run : this.bindHandler,
28706                 interval : this.monitorPoll || 200,
28707                 scope: this
28708             });
28709         }
28710     },
28711
28712     /**
28713      * Stops monitoring of the valid state of this form
28714      */
28715     stopMonitoring : function(){
28716         this.bound = false;
28717     },
28718
28719     // private
28720     bindHandler : function(){
28721         if(!this.bound){
28722             return false; // stops binding
28723         }
28724         var valid = true;
28725         this.items.each(function(f){
28726             if(!f.isValid(true)){
28727                 valid = false;
28728                 return false;
28729             }
28730         });
28731         for(var i = 0, len = this.buttons.length; i < len; i++){
28732             var btn = this.buttons[i];
28733             if(btn.formBind === true && btn.disabled === valid){
28734                 btn.setDisabled(!valid);
28735             }
28736         }
28737         this.fireEvent('clientvalidation', this, valid);
28738     }
28739     
28740     
28741     
28742     
28743     
28744     
28745     
28746     
28747 });
28748
28749
28750 // back compat
28751 Roo.Form = Roo.form.Form;
28752 /*
28753  * Based on:
28754  * Ext JS Library 1.1.1
28755  * Copyright(c) 2006-2007, Ext JS, LLC.
28756  *
28757  * Originally Released Under LGPL - original licence link has changed is not relivant.
28758  *
28759  * Fork - LGPL
28760  * <script type="text/javascript">
28761  */
28762  
28763  /**
28764  * @class Roo.form.Action
28765  * Internal Class used to handle form actions
28766  * @constructor
28767  * @param {Roo.form.BasicForm} el The form element or its id
28768  * @param {Object} config Configuration options
28769  */
28770  
28771  
28772 // define the action interface
28773 Roo.form.Action = function(form, options){
28774     this.form = form;
28775     this.options = options || {};
28776 };
28777 /**
28778  * Client Validation Failed
28779  * @const 
28780  */
28781 Roo.form.Action.CLIENT_INVALID = 'client';
28782 /**
28783  * Server Validation Failed
28784  * @const 
28785  */
28786  Roo.form.Action.SERVER_INVALID = 'server';
28787  /**
28788  * Connect to Server Failed
28789  * @const 
28790  */
28791 Roo.form.Action.CONNECT_FAILURE = 'connect';
28792 /**
28793  * Reading Data from Server Failed
28794  * @const 
28795  */
28796 Roo.form.Action.LOAD_FAILURE = 'load';
28797
28798 Roo.form.Action.prototype = {
28799     type : 'default',
28800     failureType : undefined,
28801     response : undefined,
28802     result : undefined,
28803
28804     // interface method
28805     run : function(options){
28806
28807     },
28808
28809     // interface method
28810     success : function(response){
28811
28812     },
28813
28814     // interface method
28815     handleResponse : function(response){
28816
28817     },
28818
28819     // default connection failure
28820     failure : function(response){
28821         
28822         this.response = response;
28823         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28824         this.form.afterAction(this, false);
28825     },
28826
28827     processResponse : function(response){
28828         this.response = response;
28829         if(!response.responseText){
28830             return true;
28831         }
28832         this.result = this.handleResponse(response);
28833         return this.result;
28834     },
28835
28836     // utility functions used internally
28837     getUrl : function(appendParams){
28838         var url = this.options.url || this.form.url || this.form.el.dom.action;
28839         if(appendParams){
28840             var p = this.getParams();
28841             if(p){
28842                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28843             }
28844         }
28845         return url;
28846     },
28847
28848     getMethod : function(){
28849         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28850     },
28851
28852     getParams : function(){
28853         var bp = this.form.baseParams;
28854         var p = this.options.params;
28855         if(p){
28856             if(typeof p == "object"){
28857                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28858             }else if(typeof p == 'string' && bp){
28859                 p += '&' + Roo.urlEncode(bp);
28860             }
28861         }else if(bp){
28862             p = Roo.urlEncode(bp);
28863         }
28864         return p;
28865     },
28866
28867     createCallback : function(){
28868         return {
28869             success: this.success,
28870             failure: this.failure,
28871             scope: this,
28872             timeout: (this.form.timeout*1000),
28873             upload: this.form.fileUpload ? this.success : undefined
28874         };
28875     }
28876 };
28877
28878 Roo.form.Action.Submit = function(form, options){
28879     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28880 };
28881
28882 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28883     type : 'submit',
28884
28885     haveProgress : false,
28886     uploadComplete : false,
28887     
28888     // uploadProgress indicator.
28889     uploadProgress : function()
28890     {
28891         if (!this.form.progressUrl) {
28892             return;
28893         }
28894         
28895         if (!this.haveProgress) {
28896             Roo.MessageBox.progress("Uploading", "Uploading");
28897         }
28898         if (this.uploadComplete) {
28899            Roo.MessageBox.hide();
28900            return;
28901         }
28902         
28903         this.haveProgress = true;
28904    
28905         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28906         
28907         var c = new Roo.data.Connection();
28908         c.request({
28909             url : this.form.progressUrl,
28910             params: {
28911                 id : uid
28912             },
28913             method: 'GET',
28914             success : function(req){
28915                //console.log(data);
28916                 var rdata = false;
28917                 var edata;
28918                 try  {
28919                    rdata = Roo.decode(req.responseText)
28920                 } catch (e) {
28921                     Roo.log("Invalid data from server..");
28922                     Roo.log(edata);
28923                     return;
28924                 }
28925                 if (!rdata || !rdata.success) {
28926                     Roo.log(rdata);
28927                     Roo.MessageBox.alert(Roo.encode(rdata));
28928                     return;
28929                 }
28930                 var data = rdata.data;
28931                 
28932                 if (this.uploadComplete) {
28933                    Roo.MessageBox.hide();
28934                    return;
28935                 }
28936                    
28937                 if (data){
28938                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28939                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28940                     );
28941                 }
28942                 this.uploadProgress.defer(2000,this);
28943             },
28944        
28945             failure: function(data) {
28946                 Roo.log('progress url failed ');
28947                 Roo.log(data);
28948             },
28949             scope : this
28950         });
28951            
28952     },
28953     
28954     
28955     run : function()
28956     {
28957         // run get Values on the form, so it syncs any secondary forms.
28958         this.form.getValues();
28959         
28960         var o = this.options;
28961         var method = this.getMethod();
28962         var isPost = method == 'POST';
28963         if(o.clientValidation === false || this.form.isValid()){
28964             
28965             if (this.form.progressUrl) {
28966                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28967                     (new Date() * 1) + '' + Math.random());
28968                     
28969             } 
28970             
28971             
28972             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28973                 form:this.form.el.dom,
28974                 url:this.getUrl(!isPost),
28975                 method: method,
28976                 params:isPost ? this.getParams() : null,
28977                 isUpload: this.form.fileUpload
28978             }));
28979             
28980             this.uploadProgress();
28981
28982         }else if (o.clientValidation !== false){ // client validation failed
28983             this.failureType = Roo.form.Action.CLIENT_INVALID;
28984             this.form.afterAction(this, false);
28985         }
28986     },
28987
28988     success : function(response)
28989     {
28990         this.uploadComplete= true;
28991         if (this.haveProgress) {
28992             Roo.MessageBox.hide();
28993         }
28994         
28995         
28996         var result = this.processResponse(response);
28997         if(result === true || result.success){
28998             this.form.afterAction(this, true);
28999             return;
29000         }
29001         if(result.errors){
29002             this.form.markInvalid(result.errors);
29003             this.failureType = Roo.form.Action.SERVER_INVALID;
29004         }
29005         this.form.afterAction(this, false);
29006     },
29007     failure : function(response)
29008     {
29009         this.uploadComplete= true;
29010         if (this.haveProgress) {
29011             Roo.MessageBox.hide();
29012         }
29013         
29014         this.response = response;
29015         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29016         this.form.afterAction(this, false);
29017     },
29018     
29019     handleResponse : function(response){
29020         if(this.form.errorReader){
29021             var rs = this.form.errorReader.read(response);
29022             var errors = [];
29023             if(rs.records){
29024                 for(var i = 0, len = rs.records.length; i < len; i++) {
29025                     var r = rs.records[i];
29026                     errors[i] = r.data;
29027                 }
29028             }
29029             if(errors.length < 1){
29030                 errors = null;
29031             }
29032             return {
29033                 success : rs.success,
29034                 errors : errors
29035             };
29036         }
29037         var ret = false;
29038         try {
29039             ret = Roo.decode(response.responseText);
29040         } catch (e) {
29041             ret = {
29042                 success: false,
29043                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29044                 errors : []
29045             };
29046         }
29047         return ret;
29048         
29049     }
29050 });
29051
29052
29053 Roo.form.Action.Load = function(form, options){
29054     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29055     this.reader = this.form.reader;
29056 };
29057
29058 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29059     type : 'load',
29060
29061     run : function(){
29062         
29063         Roo.Ajax.request(Roo.apply(
29064                 this.createCallback(), {
29065                     method:this.getMethod(),
29066                     url:this.getUrl(false),
29067                     params:this.getParams()
29068         }));
29069     },
29070
29071     success : function(response){
29072         
29073         var result = this.processResponse(response);
29074         if(result === true || !result.success || !result.data){
29075             this.failureType = Roo.form.Action.LOAD_FAILURE;
29076             this.form.afterAction(this, false);
29077             return;
29078         }
29079         this.form.clearInvalid();
29080         this.form.setValues(result.data);
29081         this.form.afterAction(this, true);
29082     },
29083
29084     handleResponse : function(response){
29085         if(this.form.reader){
29086             var rs = this.form.reader.read(response);
29087             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29088             return {
29089                 success : rs.success,
29090                 data : data
29091             };
29092         }
29093         return Roo.decode(response.responseText);
29094     }
29095 });
29096
29097 Roo.form.Action.ACTION_TYPES = {
29098     'load' : Roo.form.Action.Load,
29099     'submit' : Roo.form.Action.Submit
29100 };/*
29101  * Based on:
29102  * Ext JS Library 1.1.1
29103  * Copyright(c) 2006-2007, Ext JS, LLC.
29104  *
29105  * Originally Released Under LGPL - original licence link has changed is not relivant.
29106  *
29107  * Fork - LGPL
29108  * <script type="text/javascript">
29109  */
29110  
29111 /**
29112  * @class Roo.form.Layout
29113  * @extends Roo.Component
29114  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29115  * @constructor
29116  * @param {Object} config Configuration options
29117  */
29118 Roo.form.Layout = function(config){
29119     var xitems = [];
29120     if (config.items) {
29121         xitems = config.items;
29122         delete config.items;
29123     }
29124     Roo.form.Layout.superclass.constructor.call(this, config);
29125     this.stack = [];
29126     Roo.each(xitems, this.addxtype, this);
29127      
29128 };
29129
29130 Roo.extend(Roo.form.Layout, Roo.Component, {
29131     /**
29132      * @cfg {String/Object} autoCreate
29133      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29134      */
29135     /**
29136      * @cfg {String/Object/Function} style
29137      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29138      * a function which returns such a specification.
29139      */
29140     /**
29141      * @cfg {String} labelAlign
29142      * Valid values are "left," "top" and "right" (defaults to "left")
29143      */
29144     /**
29145      * @cfg {Number} labelWidth
29146      * Fixed width in pixels of all field labels (defaults to undefined)
29147      */
29148     /**
29149      * @cfg {Boolean} clear
29150      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29151      */
29152     clear : true,
29153     /**
29154      * @cfg {String} labelSeparator
29155      * The separator to use after field labels (defaults to ':')
29156      */
29157     labelSeparator : ':',
29158     /**
29159      * @cfg {Boolean} hideLabels
29160      * True to suppress the display of field labels in this layout (defaults to false)
29161      */
29162     hideLabels : false,
29163
29164     // private
29165     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29166     
29167     isLayout : true,
29168     
29169     // private
29170     onRender : function(ct, position){
29171         if(this.el){ // from markup
29172             this.el = Roo.get(this.el);
29173         }else {  // generate
29174             var cfg = this.getAutoCreate();
29175             this.el = ct.createChild(cfg, position);
29176         }
29177         if(this.style){
29178             this.el.applyStyles(this.style);
29179         }
29180         if(this.labelAlign){
29181             this.el.addClass('x-form-label-'+this.labelAlign);
29182         }
29183         if(this.hideLabels){
29184             this.labelStyle = "display:none";
29185             this.elementStyle = "padding-left:0;";
29186         }else{
29187             if(typeof this.labelWidth == 'number'){
29188                 this.labelStyle = "width:"+this.labelWidth+"px;";
29189                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29190             }
29191             if(this.labelAlign == 'top'){
29192                 this.labelStyle = "width:auto;";
29193                 this.elementStyle = "padding-left:0;";
29194             }
29195         }
29196         var stack = this.stack;
29197         var slen = stack.length;
29198         if(slen > 0){
29199             if(!this.fieldTpl){
29200                 var t = new Roo.Template(
29201                     '<div class="x-form-item {5}">',
29202                         '<label for="{0}" style="{2}">{1}{4}</label>',
29203                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29204                         '</div>',
29205                     '</div><div class="x-form-clear-left"></div>'
29206                 );
29207                 t.disableFormats = true;
29208                 t.compile();
29209                 Roo.form.Layout.prototype.fieldTpl = t;
29210             }
29211             for(var i = 0; i < slen; i++) {
29212                 if(stack[i].isFormField){
29213                     this.renderField(stack[i]);
29214                 }else{
29215                     this.renderComponent(stack[i]);
29216                 }
29217             }
29218         }
29219         if(this.clear){
29220             this.el.createChild({cls:'x-form-clear'});
29221         }
29222     },
29223
29224     // private
29225     renderField : function(f){
29226         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29227                f.id, //0
29228                f.fieldLabel, //1
29229                f.labelStyle||this.labelStyle||'', //2
29230                this.elementStyle||'', //3
29231                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29232                f.itemCls||this.itemCls||''  //5
29233        ], true).getPrevSibling());
29234     },
29235
29236     // private
29237     renderComponent : function(c){
29238         c.render(c.isLayout ? this.el : this.el.createChild());    
29239     },
29240     /**
29241      * Adds a object form elements (using the xtype property as the factory method.)
29242      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29243      * @param {Object} config 
29244      */
29245     addxtype : function(o)
29246     {
29247         // create the lement.
29248         o.form = this.form;
29249         var fe = Roo.factory(o, Roo.form);
29250         this.form.allItems.push(fe);
29251         this.stack.push(fe);
29252         
29253         if (fe.isFormField) {
29254             this.form.items.add(fe);
29255         }
29256          
29257         return fe;
29258     }
29259 });
29260
29261 /**
29262  * @class Roo.form.Column
29263  * @extends Roo.form.Layout
29264  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29265  * @constructor
29266  * @param {Object} config Configuration options
29267  */
29268 Roo.form.Column = function(config){
29269     Roo.form.Column.superclass.constructor.call(this, config);
29270 };
29271
29272 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29273     /**
29274      * @cfg {Number/String} width
29275      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29276      */
29277     /**
29278      * @cfg {String/Object} autoCreate
29279      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29280      */
29281
29282     // private
29283     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29284
29285     // private
29286     onRender : function(ct, position){
29287         Roo.form.Column.superclass.onRender.call(this, ct, position);
29288         if(this.width){
29289             this.el.setWidth(this.width);
29290         }
29291     }
29292 });
29293
29294
29295 /**
29296  * @class Roo.form.Row
29297  * @extends Roo.form.Layout
29298  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29299  * @constructor
29300  * @param {Object} config Configuration options
29301  */
29302
29303  
29304 Roo.form.Row = function(config){
29305     Roo.form.Row.superclass.constructor.call(this, config);
29306 };
29307  
29308 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29309       /**
29310      * @cfg {Number/String} width
29311      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29312      */
29313     /**
29314      * @cfg {Number/String} height
29315      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29316      */
29317     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29318     
29319     padWidth : 20,
29320     // private
29321     onRender : function(ct, position){
29322         //console.log('row render');
29323         if(!this.rowTpl){
29324             var t = new Roo.Template(
29325                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29326                     '<label for="{0}" style="{2}">{1}{4}</label>',
29327                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29328                     '</div>',
29329                 '</div>'
29330             );
29331             t.disableFormats = true;
29332             t.compile();
29333             Roo.form.Layout.prototype.rowTpl = t;
29334         }
29335         this.fieldTpl = this.rowTpl;
29336         
29337         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29338         var labelWidth = 100;
29339         
29340         if ((this.labelAlign != 'top')) {
29341             if (typeof this.labelWidth == 'number') {
29342                 labelWidth = this.labelWidth
29343             }
29344             this.padWidth =  20 + labelWidth;
29345             
29346         }
29347         
29348         Roo.form.Column.superclass.onRender.call(this, ct, position);
29349         if(this.width){
29350             this.el.setWidth(this.width);
29351         }
29352         if(this.height){
29353             this.el.setHeight(this.height);
29354         }
29355     },
29356     
29357     // private
29358     renderField : function(f){
29359         f.fieldEl = this.fieldTpl.append(this.el, [
29360                f.id, f.fieldLabel,
29361                f.labelStyle||this.labelStyle||'',
29362                this.elementStyle||'',
29363                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29364                f.itemCls||this.itemCls||'',
29365                f.width ? f.width + this.padWidth : 160 + this.padWidth
29366        ],true);
29367     }
29368 });
29369  
29370
29371 /**
29372  * @class Roo.form.FieldSet
29373  * @extends Roo.form.Layout
29374  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29375  * @constructor
29376  * @param {Object} config Configuration options
29377  */
29378 Roo.form.FieldSet = function(config){
29379     Roo.form.FieldSet.superclass.constructor.call(this, config);
29380 };
29381
29382 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29383     /**
29384      * @cfg {String} legend
29385      * The text to display as the legend for the FieldSet (defaults to '')
29386      */
29387     /**
29388      * @cfg {String/Object} autoCreate
29389      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29390      */
29391
29392     // private
29393     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29394
29395     // private
29396     onRender : function(ct, position){
29397         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29398         if(this.legend){
29399             this.setLegend(this.legend);
29400         }
29401     },
29402
29403     // private
29404     setLegend : function(text){
29405         if(this.rendered){
29406             this.el.child('legend').update(text);
29407         }
29408     }
29409 });/*
29410  * Based on:
29411  * Ext JS Library 1.1.1
29412  * Copyright(c) 2006-2007, Ext JS, LLC.
29413  *
29414  * Originally Released Under LGPL - original licence link has changed is not relivant.
29415  *
29416  * Fork - LGPL
29417  * <script type="text/javascript">
29418  */
29419 /**
29420  * @class Roo.form.VTypes
29421  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29422  * @singleton
29423  */
29424 Roo.form.VTypes = function(){
29425     // closure these in so they are only created once.
29426     var alpha = /^[a-zA-Z_]+$/;
29427     var alphanum = /^[a-zA-Z0-9_]+$/;
29428     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29429     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29430
29431     // All these messages and functions are configurable
29432     return {
29433         /**
29434          * The function used to validate email addresses
29435          * @param {String} value The email address
29436          */
29437         'email' : function(v){
29438             return email.test(v);
29439         },
29440         /**
29441          * The error text to display when the email validation function returns false
29442          * @type String
29443          */
29444         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29445         /**
29446          * The keystroke filter mask to be applied on email input
29447          * @type RegExp
29448          */
29449         'emailMask' : /[a-z0-9_\.\-@]/i,
29450
29451         /**
29452          * The function used to validate URLs
29453          * @param {String} value The URL
29454          */
29455         'url' : function(v){
29456             return url.test(v);
29457         },
29458         /**
29459          * The error text to display when the url validation function returns false
29460          * @type String
29461          */
29462         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29463         
29464         /**
29465          * The function used to validate alpha values
29466          * @param {String} value The value
29467          */
29468         'alpha' : function(v){
29469             return alpha.test(v);
29470         },
29471         /**
29472          * The error text to display when the alpha validation function returns false
29473          * @type String
29474          */
29475         'alphaText' : 'This field should only contain letters and _',
29476         /**
29477          * The keystroke filter mask to be applied on alpha input
29478          * @type RegExp
29479          */
29480         'alphaMask' : /[a-z_]/i,
29481
29482         /**
29483          * The function used to validate alphanumeric values
29484          * @param {String} value The value
29485          */
29486         'alphanum' : function(v){
29487             return alphanum.test(v);
29488         },
29489         /**
29490          * The error text to display when the alphanumeric validation function returns false
29491          * @type String
29492          */
29493         'alphanumText' : 'This field should only contain letters, numbers and _',
29494         /**
29495          * The keystroke filter mask to be applied on alphanumeric input
29496          * @type RegExp
29497          */
29498         'alphanumMask' : /[a-z0-9_]/i
29499     };
29500 }();//<script type="text/javascript">
29501
29502 /**
29503  * @class Roo.form.FCKeditor
29504  * @extends Roo.form.TextArea
29505  * Wrapper around the FCKEditor http://www.fckeditor.net
29506  * @constructor
29507  * Creates a new FCKeditor
29508  * @param {Object} config Configuration options
29509  */
29510 Roo.form.FCKeditor = function(config){
29511     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29512     this.addEvents({
29513          /**
29514          * @event editorinit
29515          * Fired when the editor is initialized - you can add extra handlers here..
29516          * @param {FCKeditor} this
29517          * @param {Object} the FCK object.
29518          */
29519         editorinit : true
29520     });
29521     
29522     
29523 };
29524 Roo.form.FCKeditor.editors = { };
29525 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29526 {
29527     //defaultAutoCreate : {
29528     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29529     //},
29530     // private
29531     /**
29532      * @cfg {Object} fck options - see fck manual for details.
29533      */
29534     fckconfig : false,
29535     
29536     /**
29537      * @cfg {Object} fck toolbar set (Basic or Default)
29538      */
29539     toolbarSet : 'Basic',
29540     /**
29541      * @cfg {Object} fck BasePath
29542      */ 
29543     basePath : '/fckeditor/',
29544     
29545     
29546     frame : false,
29547     
29548     value : '',
29549     
29550    
29551     onRender : function(ct, position)
29552     {
29553         if(!this.el){
29554             this.defaultAutoCreate = {
29555                 tag: "textarea",
29556                 style:"width:300px;height:60px;",
29557                 autocomplete: "off"
29558             };
29559         }
29560         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29561         /*
29562         if(this.grow){
29563             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29564             if(this.preventScrollbars){
29565                 this.el.setStyle("overflow", "hidden");
29566             }
29567             this.el.setHeight(this.growMin);
29568         }
29569         */
29570         //console.log('onrender' + this.getId() );
29571         Roo.form.FCKeditor.editors[this.getId()] = this;
29572          
29573
29574         this.replaceTextarea() ;
29575         
29576     },
29577     
29578     getEditor : function() {
29579         return this.fckEditor;
29580     },
29581     /**
29582      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29583      * @param {Mixed} value The value to set
29584      */
29585     
29586     
29587     setValue : function(value)
29588     {
29589         //console.log('setValue: ' + value);
29590         
29591         if(typeof(value) == 'undefined') { // not sure why this is happending...
29592             return;
29593         }
29594         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29595         
29596         //if(!this.el || !this.getEditor()) {
29597         //    this.value = value;
29598             //this.setValue.defer(100,this,[value]);    
29599         //    return;
29600         //} 
29601         
29602         if(!this.getEditor()) {
29603             return;
29604         }
29605         
29606         this.getEditor().SetData(value);
29607         
29608         //
29609
29610     },
29611
29612     /**
29613      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29614      * @return {Mixed} value The field value
29615      */
29616     getValue : function()
29617     {
29618         
29619         if (this.frame && this.frame.dom.style.display == 'none') {
29620             return Roo.form.FCKeditor.superclass.getValue.call(this);
29621         }
29622         
29623         if(!this.el || !this.getEditor()) {
29624            
29625            // this.getValue.defer(100,this); 
29626             return this.value;
29627         }
29628        
29629         
29630         var value=this.getEditor().GetData();
29631         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29632         return Roo.form.FCKeditor.superclass.getValue.call(this);
29633         
29634
29635     },
29636
29637     /**
29638      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29639      * @return {Mixed} value The field value
29640      */
29641     getRawValue : function()
29642     {
29643         if (this.frame && this.frame.dom.style.display == 'none') {
29644             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29645         }
29646         
29647         if(!this.el || !this.getEditor()) {
29648             //this.getRawValue.defer(100,this); 
29649             return this.value;
29650             return;
29651         }
29652         
29653         
29654         
29655         var value=this.getEditor().GetData();
29656         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29657         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29658          
29659     },
29660     
29661     setSize : function(w,h) {
29662         
29663         
29664         
29665         //if (this.frame && this.frame.dom.style.display == 'none') {
29666         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29667         //    return;
29668         //}
29669         //if(!this.el || !this.getEditor()) {
29670         //    this.setSize.defer(100,this, [w,h]); 
29671         //    return;
29672         //}
29673         
29674         
29675         
29676         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29677         
29678         this.frame.dom.setAttribute('width', w);
29679         this.frame.dom.setAttribute('height', h);
29680         this.frame.setSize(w,h);
29681         
29682     },
29683     
29684     toggleSourceEdit : function(value) {
29685         
29686       
29687          
29688         this.el.dom.style.display = value ? '' : 'none';
29689         this.frame.dom.style.display = value ?  'none' : '';
29690         
29691     },
29692     
29693     
29694     focus: function(tag)
29695     {
29696         if (this.frame.dom.style.display == 'none') {
29697             return Roo.form.FCKeditor.superclass.focus.call(this);
29698         }
29699         if(!this.el || !this.getEditor()) {
29700             this.focus.defer(100,this, [tag]); 
29701             return;
29702         }
29703         
29704         
29705         
29706         
29707         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29708         this.getEditor().Focus();
29709         if (tgs.length) {
29710             if (!this.getEditor().Selection.GetSelection()) {
29711                 this.focus.defer(100,this, [tag]); 
29712                 return;
29713             }
29714             
29715             
29716             var r = this.getEditor().EditorDocument.createRange();
29717             r.setStart(tgs[0],0);
29718             r.setEnd(tgs[0],0);
29719             this.getEditor().Selection.GetSelection().removeAllRanges();
29720             this.getEditor().Selection.GetSelection().addRange(r);
29721             this.getEditor().Focus();
29722         }
29723         
29724     },
29725     
29726     
29727     
29728     replaceTextarea : function()
29729     {
29730         if ( document.getElementById( this.getId() + '___Frame' ) )
29731             return ;
29732         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29733         //{
29734             // We must check the elements firstly using the Id and then the name.
29735         var oTextarea = document.getElementById( this.getId() );
29736         
29737         var colElementsByName = document.getElementsByName( this.getId() ) ;
29738          
29739         oTextarea.style.display = 'none' ;
29740
29741         if ( oTextarea.tabIndex ) {            
29742             this.TabIndex = oTextarea.tabIndex ;
29743         }
29744         
29745         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29746         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29747         this.frame = Roo.get(this.getId() + '___Frame')
29748     },
29749     
29750     _getConfigHtml : function()
29751     {
29752         var sConfig = '' ;
29753
29754         for ( var o in this.fckconfig ) {
29755             sConfig += sConfig.length > 0  ? '&amp;' : '';
29756             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29757         }
29758
29759         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29760     },
29761     
29762     
29763     _getIFrameHtml : function()
29764     {
29765         var sFile = 'fckeditor.html' ;
29766         /* no idea what this is about..
29767         try
29768         {
29769             if ( (/fcksource=true/i).test( window.top.location.search ) )
29770                 sFile = 'fckeditor.original.html' ;
29771         }
29772         catch (e) { 
29773         */
29774
29775         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29776         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29777         
29778         
29779         var html = '<iframe id="' + this.getId() +
29780             '___Frame" src="' + sLink +
29781             '" width="' + this.width +
29782             '" height="' + this.height + '"' +
29783             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29784             ' frameborder="0" scrolling="no"></iframe>' ;
29785
29786         return html ;
29787     },
29788     
29789     _insertHtmlBefore : function( html, element )
29790     {
29791         if ( element.insertAdjacentHTML )       {
29792             // IE
29793             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29794         } else { // Gecko
29795             var oRange = document.createRange() ;
29796             oRange.setStartBefore( element ) ;
29797             var oFragment = oRange.createContextualFragment( html );
29798             element.parentNode.insertBefore( oFragment, element ) ;
29799         }
29800     }
29801     
29802     
29803   
29804     
29805     
29806     
29807     
29808
29809 });
29810
29811 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29812
29813 function FCKeditor_OnComplete(editorInstance){
29814     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29815     f.fckEditor = editorInstance;
29816     //console.log("loaded");
29817     f.fireEvent('editorinit', f, editorInstance);
29818
29819   
29820
29821  
29822
29823
29824
29825
29826
29827
29828
29829
29830
29831
29832
29833
29834
29835
29836
29837 //<script type="text/javascript">
29838 /**
29839  * @class Roo.form.GridField
29840  * @extends Roo.form.Field
29841  * Embed a grid (or editable grid into a form)
29842  * STATUS ALPHA
29843  * 
29844  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29845  * it needs 
29846  * xgrid.store = Roo.data.Store
29847  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29848  * xgrid.store.reader = Roo.data.JsonReader 
29849  * 
29850  * 
29851  * @constructor
29852  * Creates a new GridField
29853  * @param {Object} config Configuration options
29854  */
29855 Roo.form.GridField = function(config){
29856     Roo.form.GridField.superclass.constructor.call(this, config);
29857      
29858 };
29859
29860 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29861     /**
29862      * @cfg {Number} width  - used to restrict width of grid..
29863      */
29864     width : 100,
29865     /**
29866      * @cfg {Number} height - used to restrict height of grid..
29867      */
29868     height : 50,
29869      /**
29870      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29871          * 
29872          *}
29873      */
29874     xgrid : false, 
29875     /**
29876      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29877      * {tag: "input", type: "checkbox", autocomplete: "off"})
29878      */
29879    // defaultAutoCreate : { tag: 'div' },
29880     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29881     /**
29882      * @cfg {String} addTitle Text to include for adding a title.
29883      */
29884     addTitle : false,
29885     //
29886     onResize : function(){
29887         Roo.form.Field.superclass.onResize.apply(this, arguments);
29888     },
29889
29890     initEvents : function(){
29891         // Roo.form.Checkbox.superclass.initEvents.call(this);
29892         // has no events...
29893        
29894     },
29895
29896
29897     getResizeEl : function(){
29898         return this.wrap;
29899     },
29900
29901     getPositionEl : function(){
29902         return this.wrap;
29903     },
29904
29905     // private
29906     onRender : function(ct, position){
29907         
29908         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29909         var style = this.style;
29910         delete this.style;
29911         
29912         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29913         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29914         this.viewEl = this.wrap.createChild({ tag: 'div' });
29915         if (style) {
29916             this.viewEl.applyStyles(style);
29917         }
29918         if (this.width) {
29919             this.viewEl.setWidth(this.width);
29920         }
29921         if (this.height) {
29922             this.viewEl.setHeight(this.height);
29923         }
29924         //if(this.inputValue !== undefined){
29925         //this.setValue(this.value);
29926         
29927         
29928         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29929         
29930         
29931         this.grid.render();
29932         this.grid.getDataSource().on('remove', this.refreshValue, this);
29933         this.grid.getDataSource().on('update', this.refreshValue, this);
29934         this.grid.on('afteredit', this.refreshValue, this);
29935  
29936     },
29937      
29938     
29939     /**
29940      * Sets the value of the item. 
29941      * @param {String} either an object  or a string..
29942      */
29943     setValue : function(v){
29944         //this.value = v;
29945         v = v || []; // empty set..
29946         // this does not seem smart - it really only affects memoryproxy grids..
29947         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29948             var ds = this.grid.getDataSource();
29949             // assumes a json reader..
29950             var data = {}
29951             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29952             ds.loadData( data);
29953         }
29954         // clear selection so it does not get stale.
29955         if (this.grid.sm) { 
29956             this.grid.sm.clearSelections();
29957         }
29958         
29959         Roo.form.GridField.superclass.setValue.call(this, v);
29960         this.refreshValue();
29961         // should load data in the grid really....
29962     },
29963     
29964     // private
29965     refreshValue: function() {
29966          var val = [];
29967         this.grid.getDataSource().each(function(r) {
29968             val.push(r.data);
29969         });
29970         this.el.dom.value = Roo.encode(val);
29971     }
29972     
29973      
29974     
29975     
29976 });/*
29977  * Based on:
29978  * Ext JS Library 1.1.1
29979  * Copyright(c) 2006-2007, Ext JS, LLC.
29980  *
29981  * Originally Released Under LGPL - original licence link has changed is not relivant.
29982  *
29983  * Fork - LGPL
29984  * <script type="text/javascript">
29985  */
29986 /**
29987  * @class Roo.form.DisplayField
29988  * @extends Roo.form.Field
29989  * A generic Field to display non-editable data.
29990  * @constructor
29991  * Creates a new Display Field item.
29992  * @param {Object} config Configuration options
29993  */
29994 Roo.form.DisplayField = function(config){
29995     Roo.form.DisplayField.superclass.constructor.call(this, config);
29996     
29997 };
29998
29999 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30000     inputType:      'hidden',
30001     allowBlank:     true,
30002     readOnly:         true,
30003     
30004  
30005     /**
30006      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30007      */
30008     focusClass : undefined,
30009     /**
30010      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30011      */
30012     fieldClass: 'x-form-field',
30013     
30014      /**
30015      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30016      */
30017     valueRenderer: undefined,
30018     
30019     width: 100,
30020     /**
30021      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30022      * {tag: "input", type: "checkbox", autocomplete: "off"})
30023      */
30024      
30025  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30026
30027     onResize : function(){
30028         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30029         
30030     },
30031
30032     initEvents : function(){
30033         // Roo.form.Checkbox.superclass.initEvents.call(this);
30034         // has no events...
30035        
30036     },
30037
30038
30039     getResizeEl : function(){
30040         return this.wrap;
30041     },
30042
30043     getPositionEl : function(){
30044         return this.wrap;
30045     },
30046
30047     // private
30048     onRender : function(ct, position){
30049         
30050         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30051         //if(this.inputValue !== undefined){
30052         this.wrap = this.el.wrap();
30053         
30054         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30055         
30056         if (this.bodyStyle) {
30057             this.viewEl.applyStyles(this.bodyStyle);
30058         }
30059         //this.viewEl.setStyle('padding', '2px');
30060         
30061         this.setValue(this.value);
30062         
30063     },
30064 /*
30065     // private
30066     initValue : Roo.emptyFn,
30067
30068   */
30069
30070         // private
30071     onClick : function(){
30072         
30073     },
30074
30075     /**
30076      * Sets the checked state of the checkbox.
30077      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30078      */
30079     setValue : function(v){
30080         this.value = v;
30081         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30082         // this might be called before we have a dom element..
30083         if (!this.viewEl) {
30084             return;
30085         }
30086         this.viewEl.dom.innerHTML = html;
30087         Roo.form.DisplayField.superclass.setValue.call(this, v);
30088
30089     }
30090 });/*
30091  * 
30092  * Licence- LGPL
30093  * 
30094  */
30095
30096 /**
30097  * @class Roo.form.DayPicker
30098  * @extends Roo.form.Field
30099  * A Day picker show [M] [T] [W] ....
30100  * @constructor
30101  * Creates a new Day Picker
30102  * @param {Object} config Configuration options
30103  */
30104 Roo.form.DayPicker= function(config){
30105     Roo.form.DayPicker.superclass.constructor.call(this, config);
30106      
30107 };
30108
30109 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30110     /**
30111      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30112      */
30113     focusClass : undefined,
30114     /**
30115      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30116      */
30117     fieldClass: "x-form-field",
30118    
30119     /**
30120      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30121      * {tag: "input", type: "checkbox", autocomplete: "off"})
30122      */
30123     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30124     
30125    
30126     actionMode : 'viewEl', 
30127     //
30128     // private
30129  
30130     inputType : 'hidden',
30131     
30132      
30133     inputElement: false, // real input element?
30134     basedOn: false, // ????
30135     
30136     isFormField: true, // not sure where this is needed!!!!
30137
30138     onResize : function(){
30139         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30140         if(!this.boxLabel){
30141             this.el.alignTo(this.wrap, 'c-c');
30142         }
30143     },
30144
30145     initEvents : function(){
30146         Roo.form.Checkbox.superclass.initEvents.call(this);
30147         this.el.on("click", this.onClick,  this);
30148         this.el.on("change", this.onClick,  this);
30149     },
30150
30151
30152     getResizeEl : function(){
30153         return this.wrap;
30154     },
30155
30156     getPositionEl : function(){
30157         return this.wrap;
30158     },
30159
30160     
30161     // private
30162     onRender : function(ct, position){
30163         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30164        
30165         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30166         
30167         var r1 = '<table><tr>';
30168         var r2 = '<tr class="x-form-daypick-icons">';
30169         for (var i=0; i < 7; i++) {
30170             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30171             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30172         }
30173         
30174         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30175         viewEl.select('img').on('click', this.onClick, this);
30176         this.viewEl = viewEl;   
30177         
30178         
30179         // this will not work on Chrome!!!
30180         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30181         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30182         
30183         
30184           
30185
30186     },
30187
30188     // private
30189     initValue : Roo.emptyFn,
30190
30191     /**
30192      * Returns the checked state of the checkbox.
30193      * @return {Boolean} True if checked, else false
30194      */
30195     getValue : function(){
30196         return this.el.dom.value;
30197         
30198     },
30199
30200         // private
30201     onClick : function(e){ 
30202         //this.setChecked(!this.checked);
30203         Roo.get(e.target).toggleClass('x-menu-item-checked');
30204         this.refreshValue();
30205         //if(this.el.dom.checked != this.checked){
30206         //    this.setValue(this.el.dom.checked);
30207        // }
30208     },
30209     
30210     // private
30211     refreshValue : function()
30212     {
30213         var val = '';
30214         this.viewEl.select('img',true).each(function(e,i,n)  {
30215             val += e.is(".x-menu-item-checked") ? String(n) : '';
30216         });
30217         this.setValue(val, true);
30218     },
30219
30220     /**
30221      * Sets the checked state of the checkbox.
30222      * On is always based on a string comparison between inputValue and the param.
30223      * @param {Boolean/String} value - the value to set 
30224      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30225      */
30226     setValue : function(v,suppressEvent){
30227         if (!this.el.dom) {
30228             return;
30229         }
30230         var old = this.el.dom.value ;
30231         this.el.dom.value = v;
30232         if (suppressEvent) {
30233             return ;
30234         }
30235          
30236         // update display..
30237         this.viewEl.select('img',true).each(function(e,i,n)  {
30238             
30239             var on = e.is(".x-menu-item-checked");
30240             var newv = v.indexOf(String(n)) > -1;
30241             if (on != newv) {
30242                 e.toggleClass('x-menu-item-checked');
30243             }
30244             
30245         });
30246         
30247         
30248         this.fireEvent('change', this, v, old);
30249         
30250         
30251     },
30252    
30253     // handle setting of hidden value by some other method!!?!?
30254     setFromHidden: function()
30255     {
30256         if(!this.el){
30257             return;
30258         }
30259         //console.log("SET FROM HIDDEN");
30260         //alert('setFrom hidden');
30261         this.setValue(this.el.dom.value);
30262     },
30263     
30264     onDestroy : function()
30265     {
30266         if(this.viewEl){
30267             Roo.get(this.viewEl).remove();
30268         }
30269          
30270         Roo.form.DayPicker.superclass.onDestroy.call(this);
30271     }
30272
30273 });/*
30274  * RooJS Library 1.1.1
30275  * Copyright(c) 2008-2011  Alan Knowles
30276  *
30277  * License - LGPL
30278  */
30279  
30280
30281 /**
30282  * @class Roo.form.ComboCheck
30283  * @extends Roo.form.ComboBox
30284  * A combobox for multiple select items.
30285  *
30286  * FIXME - could do with a reset button..
30287  * 
30288  * @constructor
30289  * Create a new ComboCheck
30290  * @param {Object} config Configuration options
30291  */
30292 Roo.form.ComboCheck = function(config){
30293     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30294     // should verify some data...
30295     // like
30296     // hiddenName = required..
30297     // displayField = required
30298     // valudField == required
30299     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30300     var _t = this;
30301     Roo.each(req, function(e) {
30302         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30303             throw "Roo.form.ComboCheck : missing value for: " + e;
30304         }
30305     });
30306     
30307     
30308 };
30309
30310 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30311      
30312      
30313     editable : false,
30314      
30315     selectedClass: 'x-menu-item-checked', 
30316     
30317     // private
30318     onRender : function(ct, position){
30319         var _t = this;
30320         
30321         
30322         
30323         if(!this.tpl){
30324             var cls = 'x-combo-list';
30325
30326             
30327             this.tpl =  new Roo.Template({
30328                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30329                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30330                    '<span>{' + this.displayField + '}</span>' +
30331                     '</div>' 
30332                 
30333             });
30334         }
30335  
30336         
30337         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30338         this.view.singleSelect = false;
30339         this.view.multiSelect = true;
30340         this.view.toggleSelect = true;
30341         this.pageTb.add(new Roo.Toolbar.Fill(), {
30342             
30343             text: 'Done',
30344             handler: function()
30345             {
30346                 _t.collapse();
30347             }
30348         });
30349     },
30350     
30351     onViewOver : function(e, t){
30352         // do nothing...
30353         return;
30354         
30355     },
30356     
30357     onViewClick : function(doFocus,index){
30358         return;
30359         
30360     },
30361     select: function () {
30362         //Roo.log("SELECT CALLED");
30363     },
30364      
30365     selectByValue : function(xv, scrollIntoView){
30366         var ar = this.getValueArray();
30367         var sels = [];
30368         
30369         Roo.each(ar, function(v) {
30370             if(v === undefined || v === null){
30371                 return;
30372             }
30373             var r = this.findRecord(this.valueField, v);
30374             if(r){
30375                 sels.push(this.store.indexOf(r))
30376                 
30377             }
30378         },this);
30379         this.view.select(sels);
30380         return false;
30381     },
30382     
30383     
30384     
30385     onSelect : function(record, index){
30386        // Roo.log("onselect Called");
30387        // this is only called by the clear button now..
30388         this.view.clearSelections();
30389         this.setValue('[]');
30390         if (this.value != this.valueBefore) {
30391             this.fireEvent('change', this, this.value, this.valueBefore);
30392         }
30393     },
30394     getValueArray : function()
30395     {
30396         var ar = [] ;
30397         
30398         try {
30399             //Roo.log(this.value);
30400             if (typeof(this.value) == 'undefined') {
30401                 return [];
30402             }
30403             var ar = Roo.decode(this.value);
30404             return  ar instanceof Array ? ar : []; //?? valid?
30405             
30406         } catch(e) {
30407             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30408             return [];
30409         }
30410          
30411     },
30412     expand : function ()
30413     {
30414         Roo.form.ComboCheck.superclass.expand.call(this);
30415         this.valueBefore = this.value;
30416         
30417
30418     },
30419     
30420     collapse : function(){
30421         Roo.form.ComboCheck.superclass.collapse.call(this);
30422         var sl = this.view.getSelectedIndexes();
30423         var st = this.store;
30424         var nv = [];
30425         var tv = [];
30426         var r;
30427         Roo.each(sl, function(i) {
30428             r = st.getAt(i);
30429             nv.push(r.get(this.valueField));
30430         },this);
30431         this.setValue(Roo.encode(nv));
30432         if (this.value != this.valueBefore) {
30433
30434             this.fireEvent('change', this, this.value, this.valueBefore);
30435         }
30436         
30437     },
30438     
30439     setValue : function(v){
30440         // Roo.log(v);
30441         this.value = v;
30442         
30443         var vals = this.getValueArray();
30444         var tv = [];
30445         Roo.each(vals, function(k) {
30446             var r = this.findRecord(this.valueField, k);
30447             if(r){
30448                 tv.push(r.data[this.displayField]);
30449             }else if(this.valueNotFoundText !== undefined){
30450                 tv.push( this.valueNotFoundText );
30451             }
30452         },this);
30453        // Roo.log(tv);
30454         
30455         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30456         this.hiddenField.value = v;
30457         this.value = v;
30458     }
30459     
30460 });//<script type="text/javasscript">
30461  
30462
30463 /**
30464  * @class Roo.DDView
30465  * A DnD enabled version of Roo.View.
30466  * @param {Element/String} container The Element in which to create the View.
30467  * @param {String} tpl The template string used to create the markup for each element of the View
30468  * @param {Object} config The configuration properties. These include all the config options of
30469  * {@link Roo.View} plus some specific to this class.<br>
30470  * <p>
30471  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30472  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30473  * <p>
30474  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30475 .x-view-drag-insert-above {
30476         border-top:1px dotted #3366cc;
30477 }
30478 .x-view-drag-insert-below {
30479         border-bottom:1px dotted #3366cc;
30480 }
30481 </code></pre>
30482  * 
30483  */
30484  
30485 Roo.DDView = function(container, tpl, config) {
30486     Roo.DDView.superclass.constructor.apply(this, arguments);
30487     this.getEl().setStyle("outline", "0px none");
30488     this.getEl().unselectable();
30489     if (this.dragGroup) {
30490                 this.setDraggable(this.dragGroup.split(","));
30491     }
30492     if (this.dropGroup) {
30493                 this.setDroppable(this.dropGroup.split(","));
30494     }
30495     if (this.deletable) {
30496         this.setDeletable();
30497     }
30498     this.isDirtyFlag = false;
30499         this.addEvents({
30500                 "drop" : true
30501         });
30502 };
30503
30504 Roo.extend(Roo.DDView, Roo.View, {
30505 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30506 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30507 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30508 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30509
30510         isFormField: true,
30511
30512         reset: Roo.emptyFn,
30513         
30514         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30515
30516         validate: function() {
30517                 return true;
30518         },
30519         
30520         destroy: function() {
30521                 this.purgeListeners();
30522                 this.getEl.removeAllListeners();
30523                 this.getEl().remove();
30524                 if (this.dragZone) {
30525                         if (this.dragZone.destroy) {
30526                                 this.dragZone.destroy();
30527                         }
30528                 }
30529                 if (this.dropZone) {
30530                         if (this.dropZone.destroy) {
30531                                 this.dropZone.destroy();
30532                         }
30533                 }
30534         },
30535
30536 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30537         getName: function() {
30538                 return this.name;
30539         },
30540
30541 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30542         setValue: function(v) {
30543                 if (!this.store) {
30544                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30545                 }
30546                 var data = {};
30547                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30548                 this.store.proxy = new Roo.data.MemoryProxy(data);
30549                 this.store.load();
30550         },
30551
30552 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30553         getValue: function() {
30554                 var result = '(';
30555                 this.store.each(function(rec) {
30556                         result += rec.id + ',';
30557                 });
30558                 return result.substr(0, result.length - 1) + ')';
30559         },
30560         
30561         getIds: function() {
30562                 var i = 0, result = new Array(this.store.getCount());
30563                 this.store.each(function(rec) {
30564                         result[i++] = rec.id;
30565                 });
30566                 return result;
30567         },
30568         
30569         isDirty: function() {
30570                 return this.isDirtyFlag;
30571         },
30572
30573 /**
30574  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30575  *      whole Element becomes the target, and this causes the drop gesture to append.
30576  */
30577     getTargetFromEvent : function(e) {
30578                 var target = e.getTarget();
30579                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30580                 target = target.parentNode;
30581                 }
30582                 if (!target) {
30583                         target = this.el.dom.lastChild || this.el.dom;
30584                 }
30585                 return target;
30586     },
30587
30588 /**
30589  *      Create the drag data which consists of an object which has the property "ddel" as
30590  *      the drag proxy element. 
30591  */
30592     getDragData : function(e) {
30593         var target = this.findItemFromChild(e.getTarget());
30594                 if(target) {
30595                         this.handleSelection(e);
30596                         var selNodes = this.getSelectedNodes();
30597             var dragData = {
30598                 source: this,
30599                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30600                 nodes: selNodes,
30601                 records: []
30602                         };
30603                         var selectedIndices = this.getSelectedIndexes();
30604                         for (var i = 0; i < selectedIndices.length; i++) {
30605                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30606                         }
30607                         if (selNodes.length == 1) {
30608                                 dragData.ddel = target.cloneNode(true); // the div element
30609                         } else {
30610                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30611                                 div.className = 'multi-proxy';
30612                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30613                                         div.appendChild(selNodes[i].cloneNode(true));
30614                                 }
30615                                 dragData.ddel = div;
30616                         }
30617             //console.log(dragData)
30618             //console.log(dragData.ddel.innerHTML)
30619                         return dragData;
30620                 }
30621         //console.log('nodragData')
30622                 return false;
30623     },
30624     
30625 /**     Specify to which ddGroup items in this DDView may be dragged. */
30626     setDraggable: function(ddGroup) {
30627         if (ddGroup instanceof Array) {
30628                 Roo.each(ddGroup, this.setDraggable, this);
30629                 return;
30630         }
30631         if (this.dragZone) {
30632                 this.dragZone.addToGroup(ddGroup);
30633         } else {
30634                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30635                                 containerScroll: true,
30636                                 ddGroup: ddGroup 
30637
30638                         });
30639 //                      Draggability implies selection. DragZone's mousedown selects the element.
30640                         if (!this.multiSelect) { this.singleSelect = true; }
30641
30642 //                      Wire the DragZone's handlers up to methods in *this*
30643                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30644                 }
30645     },
30646
30647 /**     Specify from which ddGroup this DDView accepts drops. */
30648     setDroppable: function(ddGroup) {
30649         if (ddGroup instanceof Array) {
30650                 Roo.each(ddGroup, this.setDroppable, this);
30651                 return;
30652         }
30653         if (this.dropZone) {
30654                 this.dropZone.addToGroup(ddGroup);
30655         } else {
30656                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30657                                 containerScroll: true,
30658                                 ddGroup: ddGroup
30659                         });
30660
30661 //                      Wire the DropZone's handlers up to methods in *this*
30662                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30663                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30664                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30665                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30666                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30667                 }
30668     },
30669
30670 /**     Decide whether to drop above or below a View node. */
30671     getDropPoint : function(e, n, dd){
30672         if (n == this.el.dom) { return "above"; }
30673                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30674                 var c = t + (b - t) / 2;
30675                 var y = Roo.lib.Event.getPageY(e);
30676                 if(y <= c) {
30677                         return "above";
30678                 }else{
30679                         return "below";
30680                 }
30681     },
30682
30683     onNodeEnter : function(n, dd, e, data){
30684                 return false;
30685     },
30686     
30687     onNodeOver : function(n, dd, e, data){
30688                 var pt = this.getDropPoint(e, n, dd);
30689                 // set the insert point style on the target node
30690                 var dragElClass = this.dropNotAllowed;
30691                 if (pt) {
30692                         var targetElClass;
30693                         if (pt == "above"){
30694                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30695                                 targetElClass = "x-view-drag-insert-above";
30696                         } else {
30697                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30698                                 targetElClass = "x-view-drag-insert-below";
30699                         }
30700                         if (this.lastInsertClass != targetElClass){
30701                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30702                                 this.lastInsertClass = targetElClass;
30703                         }
30704                 }
30705                 return dragElClass;
30706         },
30707
30708     onNodeOut : function(n, dd, e, data){
30709                 this.removeDropIndicators(n);
30710     },
30711
30712     onNodeDrop : function(n, dd, e, data){
30713         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30714                 return false;
30715         }
30716         var pt = this.getDropPoint(e, n, dd);
30717                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30718                 if (pt == "below") { insertAt++; }
30719                 for (var i = 0; i < data.records.length; i++) {
30720                         var r = data.records[i];
30721                         var dup = this.store.getById(r.id);
30722                         if (dup && (dd != this.dragZone)) {
30723                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30724                         } else {
30725                                 if (data.copy) {
30726                                         this.store.insert(insertAt++, r.copy());
30727                                 } else {
30728                                         data.source.isDirtyFlag = true;
30729                                         r.store.remove(r);
30730                                         this.store.insert(insertAt++, r);
30731                                 }
30732                                 this.isDirtyFlag = true;
30733                         }
30734                 }
30735                 this.dragZone.cachedTarget = null;
30736                 return true;
30737     },
30738
30739     removeDropIndicators : function(n){
30740                 if(n){
30741                         Roo.fly(n).removeClass([
30742                                 "x-view-drag-insert-above",
30743                                 "x-view-drag-insert-below"]);
30744                         this.lastInsertClass = "_noclass";
30745                 }
30746     },
30747
30748 /**
30749  *      Utility method. Add a delete option to the DDView's context menu.
30750  *      @param {String} imageUrl The URL of the "delete" icon image.
30751  */
30752         setDeletable: function(imageUrl) {
30753                 if (!this.singleSelect && !this.multiSelect) {
30754                         this.singleSelect = true;
30755                 }
30756                 var c = this.getContextMenu();
30757                 this.contextMenu.on("itemclick", function(item) {
30758                         switch (item.id) {
30759                                 case "delete":
30760                                         this.remove(this.getSelectedIndexes());
30761                                         break;
30762                         }
30763                 }, this);
30764                 this.contextMenu.add({
30765                         icon: imageUrl,
30766                         id: "delete",
30767                         text: 'Delete'
30768                 });
30769         },
30770         
30771 /**     Return the context menu for this DDView. */
30772         getContextMenu: function() {
30773                 if (!this.contextMenu) {
30774 //                      Create the View's context menu
30775                         this.contextMenu = new Roo.menu.Menu({
30776                                 id: this.id + "-contextmenu"
30777                         });
30778                         this.el.on("contextmenu", this.showContextMenu, this);
30779                 }
30780                 return this.contextMenu;
30781         },
30782         
30783         disableContextMenu: function() {
30784                 if (this.contextMenu) {
30785                         this.el.un("contextmenu", this.showContextMenu, this);
30786                 }
30787         },
30788
30789         showContextMenu: function(e, item) {
30790         item = this.findItemFromChild(e.getTarget());
30791                 if (item) {
30792                         e.stopEvent();
30793                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30794                         this.contextMenu.showAt(e.getXY());
30795             }
30796     },
30797
30798 /**
30799  *      Remove {@link Roo.data.Record}s at the specified indices.
30800  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30801  */
30802     remove: function(selectedIndices) {
30803                 selectedIndices = [].concat(selectedIndices);
30804                 for (var i = 0; i < selectedIndices.length; i++) {
30805                         var rec = this.store.getAt(selectedIndices[i]);
30806                         this.store.remove(rec);
30807                 }
30808     },
30809
30810 /**
30811  *      Double click fires the event, but also, if this is draggable, and there is only one other
30812  *      related DropZone, it transfers the selected node.
30813  */
30814     onDblClick : function(e){
30815         var item = this.findItemFromChild(e.getTarget());
30816         if(item){
30817             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30818                 return false;
30819             }
30820             if (this.dragGroup) {
30821                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30822                     while (targets.indexOf(this.dropZone) > -1) {
30823                             targets.remove(this.dropZone);
30824                                 }
30825                     if (targets.length == 1) {
30826                                         this.dragZone.cachedTarget = null;
30827                         var el = Roo.get(targets[0].getEl());
30828                         var box = el.getBox(true);
30829                         targets[0].onNodeDrop(el.dom, {
30830                                 target: el.dom,
30831                                 xy: [box.x, box.y + box.height - 1]
30832                         }, null, this.getDragData(e));
30833                     }
30834                 }
30835         }
30836     },
30837     
30838     handleSelection: function(e) {
30839                 this.dragZone.cachedTarget = null;
30840         var item = this.findItemFromChild(e.getTarget());
30841         if (!item) {
30842                 this.clearSelections(true);
30843                 return;
30844         }
30845                 if (item && (this.multiSelect || this.singleSelect)){
30846                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30847                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30848                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30849                                 this.unselect(item);
30850                         } else {
30851                                 this.select(item, this.multiSelect && e.ctrlKey);
30852                                 this.lastSelection = item;
30853                         }
30854                 }
30855     },
30856
30857     onItemClick : function(item, index, e){
30858                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30859                         return false;
30860                 }
30861                 return true;
30862     },
30863
30864     unselect : function(nodeInfo, suppressEvent){
30865                 var node = this.getNode(nodeInfo);
30866                 if(node && this.isSelected(node)){
30867                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30868                                 Roo.fly(node).removeClass(this.selectedClass);
30869                                 this.selections.remove(node);
30870                                 if(!suppressEvent){
30871                                         this.fireEvent("selectionchange", this, this.selections);
30872                                 }
30873                         }
30874                 }
30875     }
30876 });
30877 /*
30878  * Based on:
30879  * Ext JS Library 1.1.1
30880  * Copyright(c) 2006-2007, Ext JS, LLC.
30881  *
30882  * Originally Released Under LGPL - original licence link has changed is not relivant.
30883  *
30884  * Fork - LGPL
30885  * <script type="text/javascript">
30886  */
30887  
30888 /**
30889  * @class Roo.LayoutManager
30890  * @extends Roo.util.Observable
30891  * Base class for layout managers.
30892  */
30893 Roo.LayoutManager = function(container, config){
30894     Roo.LayoutManager.superclass.constructor.call(this);
30895     this.el = Roo.get(container);
30896     // ie scrollbar fix
30897     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30898         document.body.scroll = "no";
30899     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30900         this.el.position('relative');
30901     }
30902     this.id = this.el.id;
30903     this.el.addClass("x-layout-container");
30904     /** false to disable window resize monitoring @type Boolean */
30905     this.monitorWindowResize = true;
30906     this.regions = {};
30907     this.addEvents({
30908         /**
30909          * @event layout
30910          * Fires when a layout is performed. 
30911          * @param {Roo.LayoutManager} this
30912          */
30913         "layout" : true,
30914         /**
30915          * @event regionresized
30916          * Fires when the user resizes a region. 
30917          * @param {Roo.LayoutRegion} region The resized region
30918          * @param {Number} newSize The new size (width for east/west, height for north/south)
30919          */
30920         "regionresized" : true,
30921         /**
30922          * @event regioncollapsed
30923          * Fires when a region is collapsed. 
30924          * @param {Roo.LayoutRegion} region The collapsed region
30925          */
30926         "regioncollapsed" : true,
30927         /**
30928          * @event regionexpanded
30929          * Fires when a region is expanded.  
30930          * @param {Roo.LayoutRegion} region The expanded region
30931          */
30932         "regionexpanded" : true
30933     });
30934     this.updating = false;
30935     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30936 };
30937
30938 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30939     /**
30940      * Returns true if this layout is currently being updated
30941      * @return {Boolean}
30942      */
30943     isUpdating : function(){
30944         return this.updating; 
30945     },
30946     
30947     /**
30948      * Suspend the LayoutManager from doing auto-layouts while
30949      * making multiple add or remove calls
30950      */
30951     beginUpdate : function(){
30952         this.updating = true;    
30953     },
30954     
30955     /**
30956      * Restore auto-layouts and optionally disable the manager from performing a layout
30957      * @param {Boolean} noLayout true to disable a layout update 
30958      */
30959     endUpdate : function(noLayout){
30960         this.updating = false;
30961         if(!noLayout){
30962             this.layout();
30963         }    
30964     },
30965     
30966     layout: function(){
30967         
30968     },
30969     
30970     onRegionResized : function(region, newSize){
30971         this.fireEvent("regionresized", region, newSize);
30972         this.layout();
30973     },
30974     
30975     onRegionCollapsed : function(region){
30976         this.fireEvent("regioncollapsed", region);
30977     },
30978     
30979     onRegionExpanded : function(region){
30980         this.fireEvent("regionexpanded", region);
30981     },
30982         
30983     /**
30984      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30985      * performs box-model adjustments.
30986      * @return {Object} The size as an object {width: (the width), height: (the height)}
30987      */
30988     getViewSize : function(){
30989         var size;
30990         if(this.el.dom != document.body){
30991             size = this.el.getSize();
30992         }else{
30993             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30994         }
30995         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30996         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30997         return size;
30998     },
30999     
31000     /**
31001      * Returns the Element this layout is bound to.
31002      * @return {Roo.Element}
31003      */
31004     getEl : function(){
31005         return this.el;
31006     },
31007     
31008     /**
31009      * Returns the specified region.
31010      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31011      * @return {Roo.LayoutRegion}
31012      */
31013     getRegion : function(target){
31014         return this.regions[target.toLowerCase()];
31015     },
31016     
31017     onWindowResize : function(){
31018         if(this.monitorWindowResize){
31019             this.layout();
31020         }
31021     }
31022 });/*
31023  * Based on:
31024  * Ext JS Library 1.1.1
31025  * Copyright(c) 2006-2007, Ext JS, LLC.
31026  *
31027  * Originally Released Under LGPL - original licence link has changed is not relivant.
31028  *
31029  * Fork - LGPL
31030  * <script type="text/javascript">
31031  */
31032 /**
31033  * @class Roo.BorderLayout
31034  * @extends Roo.LayoutManager
31035  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31036  * please see: <br><br>
31037  * <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>
31038  * <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>
31039  * Example:
31040  <pre><code>
31041  var layout = new Roo.BorderLayout(document.body, {
31042     north: {
31043         initialSize: 25,
31044         titlebar: false
31045     },
31046     west: {
31047         split:true,
31048         initialSize: 200,
31049         minSize: 175,
31050         maxSize: 400,
31051         titlebar: true,
31052         collapsible: true
31053     },
31054     east: {
31055         split:true,
31056         initialSize: 202,
31057         minSize: 175,
31058         maxSize: 400,
31059         titlebar: true,
31060         collapsible: true
31061     },
31062     south: {
31063         split:true,
31064         initialSize: 100,
31065         minSize: 100,
31066         maxSize: 200,
31067         titlebar: true,
31068         collapsible: true
31069     },
31070     center: {
31071         titlebar: true,
31072         autoScroll:true,
31073         resizeTabs: true,
31074         minTabWidth: 50,
31075         preferredTabWidth: 150
31076     }
31077 });
31078
31079 // shorthand
31080 var CP = Roo.ContentPanel;
31081
31082 layout.beginUpdate();
31083 layout.add("north", new CP("north", "North"));
31084 layout.add("south", new CP("south", {title: "South", closable: true}));
31085 layout.add("west", new CP("west", {title: "West"}));
31086 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31087 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31088 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31089 layout.getRegion("center").showPanel("center1");
31090 layout.endUpdate();
31091 </code></pre>
31092
31093 <b>The container the layout is rendered into can be either the body element or any other element.
31094 If it is not the body element, the container needs to either be an absolute positioned element,
31095 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31096 the container size if it is not the body element.</b>
31097
31098 * @constructor
31099 * Create a new BorderLayout
31100 * @param {String/HTMLElement/Element} container The container this layout is bound to
31101 * @param {Object} config Configuration options
31102  */
31103 Roo.BorderLayout = function(container, config){
31104     config = config || {};
31105     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31106     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31107     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31108         var target = this.factory.validRegions[i];
31109         if(config[target]){
31110             this.addRegion(target, config[target]);
31111         }
31112     }
31113 };
31114
31115 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31116     /**
31117      * Creates and adds a new region if it doesn't already exist.
31118      * @param {String} target The target region key (north, south, east, west or center).
31119      * @param {Object} config The regions config object
31120      * @return {BorderLayoutRegion} The new region
31121      */
31122     addRegion : function(target, config){
31123         if(!this.regions[target]){
31124             var r = this.factory.create(target, this, config);
31125             this.bindRegion(target, r);
31126         }
31127         return this.regions[target];
31128     },
31129
31130     // private (kinda)
31131     bindRegion : function(name, r){
31132         this.regions[name] = r;
31133         r.on("visibilitychange", this.layout, this);
31134         r.on("paneladded", this.layout, this);
31135         r.on("panelremoved", this.layout, this);
31136         r.on("invalidated", this.layout, this);
31137         r.on("resized", this.onRegionResized, this);
31138         r.on("collapsed", this.onRegionCollapsed, this);
31139         r.on("expanded", this.onRegionExpanded, this);
31140     },
31141
31142     /**
31143      * Performs a layout update.
31144      */
31145     layout : function(){
31146         if(this.updating) return;
31147         var size = this.getViewSize();
31148         var w = size.width;
31149         var h = size.height;
31150         var centerW = w;
31151         var centerH = h;
31152         var centerY = 0;
31153         var centerX = 0;
31154         //var x = 0, y = 0;
31155
31156         var rs = this.regions;
31157         var north = rs["north"];
31158         var south = rs["south"]; 
31159         var west = rs["west"];
31160         var east = rs["east"];
31161         var center = rs["center"];
31162         //if(this.hideOnLayout){ // not supported anymore
31163             //c.el.setStyle("display", "none");
31164         //}
31165         if(north && north.isVisible()){
31166             var b = north.getBox();
31167             var m = north.getMargins();
31168             b.width = w - (m.left+m.right);
31169             b.x = m.left;
31170             b.y = m.top;
31171             centerY = b.height + b.y + m.bottom;
31172             centerH -= centerY;
31173             north.updateBox(this.safeBox(b));
31174         }
31175         if(south && south.isVisible()){
31176             var b = south.getBox();
31177             var m = south.getMargins();
31178             b.width = w - (m.left+m.right);
31179             b.x = m.left;
31180             var totalHeight = (b.height + m.top + m.bottom);
31181             b.y = h - totalHeight + m.top;
31182             centerH -= totalHeight;
31183             south.updateBox(this.safeBox(b));
31184         }
31185         if(west && west.isVisible()){
31186             var b = west.getBox();
31187             var m = west.getMargins();
31188             b.height = centerH - (m.top+m.bottom);
31189             b.x = m.left;
31190             b.y = centerY + m.top;
31191             var totalWidth = (b.width + m.left + m.right);
31192             centerX += totalWidth;
31193             centerW -= totalWidth;
31194             west.updateBox(this.safeBox(b));
31195         }
31196         if(east && east.isVisible()){
31197             var b = east.getBox();
31198             var m = east.getMargins();
31199             b.height = centerH - (m.top+m.bottom);
31200             var totalWidth = (b.width + m.left + m.right);
31201             b.x = w - totalWidth + m.left;
31202             b.y = centerY + m.top;
31203             centerW -= totalWidth;
31204             east.updateBox(this.safeBox(b));
31205         }
31206         if(center){
31207             var m = center.getMargins();
31208             var centerBox = {
31209                 x: centerX + m.left,
31210                 y: centerY + m.top,
31211                 width: centerW - (m.left+m.right),
31212                 height: centerH - (m.top+m.bottom)
31213             };
31214             //if(this.hideOnLayout){
31215                 //center.el.setStyle("display", "block");
31216             //}
31217             center.updateBox(this.safeBox(centerBox));
31218         }
31219         this.el.repaint();
31220         this.fireEvent("layout", this);
31221     },
31222
31223     // private
31224     safeBox : function(box){
31225         box.width = Math.max(0, box.width);
31226         box.height = Math.max(0, box.height);
31227         return box;
31228     },
31229
31230     /**
31231      * Adds a ContentPanel (or subclass) to this layout.
31232      * @param {String} target The target region key (north, south, east, west or center).
31233      * @param {Roo.ContentPanel} panel The panel to add
31234      * @return {Roo.ContentPanel} The added panel
31235      */
31236     add : function(target, panel){
31237          
31238         target = target.toLowerCase();
31239         return this.regions[target].add(panel);
31240     },
31241
31242     /**
31243      * Remove a ContentPanel (or subclass) to this layout.
31244      * @param {String} target The target region key (north, south, east, west or center).
31245      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31246      * @return {Roo.ContentPanel} The removed panel
31247      */
31248     remove : function(target, panel){
31249         target = target.toLowerCase();
31250         return this.regions[target].remove(panel);
31251     },
31252
31253     /**
31254      * Searches all regions for a panel with the specified id
31255      * @param {String} panelId
31256      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31257      */
31258     findPanel : function(panelId){
31259         var rs = this.regions;
31260         for(var target in rs){
31261             if(typeof rs[target] != "function"){
31262                 var p = rs[target].getPanel(panelId);
31263                 if(p){
31264                     return p;
31265                 }
31266             }
31267         }
31268         return null;
31269     },
31270
31271     /**
31272      * Searches all regions for a panel with the specified id and activates (shows) it.
31273      * @param {String/ContentPanel} panelId The panels id or the panel itself
31274      * @return {Roo.ContentPanel} The shown panel or null
31275      */
31276     showPanel : function(panelId) {
31277       var rs = this.regions;
31278       for(var target in rs){
31279          var r = rs[target];
31280          if(typeof r != "function"){
31281             if(r.hasPanel(panelId)){
31282                return r.showPanel(panelId);
31283             }
31284          }
31285       }
31286       return null;
31287    },
31288
31289    /**
31290      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31291      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31292      */
31293     restoreState : function(provider){
31294         if(!provider){
31295             provider = Roo.state.Manager;
31296         }
31297         var sm = new Roo.LayoutStateManager();
31298         sm.init(this, provider);
31299     },
31300
31301     /**
31302      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31303      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31304      * a valid ContentPanel config object.  Example:
31305      * <pre><code>
31306 // Create the main layout
31307 var layout = new Roo.BorderLayout('main-ct', {
31308     west: {
31309         split:true,
31310         minSize: 175,
31311         titlebar: true
31312     },
31313     center: {
31314         title:'Components'
31315     }
31316 }, 'main-ct');
31317
31318 // Create and add multiple ContentPanels at once via configs
31319 layout.batchAdd({
31320    west: {
31321        id: 'source-files',
31322        autoCreate:true,
31323        title:'Ext Source Files',
31324        autoScroll:true,
31325        fitToFrame:true
31326    },
31327    center : {
31328        el: cview,
31329        autoScroll:true,
31330        fitToFrame:true,
31331        toolbar: tb,
31332        resizeEl:'cbody'
31333    }
31334 });
31335 </code></pre>
31336      * @param {Object} regions An object containing ContentPanel configs by region name
31337      */
31338     batchAdd : function(regions){
31339         this.beginUpdate();
31340         for(var rname in regions){
31341             var lr = this.regions[rname];
31342             if(lr){
31343                 this.addTypedPanels(lr, regions[rname]);
31344             }
31345         }
31346         this.endUpdate();
31347     },
31348
31349     // private
31350     addTypedPanels : function(lr, ps){
31351         if(typeof ps == 'string'){
31352             lr.add(new Roo.ContentPanel(ps));
31353         }
31354         else if(ps instanceof Array){
31355             for(var i =0, len = ps.length; i < len; i++){
31356                 this.addTypedPanels(lr, ps[i]);
31357             }
31358         }
31359         else if(!ps.events){ // raw config?
31360             var el = ps.el;
31361             delete ps.el; // prevent conflict
31362             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31363         }
31364         else {  // panel object assumed!
31365             lr.add(ps);
31366         }
31367     },
31368     /**
31369      * Adds a xtype elements to the layout.
31370      * <pre><code>
31371
31372 layout.addxtype({
31373        xtype : 'ContentPanel',
31374        region: 'west',
31375        items: [ .... ]
31376    }
31377 );
31378
31379 layout.addxtype({
31380         xtype : 'NestedLayoutPanel',
31381         region: 'west',
31382         layout: {
31383            center: { },
31384            west: { }   
31385         },
31386         items : [ ... list of content panels or nested layout panels.. ]
31387    }
31388 );
31389 </code></pre>
31390      * @param {Object} cfg Xtype definition of item to add.
31391      */
31392     addxtype : function(cfg)
31393     {
31394         // basically accepts a pannel...
31395         // can accept a layout region..!?!?
31396         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31397         
31398         if (!cfg.xtype.match(/Panel$/)) {
31399             return false;
31400         }
31401         var ret = false;
31402         
31403         if (typeof(cfg.region) == 'undefined') {
31404             Roo.log("Failed to add Panel, region was not set");
31405             Roo.log(cfg);
31406             return false;
31407         }
31408         var region = cfg.region;
31409         delete cfg.region;
31410         
31411           
31412         var xitems = [];
31413         if (cfg.items) {
31414             xitems = cfg.items;
31415             delete cfg.items;
31416         }
31417         var nb = false;
31418         
31419         switch(cfg.xtype) 
31420         {
31421             case 'ContentPanel':  // ContentPanel (el, cfg)
31422             case 'ScrollPanel':  // ContentPanel (el, cfg)
31423                 if(cfg.autoCreate) {
31424                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31425                 } else {
31426                     var el = this.el.createChild();
31427                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31428                 }
31429                 
31430                 this.add(region, ret);
31431                 break;
31432             
31433             
31434             case 'TreePanel': // our new panel!
31435                 cfg.el = this.el.createChild();
31436                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31437                 this.add(region, ret);
31438                 break;
31439             
31440             case 'NestedLayoutPanel': 
31441                 // create a new Layout (which is  a Border Layout...
31442                 var el = this.el.createChild();
31443                 var clayout = cfg.layout;
31444                 delete cfg.layout;
31445                 clayout.items   = clayout.items  || [];
31446                 // replace this exitems with the clayout ones..
31447                 xitems = clayout.items;
31448                  
31449                 
31450                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31451                     cfg.background = false;
31452                 }
31453                 var layout = new Roo.BorderLayout(el, clayout);
31454                 
31455                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31456                 //console.log('adding nested layout panel '  + cfg.toSource());
31457                 this.add(region, ret);
31458                 nb = {}; /// find first...
31459                 break;
31460                 
31461             case 'GridPanel': 
31462             
31463                 // needs grid and region
31464                 
31465                 //var el = this.getRegion(region).el.createChild();
31466                 var el = this.el.createChild();
31467                 // create the grid first...
31468                 
31469                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31470                 delete cfg.grid;
31471                 if (region == 'center' && this.active ) {
31472                     cfg.background = false;
31473                 }
31474                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31475                 
31476                 this.add(region, ret);
31477                 if (cfg.background) {
31478                     ret.on('activate', function(gp) {
31479                         if (!gp.grid.rendered) {
31480                             gp.grid.render();
31481                         }
31482                     });
31483                 } else {
31484                     grid.render();
31485                 }
31486                 break;
31487            
31488                
31489                 
31490                 
31491             default: 
31492                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31493                 return null;
31494              // GridPanel (grid, cfg)
31495             
31496         }
31497         this.beginUpdate();
31498         // add children..
31499         var region = '';
31500         var abn = {};
31501         Roo.each(xitems, function(i)  {
31502             region = nb && i.region ? i.region : false;
31503             
31504             var add = ret.addxtype(i);
31505            
31506             if (region) {
31507                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31508                 if (!i.background) {
31509                     abn[region] = nb[region] ;
31510                 }
31511             }
31512             
31513         });
31514         this.endUpdate();
31515
31516         // make the last non-background panel active..
31517         //if (nb) { Roo.log(abn); }
31518         if (nb) {
31519             
31520             for(var r in abn) {
31521                 region = this.getRegion(r);
31522                 if (region) {
31523                     // tried using nb[r], but it does not work..
31524                      
31525                     region.showPanel(abn[r]);
31526                    
31527                 }
31528             }
31529         }
31530         return ret;
31531         
31532     }
31533 });
31534
31535 /**
31536  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31537  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31538  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31539  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31540  * <pre><code>
31541 // shorthand
31542 var CP = Roo.ContentPanel;
31543
31544 var layout = Roo.BorderLayout.create({
31545     north: {
31546         initialSize: 25,
31547         titlebar: false,
31548         panels: [new CP("north", "North")]
31549     },
31550     west: {
31551         split:true,
31552         initialSize: 200,
31553         minSize: 175,
31554         maxSize: 400,
31555         titlebar: true,
31556         collapsible: true,
31557         panels: [new CP("west", {title: "West"})]
31558     },
31559     east: {
31560         split:true,
31561         initialSize: 202,
31562         minSize: 175,
31563         maxSize: 400,
31564         titlebar: true,
31565         collapsible: true,
31566         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31567     },
31568     south: {
31569         split:true,
31570         initialSize: 100,
31571         minSize: 100,
31572         maxSize: 200,
31573         titlebar: true,
31574         collapsible: true,
31575         panels: [new CP("south", {title: "South", closable: true})]
31576     },
31577     center: {
31578         titlebar: true,
31579         autoScroll:true,
31580         resizeTabs: true,
31581         minTabWidth: 50,
31582         preferredTabWidth: 150,
31583         panels: [
31584             new CP("center1", {title: "Close Me", closable: true}),
31585             new CP("center2", {title: "Center Panel", closable: false})
31586         ]
31587     }
31588 }, document.body);
31589
31590 layout.getRegion("center").showPanel("center1");
31591 </code></pre>
31592  * @param config
31593  * @param targetEl
31594  */
31595 Roo.BorderLayout.create = function(config, targetEl){
31596     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31597     layout.beginUpdate();
31598     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31599     for(var j = 0, jlen = regions.length; j < jlen; j++){
31600         var lr = regions[j];
31601         if(layout.regions[lr] && config[lr].panels){
31602             var r = layout.regions[lr];
31603             var ps = config[lr].panels;
31604             layout.addTypedPanels(r, ps);
31605         }
31606     }
31607     layout.endUpdate();
31608     return layout;
31609 };
31610
31611 // private
31612 Roo.BorderLayout.RegionFactory = {
31613     // private
31614     validRegions : ["north","south","east","west","center"],
31615
31616     // private
31617     create : function(target, mgr, config){
31618         target = target.toLowerCase();
31619         if(config.lightweight || config.basic){
31620             return new Roo.BasicLayoutRegion(mgr, config, target);
31621         }
31622         switch(target){
31623             case "north":
31624                 return new Roo.NorthLayoutRegion(mgr, config);
31625             case "south":
31626                 return new Roo.SouthLayoutRegion(mgr, config);
31627             case "east":
31628                 return new Roo.EastLayoutRegion(mgr, config);
31629             case "west":
31630                 return new Roo.WestLayoutRegion(mgr, config);
31631             case "center":
31632                 return new Roo.CenterLayoutRegion(mgr, config);
31633         }
31634         throw 'Layout region "'+target+'" not supported.';
31635     }
31636 };/*
31637  * Based on:
31638  * Ext JS Library 1.1.1
31639  * Copyright(c) 2006-2007, Ext JS, LLC.
31640  *
31641  * Originally Released Under LGPL - original licence link has changed is not relivant.
31642  *
31643  * Fork - LGPL
31644  * <script type="text/javascript">
31645  */
31646  
31647 /**
31648  * @class Roo.BasicLayoutRegion
31649  * @extends Roo.util.Observable
31650  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31651  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31652  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31653  */
31654 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31655     this.mgr = mgr;
31656     this.position  = pos;
31657     this.events = {
31658         /**
31659          * @scope Roo.BasicLayoutRegion
31660          */
31661         
31662         /**
31663          * @event beforeremove
31664          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31665          * @param {Roo.LayoutRegion} this
31666          * @param {Roo.ContentPanel} panel The panel
31667          * @param {Object} e The cancel event object
31668          */
31669         "beforeremove" : true,
31670         /**
31671          * @event invalidated
31672          * Fires when the layout for this region is changed.
31673          * @param {Roo.LayoutRegion} this
31674          */
31675         "invalidated" : true,
31676         /**
31677          * @event visibilitychange
31678          * Fires when this region is shown or hidden 
31679          * @param {Roo.LayoutRegion} this
31680          * @param {Boolean} visibility true or false
31681          */
31682         "visibilitychange" : true,
31683         /**
31684          * @event paneladded
31685          * Fires when a panel is added. 
31686          * @param {Roo.LayoutRegion} this
31687          * @param {Roo.ContentPanel} panel The panel
31688          */
31689         "paneladded" : true,
31690         /**
31691          * @event panelremoved
31692          * Fires when a panel is removed. 
31693          * @param {Roo.LayoutRegion} this
31694          * @param {Roo.ContentPanel} panel The panel
31695          */
31696         "panelremoved" : true,
31697         /**
31698          * @event collapsed
31699          * Fires when this region is collapsed.
31700          * @param {Roo.LayoutRegion} this
31701          */
31702         "collapsed" : true,
31703         /**
31704          * @event expanded
31705          * Fires when this region is expanded.
31706          * @param {Roo.LayoutRegion} this
31707          */
31708         "expanded" : true,
31709         /**
31710          * @event slideshow
31711          * Fires when this region is slid into view.
31712          * @param {Roo.LayoutRegion} this
31713          */
31714         "slideshow" : true,
31715         /**
31716          * @event slidehide
31717          * Fires when this region slides out of view. 
31718          * @param {Roo.LayoutRegion} this
31719          */
31720         "slidehide" : true,
31721         /**
31722          * @event panelactivated
31723          * Fires when a panel is activated. 
31724          * @param {Roo.LayoutRegion} this
31725          * @param {Roo.ContentPanel} panel The activated panel
31726          */
31727         "panelactivated" : true,
31728         /**
31729          * @event resized
31730          * Fires when the user resizes this region. 
31731          * @param {Roo.LayoutRegion} this
31732          * @param {Number} newSize The new size (width for east/west, height for north/south)
31733          */
31734         "resized" : true
31735     };
31736     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31737     this.panels = new Roo.util.MixedCollection();
31738     this.panels.getKey = this.getPanelId.createDelegate(this);
31739     this.box = null;
31740     this.activePanel = null;
31741     // ensure listeners are added...
31742     
31743     if (config.listeners || config.events) {
31744         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31745             listeners : config.listeners || {},
31746             events : config.events || {}
31747         });
31748     }
31749     
31750     if(skipConfig !== true){
31751         this.applyConfig(config);
31752     }
31753 };
31754
31755 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31756     getPanelId : function(p){
31757         return p.getId();
31758     },
31759     
31760     applyConfig : function(config){
31761         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31762         this.config = config;
31763         
31764     },
31765     
31766     /**
31767      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31768      * the width, for horizontal (north, south) the height.
31769      * @param {Number} newSize The new width or height
31770      */
31771     resizeTo : function(newSize){
31772         var el = this.el ? this.el :
31773                  (this.activePanel ? this.activePanel.getEl() : null);
31774         if(el){
31775             switch(this.position){
31776                 case "east":
31777                 case "west":
31778                     el.setWidth(newSize);
31779                     this.fireEvent("resized", this, newSize);
31780                 break;
31781                 case "north":
31782                 case "south":
31783                     el.setHeight(newSize);
31784                     this.fireEvent("resized", this, newSize);
31785                 break;                
31786             }
31787         }
31788     },
31789     
31790     getBox : function(){
31791         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31792     },
31793     
31794     getMargins : function(){
31795         return this.margins;
31796     },
31797     
31798     updateBox : function(box){
31799         this.box = box;
31800         var el = this.activePanel.getEl();
31801         el.dom.style.left = box.x + "px";
31802         el.dom.style.top = box.y + "px";
31803         this.activePanel.setSize(box.width, box.height);
31804     },
31805     
31806     /**
31807      * Returns the container element for this region.
31808      * @return {Roo.Element}
31809      */
31810     getEl : function(){
31811         return this.activePanel;
31812     },
31813     
31814     /**
31815      * Returns true if this region is currently visible.
31816      * @return {Boolean}
31817      */
31818     isVisible : function(){
31819         return this.activePanel ? true : false;
31820     },
31821     
31822     setActivePanel : function(panel){
31823         panel = this.getPanel(panel);
31824         if(this.activePanel && this.activePanel != panel){
31825             this.activePanel.setActiveState(false);
31826             this.activePanel.getEl().setLeftTop(-10000,-10000);
31827         }
31828         this.activePanel = panel;
31829         panel.setActiveState(true);
31830         if(this.box){
31831             panel.setSize(this.box.width, this.box.height);
31832         }
31833         this.fireEvent("panelactivated", this, panel);
31834         this.fireEvent("invalidated");
31835     },
31836     
31837     /**
31838      * Show the specified panel.
31839      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31840      * @return {Roo.ContentPanel} The shown panel or null
31841      */
31842     showPanel : function(panel){
31843         if(panel = this.getPanel(panel)){
31844             this.setActivePanel(panel);
31845         }
31846         return panel;
31847     },
31848     
31849     /**
31850      * Get the active panel for this region.
31851      * @return {Roo.ContentPanel} The active panel or null
31852      */
31853     getActivePanel : function(){
31854         return this.activePanel;
31855     },
31856     
31857     /**
31858      * Add the passed ContentPanel(s)
31859      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31860      * @return {Roo.ContentPanel} The panel added (if only one was added)
31861      */
31862     add : function(panel){
31863         if(arguments.length > 1){
31864             for(var i = 0, len = arguments.length; i < len; i++) {
31865                 this.add(arguments[i]);
31866             }
31867             return null;
31868         }
31869         if(this.hasPanel(panel)){
31870             this.showPanel(panel);
31871             return panel;
31872         }
31873         var el = panel.getEl();
31874         if(el.dom.parentNode != this.mgr.el.dom){
31875             this.mgr.el.dom.appendChild(el.dom);
31876         }
31877         if(panel.setRegion){
31878             panel.setRegion(this);
31879         }
31880         this.panels.add(panel);
31881         el.setStyle("position", "absolute");
31882         if(!panel.background){
31883             this.setActivePanel(panel);
31884             if(this.config.initialSize && this.panels.getCount()==1){
31885                 this.resizeTo(this.config.initialSize);
31886             }
31887         }
31888         this.fireEvent("paneladded", this, panel);
31889         return panel;
31890     },
31891     
31892     /**
31893      * Returns true if the panel is in this region.
31894      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31895      * @return {Boolean}
31896      */
31897     hasPanel : function(panel){
31898         if(typeof panel == "object"){ // must be panel obj
31899             panel = panel.getId();
31900         }
31901         return this.getPanel(panel) ? true : false;
31902     },
31903     
31904     /**
31905      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31906      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31907      * @param {Boolean} preservePanel Overrides the config preservePanel option
31908      * @return {Roo.ContentPanel} The panel that was removed
31909      */
31910     remove : function(panel, preservePanel){
31911         panel = this.getPanel(panel);
31912         if(!panel){
31913             return null;
31914         }
31915         var e = {};
31916         this.fireEvent("beforeremove", this, panel, e);
31917         if(e.cancel === true){
31918             return null;
31919         }
31920         var panelId = panel.getId();
31921         this.panels.removeKey(panelId);
31922         return panel;
31923     },
31924     
31925     /**
31926      * Returns the panel specified or null if it's not in this region.
31927      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31928      * @return {Roo.ContentPanel}
31929      */
31930     getPanel : function(id){
31931         if(typeof id == "object"){ // must be panel obj
31932             return id;
31933         }
31934         return this.panels.get(id);
31935     },
31936     
31937     /**
31938      * Returns this regions position (north/south/east/west/center).
31939      * @return {String} 
31940      */
31941     getPosition: function(){
31942         return this.position;    
31943     }
31944 });/*
31945  * Based on:
31946  * Ext JS Library 1.1.1
31947  * Copyright(c) 2006-2007, Ext JS, LLC.
31948  *
31949  * Originally Released Under LGPL - original licence link has changed is not relivant.
31950  *
31951  * Fork - LGPL
31952  * <script type="text/javascript">
31953  */
31954  
31955 /**
31956  * @class Roo.LayoutRegion
31957  * @extends Roo.BasicLayoutRegion
31958  * This class represents a region in a layout manager.
31959  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31960  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31961  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31962  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31963  * @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})
31964  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31965  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31966  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31967  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31968  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31969  * @cfg {String}    title           The title for the region (overrides panel titles)
31970  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31971  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31972  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31973  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31974  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31975  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31976  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31977  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31978  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31979  * @cfg {Boolean}   showPin         True to show a pin button
31980  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31981  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31982  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31983  * @cfg {Number}    width           For East/West panels
31984  * @cfg {Number}    height          For North/South panels
31985  * @cfg {Boolean}   split           To show the splitter
31986  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31987  */
31988 Roo.LayoutRegion = function(mgr, config, pos){
31989     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31990     var dh = Roo.DomHelper;
31991     /** This region's container element 
31992     * @type Roo.Element */
31993     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31994     /** This region's title element 
31995     * @type Roo.Element */
31996
31997     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31998         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31999         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
32000     ]}, true);
32001     this.titleEl.enableDisplayMode();
32002     /** This region's title text element 
32003     * @type HTMLElement */
32004     this.titleTextEl = this.titleEl.dom.firstChild;
32005     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32006     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
32007     this.closeBtn.enableDisplayMode();
32008     this.closeBtn.on("click", this.closeClicked, this);
32009     this.closeBtn.hide();
32010
32011     this.createBody(config);
32012     this.visible = true;
32013     this.collapsed = false;
32014
32015     if(config.hideWhenEmpty){
32016         this.hide();
32017         this.on("paneladded", this.validateVisibility, this);
32018         this.on("panelremoved", this.validateVisibility, this);
32019     }
32020     this.applyConfig(config);
32021 };
32022
32023 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
32024
32025     createBody : function(){
32026         /** This region's body element 
32027         * @type Roo.Element */
32028         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
32029     },
32030
32031     applyConfig : function(c){
32032         if(c.collapsible && this.position != "center" && !this.collapsedEl){
32033             var dh = Roo.DomHelper;
32034             if(c.titlebar !== false){
32035                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
32036                 this.collapseBtn.on("click", this.collapse, this);
32037                 this.collapseBtn.enableDisplayMode();
32038
32039                 if(c.showPin === true || this.showPin){
32040                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
32041                     this.stickBtn.enableDisplayMode();
32042                     this.stickBtn.on("click", this.expand, this);
32043                     this.stickBtn.hide();
32044                 }
32045             }
32046             /** This region's collapsed element
32047             * @type Roo.Element */
32048             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32049                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32050             ]}, true);
32051             if(c.floatable !== false){
32052                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32053                this.collapsedEl.on("click", this.collapseClick, this);
32054             }
32055
32056             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32057                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32058                    id: "message", unselectable: "on", style:{"float":"left"}});
32059                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32060              }
32061             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32062             this.expandBtn.on("click", this.expand, this);
32063         }
32064         if(this.collapseBtn){
32065             this.collapseBtn.setVisible(c.collapsible == true);
32066         }
32067         this.cmargins = c.cmargins || this.cmargins ||
32068                          (this.position == "west" || this.position == "east" ?
32069                              {top: 0, left: 2, right:2, bottom: 0} :
32070                              {top: 2, left: 0, right:0, bottom: 2});
32071         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32072         this.bottomTabs = c.tabPosition != "top";
32073         this.autoScroll = c.autoScroll || false;
32074         if(this.autoScroll){
32075             this.bodyEl.setStyle("overflow", "auto");
32076         }else{
32077             this.bodyEl.setStyle("overflow", "hidden");
32078         }
32079         //if(c.titlebar !== false){
32080             if((!c.titlebar && !c.title) || c.titlebar === false){
32081                 this.titleEl.hide();
32082             }else{
32083                 this.titleEl.show();
32084                 if(c.title){
32085                     this.titleTextEl.innerHTML = c.title;
32086                 }
32087             }
32088         //}
32089         this.duration = c.duration || .30;
32090         this.slideDuration = c.slideDuration || .45;
32091         this.config = c;
32092         if(c.collapsed){
32093             this.collapse(true);
32094         }
32095         if(c.hidden){
32096             this.hide();
32097         }
32098     },
32099     /**
32100      * Returns true if this region is currently visible.
32101      * @return {Boolean}
32102      */
32103     isVisible : function(){
32104         return this.visible;
32105     },
32106
32107     /**
32108      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32109      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32110      */
32111     setCollapsedTitle : function(title){
32112         title = title || "&#160;";
32113         if(this.collapsedTitleTextEl){
32114             this.collapsedTitleTextEl.innerHTML = title;
32115         }
32116     },
32117
32118     getBox : function(){
32119         var b;
32120         if(!this.collapsed){
32121             b = this.el.getBox(false, true);
32122         }else{
32123             b = this.collapsedEl.getBox(false, true);
32124         }
32125         return b;
32126     },
32127
32128     getMargins : function(){
32129         return this.collapsed ? this.cmargins : this.margins;
32130     },
32131
32132     highlight : function(){
32133         this.el.addClass("x-layout-panel-dragover");
32134     },
32135
32136     unhighlight : function(){
32137         this.el.removeClass("x-layout-panel-dragover");
32138     },
32139
32140     updateBox : function(box){
32141         this.box = box;
32142         if(!this.collapsed){
32143             this.el.dom.style.left = box.x + "px";
32144             this.el.dom.style.top = box.y + "px";
32145             this.updateBody(box.width, box.height);
32146         }else{
32147             this.collapsedEl.dom.style.left = box.x + "px";
32148             this.collapsedEl.dom.style.top = box.y + "px";
32149             this.collapsedEl.setSize(box.width, box.height);
32150         }
32151         if(this.tabs){
32152             this.tabs.autoSizeTabs();
32153         }
32154     },
32155
32156     updateBody : function(w, h){
32157         if(w !== null){
32158             this.el.setWidth(w);
32159             w -= this.el.getBorderWidth("rl");
32160             if(this.config.adjustments){
32161                 w += this.config.adjustments[0];
32162             }
32163         }
32164         if(h !== null){
32165             this.el.setHeight(h);
32166             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32167             h -= this.el.getBorderWidth("tb");
32168             if(this.config.adjustments){
32169                 h += this.config.adjustments[1];
32170             }
32171             this.bodyEl.setHeight(h);
32172             if(this.tabs){
32173                 h = this.tabs.syncHeight(h);
32174             }
32175         }
32176         if(this.panelSize){
32177             w = w !== null ? w : this.panelSize.width;
32178             h = h !== null ? h : this.panelSize.height;
32179         }
32180         if(this.activePanel){
32181             var el = this.activePanel.getEl();
32182             w = w !== null ? w : el.getWidth();
32183             h = h !== null ? h : el.getHeight();
32184             this.panelSize = {width: w, height: h};
32185             this.activePanel.setSize(w, h);
32186         }
32187         if(Roo.isIE && this.tabs){
32188             this.tabs.el.repaint();
32189         }
32190     },
32191
32192     /**
32193      * Returns the container element for this region.
32194      * @return {Roo.Element}
32195      */
32196     getEl : function(){
32197         return this.el;
32198     },
32199
32200     /**
32201      * Hides this region.
32202      */
32203     hide : function(){
32204         if(!this.collapsed){
32205             this.el.dom.style.left = "-2000px";
32206             this.el.hide();
32207         }else{
32208             this.collapsedEl.dom.style.left = "-2000px";
32209             this.collapsedEl.hide();
32210         }
32211         this.visible = false;
32212         this.fireEvent("visibilitychange", this, false);
32213     },
32214
32215     /**
32216      * Shows this region if it was previously hidden.
32217      */
32218     show : function(){
32219         if(!this.collapsed){
32220             this.el.show();
32221         }else{
32222             this.collapsedEl.show();
32223         }
32224         this.visible = true;
32225         this.fireEvent("visibilitychange", this, true);
32226     },
32227
32228     closeClicked : function(){
32229         if(this.activePanel){
32230             this.remove(this.activePanel);
32231         }
32232     },
32233
32234     collapseClick : function(e){
32235         if(this.isSlid){
32236            e.stopPropagation();
32237            this.slideIn();
32238         }else{
32239            e.stopPropagation();
32240            this.slideOut();
32241         }
32242     },
32243
32244     /**
32245      * Collapses this region.
32246      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32247      */
32248     collapse : function(skipAnim){
32249         if(this.collapsed) return;
32250         this.collapsed = true;
32251         if(this.split){
32252             this.split.el.hide();
32253         }
32254         if(this.config.animate && skipAnim !== true){
32255             this.fireEvent("invalidated", this);
32256             this.animateCollapse();
32257         }else{
32258             this.el.setLocation(-20000,-20000);
32259             this.el.hide();
32260             this.collapsedEl.show();
32261             this.fireEvent("collapsed", this);
32262             this.fireEvent("invalidated", this);
32263         }
32264     },
32265
32266     animateCollapse : function(){
32267         // overridden
32268     },
32269
32270     /**
32271      * Expands this region if it was previously collapsed.
32272      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32273      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32274      */
32275     expand : function(e, skipAnim){
32276         if(e) e.stopPropagation();
32277         if(!this.collapsed || this.el.hasActiveFx()) return;
32278         if(this.isSlid){
32279             this.afterSlideIn();
32280             skipAnim = true;
32281         }
32282         this.collapsed = false;
32283         if(this.config.animate && skipAnim !== true){
32284             this.animateExpand();
32285         }else{
32286             this.el.show();
32287             if(this.split){
32288                 this.split.el.show();
32289             }
32290             this.collapsedEl.setLocation(-2000,-2000);
32291             this.collapsedEl.hide();
32292             this.fireEvent("invalidated", this);
32293             this.fireEvent("expanded", this);
32294         }
32295     },
32296
32297     animateExpand : function(){
32298         // overridden
32299     },
32300
32301     initTabs : function()
32302     {
32303         this.bodyEl.setStyle("overflow", "hidden");
32304         var ts = new Roo.TabPanel(
32305                 this.bodyEl.dom,
32306                 {
32307                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32308                     disableTooltips: this.config.disableTabTips,
32309                     toolbar : this.config.toolbar
32310                 }
32311         );
32312         if(this.config.hideTabs){
32313             ts.stripWrap.setDisplayed(false);
32314         }
32315         this.tabs = ts;
32316         ts.resizeTabs = this.config.resizeTabs === true;
32317         ts.minTabWidth = this.config.minTabWidth || 40;
32318         ts.maxTabWidth = this.config.maxTabWidth || 250;
32319         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32320         ts.monitorResize = false;
32321         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32322         ts.bodyEl.addClass('x-layout-tabs-body');
32323         this.panels.each(this.initPanelAsTab, this);
32324     },
32325
32326     initPanelAsTab : function(panel){
32327         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32328                     this.config.closeOnTab && panel.isClosable());
32329         if(panel.tabTip !== undefined){
32330             ti.setTooltip(panel.tabTip);
32331         }
32332         ti.on("activate", function(){
32333               this.setActivePanel(panel);
32334         }, this);
32335         if(this.config.closeOnTab){
32336             ti.on("beforeclose", function(t, e){
32337                 e.cancel = true;
32338                 this.remove(panel);
32339             }, this);
32340         }
32341         return ti;
32342     },
32343
32344     updatePanelTitle : function(panel, title){
32345         if(this.activePanel == panel){
32346             this.updateTitle(title);
32347         }
32348         if(this.tabs){
32349             var ti = this.tabs.getTab(panel.getEl().id);
32350             ti.setText(title);
32351             if(panel.tabTip !== undefined){
32352                 ti.setTooltip(panel.tabTip);
32353             }
32354         }
32355     },
32356
32357     updateTitle : function(title){
32358         if(this.titleTextEl && !this.config.title){
32359             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32360         }
32361     },
32362
32363     setActivePanel : function(panel){
32364         panel = this.getPanel(panel);
32365         if(this.activePanel && this.activePanel != panel){
32366             this.activePanel.setActiveState(false);
32367         }
32368         this.activePanel = panel;
32369         panel.setActiveState(true);
32370         if(this.panelSize){
32371             panel.setSize(this.panelSize.width, this.panelSize.height);
32372         }
32373         if(this.closeBtn){
32374             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32375         }
32376         this.updateTitle(panel.getTitle());
32377         if(this.tabs){
32378             this.fireEvent("invalidated", this);
32379         }
32380         this.fireEvent("panelactivated", this, panel);
32381     },
32382
32383     /**
32384      * Shows the specified panel.
32385      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32386      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32387      */
32388     showPanel : function(panel){
32389         if(panel = this.getPanel(panel)){
32390             if(this.tabs){
32391                 var tab = this.tabs.getTab(panel.getEl().id);
32392                 if(tab.isHidden()){
32393                     this.tabs.unhideTab(tab.id);
32394                 }
32395                 tab.activate();
32396             }else{
32397                 this.setActivePanel(panel);
32398             }
32399         }
32400         return panel;
32401     },
32402
32403     /**
32404      * Get the active panel for this region.
32405      * @return {Roo.ContentPanel} The active panel or null
32406      */
32407     getActivePanel : function(){
32408         return this.activePanel;
32409     },
32410
32411     validateVisibility : function(){
32412         if(this.panels.getCount() < 1){
32413             this.updateTitle("&#160;");
32414             this.closeBtn.hide();
32415             this.hide();
32416         }else{
32417             if(!this.isVisible()){
32418                 this.show();
32419             }
32420         }
32421     },
32422
32423     /**
32424      * Adds the passed ContentPanel(s) to this region.
32425      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32426      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32427      */
32428     add : function(panel){
32429         if(arguments.length > 1){
32430             for(var i = 0, len = arguments.length; i < len; i++) {
32431                 this.add(arguments[i]);
32432             }
32433             return null;
32434         }
32435         if(this.hasPanel(panel)){
32436             this.showPanel(panel);
32437             return panel;
32438         }
32439         panel.setRegion(this);
32440         this.panels.add(panel);
32441         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32442             this.bodyEl.dom.appendChild(panel.getEl().dom);
32443             if(panel.background !== true){
32444                 this.setActivePanel(panel);
32445             }
32446             this.fireEvent("paneladded", this, panel);
32447             return panel;
32448         }
32449         if(!this.tabs){
32450             this.initTabs();
32451         }else{
32452             this.initPanelAsTab(panel);
32453         }
32454         if(panel.background !== true){
32455             this.tabs.activate(panel.getEl().id);
32456         }
32457         this.fireEvent("paneladded", this, panel);
32458         return panel;
32459     },
32460
32461     /**
32462      * Hides the tab for the specified panel.
32463      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32464      */
32465     hidePanel : function(panel){
32466         if(this.tabs && (panel = this.getPanel(panel))){
32467             this.tabs.hideTab(panel.getEl().id);
32468         }
32469     },
32470
32471     /**
32472      * Unhides the tab for a previously hidden panel.
32473      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32474      */
32475     unhidePanel : function(panel){
32476         if(this.tabs && (panel = this.getPanel(panel))){
32477             this.tabs.unhideTab(panel.getEl().id);
32478         }
32479     },
32480
32481     clearPanels : function(){
32482         while(this.panels.getCount() > 0){
32483              this.remove(this.panels.first());
32484         }
32485     },
32486
32487     /**
32488      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32489      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32490      * @param {Boolean} preservePanel Overrides the config preservePanel option
32491      * @return {Roo.ContentPanel} The panel that was removed
32492      */
32493     remove : function(panel, preservePanel){
32494         panel = this.getPanel(panel);
32495         if(!panel){
32496             return null;
32497         }
32498         var e = {};
32499         this.fireEvent("beforeremove", this, panel, e);
32500         if(e.cancel === true){
32501             return null;
32502         }
32503         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32504         var panelId = panel.getId();
32505         this.panels.removeKey(panelId);
32506         if(preservePanel){
32507             document.body.appendChild(panel.getEl().dom);
32508         }
32509         if(this.tabs){
32510             this.tabs.removeTab(panel.getEl().id);
32511         }else if (!preservePanel){
32512             this.bodyEl.dom.removeChild(panel.getEl().dom);
32513         }
32514         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32515             var p = this.panels.first();
32516             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32517             tempEl.appendChild(p.getEl().dom);
32518             this.bodyEl.update("");
32519             this.bodyEl.dom.appendChild(p.getEl().dom);
32520             tempEl = null;
32521             this.updateTitle(p.getTitle());
32522             this.tabs = null;
32523             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32524             this.setActivePanel(p);
32525         }
32526         panel.setRegion(null);
32527         if(this.activePanel == panel){
32528             this.activePanel = null;
32529         }
32530         if(this.config.autoDestroy !== false && preservePanel !== true){
32531             try{panel.destroy();}catch(e){}
32532         }
32533         this.fireEvent("panelremoved", this, panel);
32534         return panel;
32535     },
32536
32537     /**
32538      * Returns the TabPanel component used by this region
32539      * @return {Roo.TabPanel}
32540      */
32541     getTabs : function(){
32542         return this.tabs;
32543     },
32544
32545     createTool : function(parentEl, className){
32546         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32547             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32548         btn.addClassOnOver("x-layout-tools-button-over");
32549         return btn;
32550     }
32551 });/*
32552  * Based on:
32553  * Ext JS Library 1.1.1
32554  * Copyright(c) 2006-2007, Ext JS, LLC.
32555  *
32556  * Originally Released Under LGPL - original licence link has changed is not relivant.
32557  *
32558  * Fork - LGPL
32559  * <script type="text/javascript">
32560  */
32561  
32562
32563
32564 /**
32565  * @class Roo.SplitLayoutRegion
32566  * @extends Roo.LayoutRegion
32567  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32568  */
32569 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32570     this.cursor = cursor;
32571     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32572 };
32573
32574 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32575     splitTip : "Drag to resize.",
32576     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32577     useSplitTips : false,
32578
32579     applyConfig : function(config){
32580         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32581         if(config.split){
32582             if(!this.split){
32583                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32584                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32585                 /** The SplitBar for this region 
32586                 * @type Roo.SplitBar */
32587                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32588                 this.split.on("moved", this.onSplitMove, this);
32589                 this.split.useShim = config.useShim === true;
32590                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32591                 if(this.useSplitTips){
32592                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32593                 }
32594                 if(config.collapsible){
32595                     this.split.el.on("dblclick", this.collapse,  this);
32596                 }
32597             }
32598             if(typeof config.minSize != "undefined"){
32599                 this.split.minSize = config.minSize;
32600             }
32601             if(typeof config.maxSize != "undefined"){
32602                 this.split.maxSize = config.maxSize;
32603             }
32604             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32605                 this.hideSplitter();
32606             }
32607         }
32608     },
32609
32610     getHMaxSize : function(){
32611          var cmax = this.config.maxSize || 10000;
32612          var center = this.mgr.getRegion("center");
32613          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32614     },
32615
32616     getVMaxSize : function(){
32617          var cmax = this.config.maxSize || 10000;
32618          var center = this.mgr.getRegion("center");
32619          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32620     },
32621
32622     onSplitMove : function(split, newSize){
32623         this.fireEvent("resized", this, newSize);
32624     },
32625     
32626     /** 
32627      * Returns the {@link Roo.SplitBar} for this region.
32628      * @return {Roo.SplitBar}
32629      */
32630     getSplitBar : function(){
32631         return this.split;
32632     },
32633     
32634     hide : function(){
32635         this.hideSplitter();
32636         Roo.SplitLayoutRegion.superclass.hide.call(this);
32637     },
32638
32639     hideSplitter : function(){
32640         if(this.split){
32641             this.split.el.setLocation(-2000,-2000);
32642             this.split.el.hide();
32643         }
32644     },
32645
32646     show : function(){
32647         if(this.split){
32648             this.split.el.show();
32649         }
32650         Roo.SplitLayoutRegion.superclass.show.call(this);
32651     },
32652     
32653     beforeSlide: function(){
32654         if(Roo.isGecko){// firefox overflow auto bug workaround
32655             this.bodyEl.clip();
32656             if(this.tabs) this.tabs.bodyEl.clip();
32657             if(this.activePanel){
32658                 this.activePanel.getEl().clip();
32659                 
32660                 if(this.activePanel.beforeSlide){
32661                     this.activePanel.beforeSlide();
32662                 }
32663             }
32664         }
32665     },
32666     
32667     afterSlide : function(){
32668         if(Roo.isGecko){// firefox overflow auto bug workaround
32669             this.bodyEl.unclip();
32670             if(this.tabs) this.tabs.bodyEl.unclip();
32671             if(this.activePanel){
32672                 this.activePanel.getEl().unclip();
32673                 if(this.activePanel.afterSlide){
32674                     this.activePanel.afterSlide();
32675                 }
32676             }
32677         }
32678     },
32679
32680     initAutoHide : function(){
32681         if(this.autoHide !== false){
32682             if(!this.autoHideHd){
32683                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32684                 this.autoHideHd = {
32685                     "mouseout": function(e){
32686                         if(!e.within(this.el, true)){
32687                             st.delay(500);
32688                         }
32689                     },
32690                     "mouseover" : function(e){
32691                         st.cancel();
32692                     },
32693                     scope : this
32694                 };
32695             }
32696             this.el.on(this.autoHideHd);
32697         }
32698     },
32699
32700     clearAutoHide : function(){
32701         if(this.autoHide !== false){
32702             this.el.un("mouseout", this.autoHideHd.mouseout);
32703             this.el.un("mouseover", this.autoHideHd.mouseover);
32704         }
32705     },
32706
32707     clearMonitor : function(){
32708         Roo.get(document).un("click", this.slideInIf, this);
32709     },
32710
32711     // these names are backwards but not changed for compat
32712     slideOut : function(){
32713         if(this.isSlid || this.el.hasActiveFx()){
32714             return;
32715         }
32716         this.isSlid = true;
32717         if(this.collapseBtn){
32718             this.collapseBtn.hide();
32719         }
32720         this.closeBtnState = this.closeBtn.getStyle('display');
32721         this.closeBtn.hide();
32722         if(this.stickBtn){
32723             this.stickBtn.show();
32724         }
32725         this.el.show();
32726         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32727         this.beforeSlide();
32728         this.el.setStyle("z-index", 10001);
32729         this.el.slideIn(this.getSlideAnchor(), {
32730             callback: function(){
32731                 this.afterSlide();
32732                 this.initAutoHide();
32733                 Roo.get(document).on("click", this.slideInIf, this);
32734                 this.fireEvent("slideshow", this);
32735             },
32736             scope: this,
32737             block: true
32738         });
32739     },
32740
32741     afterSlideIn : function(){
32742         this.clearAutoHide();
32743         this.isSlid = false;
32744         this.clearMonitor();
32745         this.el.setStyle("z-index", "");
32746         if(this.collapseBtn){
32747             this.collapseBtn.show();
32748         }
32749         this.closeBtn.setStyle('display', this.closeBtnState);
32750         if(this.stickBtn){
32751             this.stickBtn.hide();
32752         }
32753         this.fireEvent("slidehide", this);
32754     },
32755
32756     slideIn : function(cb){
32757         if(!this.isSlid || this.el.hasActiveFx()){
32758             Roo.callback(cb);
32759             return;
32760         }
32761         this.isSlid = false;
32762         this.beforeSlide();
32763         this.el.slideOut(this.getSlideAnchor(), {
32764             callback: function(){
32765                 this.el.setLeftTop(-10000, -10000);
32766                 this.afterSlide();
32767                 this.afterSlideIn();
32768                 Roo.callback(cb);
32769             },
32770             scope: this,
32771             block: true
32772         });
32773     },
32774     
32775     slideInIf : function(e){
32776         if(!e.within(this.el)){
32777             this.slideIn();
32778         }
32779     },
32780
32781     animateCollapse : function(){
32782         this.beforeSlide();
32783         this.el.setStyle("z-index", 20000);
32784         var anchor = this.getSlideAnchor();
32785         this.el.slideOut(anchor, {
32786             callback : function(){
32787                 this.el.setStyle("z-index", "");
32788                 this.collapsedEl.slideIn(anchor, {duration:.3});
32789                 this.afterSlide();
32790                 this.el.setLocation(-10000,-10000);
32791                 this.el.hide();
32792                 this.fireEvent("collapsed", this);
32793             },
32794             scope: this,
32795             block: true
32796         });
32797     },
32798
32799     animateExpand : function(){
32800         this.beforeSlide();
32801         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32802         this.el.setStyle("z-index", 20000);
32803         this.collapsedEl.hide({
32804             duration:.1
32805         });
32806         this.el.slideIn(this.getSlideAnchor(), {
32807             callback : function(){
32808                 this.el.setStyle("z-index", "");
32809                 this.afterSlide();
32810                 if(this.split){
32811                     this.split.el.show();
32812                 }
32813                 this.fireEvent("invalidated", this);
32814                 this.fireEvent("expanded", this);
32815             },
32816             scope: this,
32817             block: true
32818         });
32819     },
32820
32821     anchors : {
32822         "west" : "left",
32823         "east" : "right",
32824         "north" : "top",
32825         "south" : "bottom"
32826     },
32827
32828     sanchors : {
32829         "west" : "l",
32830         "east" : "r",
32831         "north" : "t",
32832         "south" : "b"
32833     },
32834
32835     canchors : {
32836         "west" : "tl-tr",
32837         "east" : "tr-tl",
32838         "north" : "tl-bl",
32839         "south" : "bl-tl"
32840     },
32841
32842     getAnchor : function(){
32843         return this.anchors[this.position];
32844     },
32845
32846     getCollapseAnchor : function(){
32847         return this.canchors[this.position];
32848     },
32849
32850     getSlideAnchor : function(){
32851         return this.sanchors[this.position];
32852     },
32853
32854     getAlignAdj : function(){
32855         var cm = this.cmargins;
32856         switch(this.position){
32857             case "west":
32858                 return [0, 0];
32859             break;
32860             case "east":
32861                 return [0, 0];
32862             break;
32863             case "north":
32864                 return [0, 0];
32865             break;
32866             case "south":
32867                 return [0, 0];
32868             break;
32869         }
32870     },
32871
32872     getExpandAdj : function(){
32873         var c = this.collapsedEl, cm = this.cmargins;
32874         switch(this.position){
32875             case "west":
32876                 return [-(cm.right+c.getWidth()+cm.left), 0];
32877             break;
32878             case "east":
32879                 return [cm.right+c.getWidth()+cm.left, 0];
32880             break;
32881             case "north":
32882                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32883             break;
32884             case "south":
32885                 return [0, cm.top+cm.bottom+c.getHeight()];
32886             break;
32887         }
32888     }
32889 });/*
32890  * Based on:
32891  * Ext JS Library 1.1.1
32892  * Copyright(c) 2006-2007, Ext JS, LLC.
32893  *
32894  * Originally Released Under LGPL - original licence link has changed is not relivant.
32895  *
32896  * Fork - LGPL
32897  * <script type="text/javascript">
32898  */
32899 /*
32900  * These classes are private internal classes
32901  */
32902 Roo.CenterLayoutRegion = function(mgr, config){
32903     Roo.LayoutRegion.call(this, mgr, config, "center");
32904     this.visible = true;
32905     this.minWidth = config.minWidth || 20;
32906     this.minHeight = config.minHeight || 20;
32907 };
32908
32909 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32910     hide : function(){
32911         // center panel can't be hidden
32912     },
32913     
32914     show : function(){
32915         // center panel can't be hidden
32916     },
32917     
32918     getMinWidth: function(){
32919         return this.minWidth;
32920     },
32921     
32922     getMinHeight: function(){
32923         return this.minHeight;
32924     }
32925 });
32926
32927
32928 Roo.NorthLayoutRegion = function(mgr, config){
32929     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32930     if(this.split){
32931         this.split.placement = Roo.SplitBar.TOP;
32932         this.split.orientation = Roo.SplitBar.VERTICAL;
32933         this.split.el.addClass("x-layout-split-v");
32934     }
32935     var size = config.initialSize || config.height;
32936     if(typeof size != "undefined"){
32937         this.el.setHeight(size);
32938     }
32939 };
32940 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32941     orientation: Roo.SplitBar.VERTICAL,
32942     getBox : function(){
32943         if(this.collapsed){
32944             return this.collapsedEl.getBox();
32945         }
32946         var box = this.el.getBox();
32947         if(this.split){
32948             box.height += this.split.el.getHeight();
32949         }
32950         return box;
32951     },
32952     
32953     updateBox : function(box){
32954         if(this.split && !this.collapsed){
32955             box.height -= this.split.el.getHeight();
32956             this.split.el.setLeft(box.x);
32957             this.split.el.setTop(box.y+box.height);
32958             this.split.el.setWidth(box.width);
32959         }
32960         if(this.collapsed){
32961             this.updateBody(box.width, null);
32962         }
32963         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32964     }
32965 });
32966
32967 Roo.SouthLayoutRegion = function(mgr, config){
32968     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32969     if(this.split){
32970         this.split.placement = Roo.SplitBar.BOTTOM;
32971         this.split.orientation = Roo.SplitBar.VERTICAL;
32972         this.split.el.addClass("x-layout-split-v");
32973     }
32974     var size = config.initialSize || config.height;
32975     if(typeof size != "undefined"){
32976         this.el.setHeight(size);
32977     }
32978 };
32979 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32980     orientation: Roo.SplitBar.VERTICAL,
32981     getBox : function(){
32982         if(this.collapsed){
32983             return this.collapsedEl.getBox();
32984         }
32985         var box = this.el.getBox();
32986         if(this.split){
32987             var sh = this.split.el.getHeight();
32988             box.height += sh;
32989             box.y -= sh;
32990         }
32991         return box;
32992     },
32993     
32994     updateBox : function(box){
32995         if(this.split && !this.collapsed){
32996             var sh = this.split.el.getHeight();
32997             box.height -= sh;
32998             box.y += sh;
32999             this.split.el.setLeft(box.x);
33000             this.split.el.setTop(box.y-sh);
33001             this.split.el.setWidth(box.width);
33002         }
33003         if(this.collapsed){
33004             this.updateBody(box.width, null);
33005         }
33006         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33007     }
33008 });
33009
33010 Roo.EastLayoutRegion = function(mgr, config){
33011     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
33012     if(this.split){
33013         this.split.placement = Roo.SplitBar.RIGHT;
33014         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33015         this.split.el.addClass("x-layout-split-h");
33016     }
33017     var size = config.initialSize || config.width;
33018     if(typeof size != "undefined"){
33019         this.el.setWidth(size);
33020     }
33021 };
33022 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
33023     orientation: Roo.SplitBar.HORIZONTAL,
33024     getBox : function(){
33025         if(this.collapsed){
33026             return this.collapsedEl.getBox();
33027         }
33028         var box = this.el.getBox();
33029         if(this.split){
33030             var sw = this.split.el.getWidth();
33031             box.width += sw;
33032             box.x -= sw;
33033         }
33034         return box;
33035     },
33036
33037     updateBox : function(box){
33038         if(this.split && !this.collapsed){
33039             var sw = this.split.el.getWidth();
33040             box.width -= sw;
33041             this.split.el.setLeft(box.x);
33042             this.split.el.setTop(box.y);
33043             this.split.el.setHeight(box.height);
33044             box.x += sw;
33045         }
33046         if(this.collapsed){
33047             this.updateBody(null, box.height);
33048         }
33049         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33050     }
33051 });
33052
33053 Roo.WestLayoutRegion = function(mgr, config){
33054     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
33055     if(this.split){
33056         this.split.placement = Roo.SplitBar.LEFT;
33057         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33058         this.split.el.addClass("x-layout-split-h");
33059     }
33060     var size = config.initialSize || config.width;
33061     if(typeof size != "undefined"){
33062         this.el.setWidth(size);
33063     }
33064 };
33065 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
33066     orientation: Roo.SplitBar.HORIZONTAL,
33067     getBox : function(){
33068         if(this.collapsed){
33069             return this.collapsedEl.getBox();
33070         }
33071         var box = this.el.getBox();
33072         if(this.split){
33073             box.width += this.split.el.getWidth();
33074         }
33075         return box;
33076     },
33077     
33078     updateBox : function(box){
33079         if(this.split && !this.collapsed){
33080             var sw = this.split.el.getWidth();
33081             box.width -= sw;
33082             this.split.el.setLeft(box.x+box.width);
33083             this.split.el.setTop(box.y);
33084             this.split.el.setHeight(box.height);
33085         }
33086         if(this.collapsed){
33087             this.updateBody(null, box.height);
33088         }
33089         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33090     }
33091 });
33092 /*
33093  * Based on:
33094  * Ext JS Library 1.1.1
33095  * Copyright(c) 2006-2007, Ext JS, LLC.
33096  *
33097  * Originally Released Under LGPL - original licence link has changed is not relivant.
33098  *
33099  * Fork - LGPL
33100  * <script type="text/javascript">
33101  */
33102  
33103  
33104 /*
33105  * Private internal class for reading and applying state
33106  */
33107 Roo.LayoutStateManager = function(layout){
33108      // default empty state
33109      this.state = {
33110         north: {},
33111         south: {},
33112         east: {},
33113         west: {}       
33114     };
33115 };
33116
33117 Roo.LayoutStateManager.prototype = {
33118     init : function(layout, provider){
33119         this.provider = provider;
33120         var state = provider.get(layout.id+"-layout-state");
33121         if(state){
33122             var wasUpdating = layout.isUpdating();
33123             if(!wasUpdating){
33124                 layout.beginUpdate();
33125             }
33126             for(var key in state){
33127                 if(typeof state[key] != "function"){
33128                     var rstate = state[key];
33129                     var r = layout.getRegion(key);
33130                     if(r && rstate){
33131                         if(rstate.size){
33132                             r.resizeTo(rstate.size);
33133                         }
33134                         if(rstate.collapsed == true){
33135                             r.collapse(true);
33136                         }else{
33137                             r.expand(null, true);
33138                         }
33139                     }
33140                 }
33141             }
33142             if(!wasUpdating){
33143                 layout.endUpdate();
33144             }
33145             this.state = state; 
33146         }
33147         this.layout = layout;
33148         layout.on("regionresized", this.onRegionResized, this);
33149         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33150         layout.on("regionexpanded", this.onRegionExpanded, this);
33151     },
33152     
33153     storeState : function(){
33154         this.provider.set(this.layout.id+"-layout-state", this.state);
33155     },
33156     
33157     onRegionResized : function(region, newSize){
33158         this.state[region.getPosition()].size = newSize;
33159         this.storeState();
33160     },
33161     
33162     onRegionCollapsed : function(region){
33163         this.state[region.getPosition()].collapsed = true;
33164         this.storeState();
33165     },
33166     
33167     onRegionExpanded : function(region){
33168         this.state[region.getPosition()].collapsed = false;
33169         this.storeState();
33170     }
33171 };/*
33172  * Based on:
33173  * Ext JS Library 1.1.1
33174  * Copyright(c) 2006-2007, Ext JS, LLC.
33175  *
33176  * Originally Released Under LGPL - original licence link has changed is not relivant.
33177  *
33178  * Fork - LGPL
33179  * <script type="text/javascript">
33180  */
33181 /**
33182  * @class Roo.ContentPanel
33183  * @extends Roo.util.Observable
33184  * A basic ContentPanel element.
33185  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33186  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33187  * @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
33188  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33189  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33190  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33191  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33192  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33193  * @cfg {String} title          The title for this panel
33194  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33195  * @cfg {String} url            Calls {@link #setUrl} with this value
33196  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33197  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33198  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33199  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33200
33201  * @constructor
33202  * Create a new ContentPanel.
33203  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33204  * @param {String/Object} config A string to set only the title or a config object
33205  * @param {String} content (optional) Set the HTML content for this panel
33206  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33207  */
33208 Roo.ContentPanel = function(el, config, content){
33209     
33210      
33211     /*
33212     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33213         config = el;
33214         el = Roo.id();
33215     }
33216     if (config && config.parentLayout) { 
33217         el = config.parentLayout.el.createChild(); 
33218     }
33219     */
33220     if(el.autoCreate){ // xtype is available if this is called from factory
33221         config = el;
33222         el = Roo.id();
33223     }
33224     this.el = Roo.get(el);
33225     if(!this.el && config && config.autoCreate){
33226         if(typeof config.autoCreate == "object"){
33227             if(!config.autoCreate.id){
33228                 config.autoCreate.id = config.id||el;
33229             }
33230             this.el = Roo.DomHelper.append(document.body,
33231                         config.autoCreate, true);
33232         }else{
33233             this.el = Roo.DomHelper.append(document.body,
33234                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33235         }
33236     }
33237     this.closable = false;
33238     this.loaded = false;
33239     this.active = false;
33240     if(typeof config == "string"){
33241         this.title = config;
33242     }else{
33243         Roo.apply(this, config);
33244     }
33245     
33246     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33247         this.wrapEl = this.el.wrap();
33248         this.toolbar.container = this.el.insertSibling(false, 'before');
33249         this.toolbar = new Roo.Toolbar(this.toolbar);
33250     }
33251     
33252     // xtype created footer. - not sure if will work as we normally have to render first..
33253     if (this.footer && !this.footer.el && this.footer.xtype) {
33254         if (!this.wrapEl) {
33255             this.wrapEl = this.el.wrap();
33256         }
33257     
33258         this.footer.container = this.wrapEl.createChild();
33259          
33260         this.footer = Roo.factory(this.footer, Roo);
33261         
33262     }
33263     
33264     if(this.resizeEl){
33265         this.resizeEl = Roo.get(this.resizeEl, true);
33266     }else{
33267         this.resizeEl = this.el;
33268     }
33269     // handle view.xtype
33270     
33271     if (this.view && typeof(this.view.xtype) != 'undefined') {
33272         this.view.el = this.el.appendChild(document.createElement("div"));
33273         this.view = Roo.factory(this.view);
33274         this.view.render && this.view.render(false, ''); // render blank..
33275     }
33276     
33277     
33278     
33279     this.addEvents({
33280         /**
33281          * @event activate
33282          * Fires when this panel is activated. 
33283          * @param {Roo.ContentPanel} this
33284          */
33285         "activate" : true,
33286         /**
33287          * @event deactivate
33288          * Fires when this panel is activated. 
33289          * @param {Roo.ContentPanel} this
33290          */
33291         "deactivate" : true,
33292
33293         /**
33294          * @event resize
33295          * Fires when this panel is resized if fitToFrame is true.
33296          * @param {Roo.ContentPanel} this
33297          * @param {Number} width The width after any component adjustments
33298          * @param {Number} height The height after any component adjustments
33299          */
33300         "resize" : true,
33301         
33302          /**
33303          * @event render
33304          * Fires when this tab is created
33305          * @param {Roo.ContentPanel} this
33306          */
33307         "render" : true
33308         
33309         
33310         
33311     });
33312     if(this.autoScroll){
33313         this.resizeEl.setStyle("overflow", "auto");
33314     } else {
33315         // fix randome scrolling
33316         this.el.on('scroll', function() {
33317             Roo.log('fix random scolling');
33318             this.scrollTo('top',0); 
33319         });
33320     }
33321     content = content || this.content;
33322     if(content){
33323         this.setContent(content);
33324     }
33325     if(config && config.url){
33326         this.setUrl(this.url, this.params, this.loadOnce);
33327     }
33328     
33329     
33330     
33331     Roo.ContentPanel.superclass.constructor.call(this);
33332     
33333     this.fireEvent('render', this);
33334 };
33335
33336 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33337     tabTip:'',
33338     setRegion : function(region){
33339         this.region = region;
33340         if(region){
33341            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33342         }else{
33343            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33344         } 
33345     },
33346     
33347     /**
33348      * Returns the toolbar for this Panel if one was configured. 
33349      * @return {Roo.Toolbar} 
33350      */
33351     getToolbar : function(){
33352         return this.toolbar;
33353     },
33354     
33355     setActiveState : function(active){
33356         this.active = active;
33357         if(!active){
33358             this.fireEvent("deactivate", this);
33359         }else{
33360             this.fireEvent("activate", this);
33361         }
33362     },
33363     /**
33364      * Updates this panel's element
33365      * @param {String} content The new content
33366      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33367     */
33368     setContent : function(content, loadScripts){
33369         this.el.update(content, loadScripts);
33370     },
33371
33372     ignoreResize : function(w, h){
33373         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33374             return true;
33375         }else{
33376             this.lastSize = {width: w, height: h};
33377             return false;
33378         }
33379     },
33380     /**
33381      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33382      * @return {Roo.UpdateManager} The UpdateManager
33383      */
33384     getUpdateManager : function(){
33385         return this.el.getUpdateManager();
33386     },
33387      /**
33388      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33389      * @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:
33390 <pre><code>
33391 panel.load({
33392     url: "your-url.php",
33393     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33394     callback: yourFunction,
33395     scope: yourObject, //(optional scope)
33396     discardUrl: false,
33397     nocache: false,
33398     text: "Loading...",
33399     timeout: 30,
33400     scripts: false
33401 });
33402 </code></pre>
33403      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33404      * 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.
33405      * @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}
33406      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33407      * @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.
33408      * @return {Roo.ContentPanel} this
33409      */
33410     load : function(){
33411         var um = this.el.getUpdateManager();
33412         um.update.apply(um, arguments);
33413         return this;
33414     },
33415
33416
33417     /**
33418      * 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.
33419      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33420      * @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)
33421      * @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)
33422      * @return {Roo.UpdateManager} The UpdateManager
33423      */
33424     setUrl : function(url, params, loadOnce){
33425         if(this.refreshDelegate){
33426             this.removeListener("activate", this.refreshDelegate);
33427         }
33428         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33429         this.on("activate", this.refreshDelegate);
33430         return this.el.getUpdateManager();
33431     },
33432     
33433     _handleRefresh : function(url, params, loadOnce){
33434         if(!loadOnce || !this.loaded){
33435             var updater = this.el.getUpdateManager();
33436             updater.update(url, params, this._setLoaded.createDelegate(this));
33437         }
33438     },
33439     
33440     _setLoaded : function(){
33441         this.loaded = true;
33442     }, 
33443     
33444     /**
33445      * Returns this panel's id
33446      * @return {String} 
33447      */
33448     getId : function(){
33449         return this.el.id;
33450     },
33451     
33452     /** 
33453      * Returns this panel's element - used by regiosn to add.
33454      * @return {Roo.Element} 
33455      */
33456     getEl : function(){
33457         return this.wrapEl || this.el;
33458     },
33459     
33460     adjustForComponents : function(width, height)
33461     {
33462         Roo.log('adjustForComponents ');
33463         if(this.resizeEl != this.el){
33464             width -= this.el.getFrameWidth('lr');
33465             height -= this.el.getFrameWidth('tb');
33466         }
33467         if(this.toolbar){
33468             var te = this.toolbar.getEl();
33469             height -= te.getHeight();
33470             te.setWidth(width);
33471         }
33472         if(this.footer){
33473             var te = this.footer.getEl();
33474             Roo.log("footer:" + te.getHeight());
33475             
33476             height -= te.getHeight();
33477             te.setWidth(width);
33478         }
33479         
33480         
33481         if(this.adjustments){
33482             width += this.adjustments[0];
33483             height += this.adjustments[1];
33484         }
33485         return {"width": width, "height": height};
33486     },
33487     
33488     setSize : function(width, height){
33489         if(this.fitToFrame && !this.ignoreResize(width, height)){
33490             if(this.fitContainer && this.resizeEl != this.el){
33491                 this.el.setSize(width, height);
33492             }
33493             var size = this.adjustForComponents(width, height);
33494             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33495             this.fireEvent('resize', this, size.width, size.height);
33496         }
33497     },
33498     
33499     /**
33500      * Returns this panel's title
33501      * @return {String} 
33502      */
33503     getTitle : function(){
33504         return this.title;
33505     },
33506     
33507     /**
33508      * Set this panel's title
33509      * @param {String} title
33510      */
33511     setTitle : function(title){
33512         this.title = title;
33513         if(this.region){
33514             this.region.updatePanelTitle(this, title);
33515         }
33516     },
33517     
33518     /**
33519      * Returns true is this panel was configured to be closable
33520      * @return {Boolean} 
33521      */
33522     isClosable : function(){
33523         return this.closable;
33524     },
33525     
33526     beforeSlide : function(){
33527         this.el.clip();
33528         this.resizeEl.clip();
33529     },
33530     
33531     afterSlide : function(){
33532         this.el.unclip();
33533         this.resizeEl.unclip();
33534     },
33535     
33536     /**
33537      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33538      *   Will fail silently if the {@link #setUrl} method has not been called.
33539      *   This does not activate the panel, just updates its content.
33540      */
33541     refresh : function(){
33542         if(this.refreshDelegate){
33543            this.loaded = false;
33544            this.refreshDelegate();
33545         }
33546     },
33547     
33548     /**
33549      * Destroys this panel
33550      */
33551     destroy : function(){
33552         this.el.removeAllListeners();
33553         var tempEl = document.createElement("span");
33554         tempEl.appendChild(this.el.dom);
33555         tempEl.innerHTML = "";
33556         this.el.remove();
33557         this.el = null;
33558     },
33559     
33560     /**
33561      * form - if the content panel contains a form - this is a reference to it.
33562      * @type {Roo.form.Form}
33563      */
33564     form : false,
33565     /**
33566      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33567      *    This contains a reference to it.
33568      * @type {Roo.View}
33569      */
33570     view : false,
33571     
33572       /**
33573      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33574      * <pre><code>
33575
33576 layout.addxtype({
33577        xtype : 'Form',
33578        items: [ .... ]
33579    }
33580 );
33581
33582 </code></pre>
33583      * @param {Object} cfg Xtype definition of item to add.
33584      */
33585     
33586     addxtype : function(cfg) {
33587         // add form..
33588         if (cfg.xtype.match(/^Form$/)) {
33589             
33590             var el;
33591             //if (this.footer) {
33592             //    el = this.footer.container.insertSibling(false, 'before');
33593             //} else {
33594                 el = this.el.createChild();
33595             //}
33596
33597             this.form = new  Roo.form.Form(cfg);
33598             
33599             
33600             if ( this.form.allItems.length) this.form.render(el.dom);
33601             return this.form;
33602         }
33603         // should only have one of theses..
33604         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33605             // views..
33606             cfg.el = this.el.appendChild(document.createElement("div"));
33607             // factory?
33608             
33609             var ret = new Roo.factory(cfg);
33610             ret.render && ret.render(false, ''); // render blank..
33611             this.view = ret;
33612             return ret;
33613         }
33614         return false;
33615     }
33616 });
33617
33618 /**
33619  * @class Roo.GridPanel
33620  * @extends Roo.ContentPanel
33621  * @constructor
33622  * Create a new GridPanel.
33623  * @param {Roo.grid.Grid} grid The grid for this panel
33624  * @param {String/Object} config A string to set only the panel's title, or a config object
33625  */
33626 Roo.GridPanel = function(grid, config){
33627     
33628   
33629     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33630         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33631         
33632     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33633     
33634     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33635     
33636     if(this.toolbar){
33637         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33638     }
33639     // xtype created footer. - not sure if will work as we normally have to render first..
33640     if (this.footer && !this.footer.el && this.footer.xtype) {
33641         
33642         this.footer.container = this.grid.getView().getFooterPanel(true);
33643         this.footer.dataSource = this.grid.dataSource;
33644         this.footer = Roo.factory(this.footer, Roo);
33645         
33646     }
33647     
33648     grid.monitorWindowResize = false; // turn off autosizing
33649     grid.autoHeight = false;
33650     grid.autoWidth = false;
33651     this.grid = grid;
33652     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33653 };
33654
33655 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33656     getId : function(){
33657         return this.grid.id;
33658     },
33659     
33660     /**
33661      * Returns the grid for this panel
33662      * @return {Roo.grid.Grid} 
33663      */
33664     getGrid : function(){
33665         return this.grid;    
33666     },
33667     
33668     setSize : function(width, height){
33669         if(!this.ignoreResize(width, height)){
33670             var grid = this.grid;
33671             var size = this.adjustForComponents(width, height);
33672             grid.getGridEl().setSize(size.width, size.height);
33673             grid.autoSize();
33674         }
33675     },
33676     
33677     beforeSlide : function(){
33678         this.grid.getView().scroller.clip();
33679     },
33680     
33681     afterSlide : function(){
33682         this.grid.getView().scroller.unclip();
33683     },
33684     
33685     destroy : function(){
33686         this.grid.destroy();
33687         delete this.grid;
33688         Roo.GridPanel.superclass.destroy.call(this); 
33689     }
33690 });
33691
33692
33693 /**
33694  * @class Roo.NestedLayoutPanel
33695  * @extends Roo.ContentPanel
33696  * @constructor
33697  * Create a new NestedLayoutPanel.
33698  * 
33699  * 
33700  * @param {Roo.BorderLayout} layout The layout for this panel
33701  * @param {String/Object} config A string to set only the title or a config object
33702  */
33703 Roo.NestedLayoutPanel = function(layout, config)
33704 {
33705     // construct with only one argument..
33706     /* FIXME - implement nicer consturctors
33707     if (layout.layout) {
33708         config = layout;
33709         layout = config.layout;
33710         delete config.layout;
33711     }
33712     if (layout.xtype && !layout.getEl) {
33713         // then layout needs constructing..
33714         layout = Roo.factory(layout, Roo);
33715     }
33716     */
33717     
33718     
33719     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33720     
33721     layout.monitorWindowResize = false; // turn off autosizing
33722     this.layout = layout;
33723     this.layout.getEl().addClass("x-layout-nested-layout");
33724     
33725     
33726     
33727     
33728 };
33729
33730 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33731
33732     setSize : function(width, height){
33733         if(!this.ignoreResize(width, height)){
33734             var size = this.adjustForComponents(width, height);
33735             var el = this.layout.getEl();
33736             el.setSize(size.width, size.height);
33737             var touch = el.dom.offsetWidth;
33738             this.layout.layout();
33739             // ie requires a double layout on the first pass
33740             if(Roo.isIE && !this.initialized){
33741                 this.initialized = true;
33742                 this.layout.layout();
33743             }
33744         }
33745     },
33746     
33747     // activate all subpanels if not currently active..
33748     
33749     setActiveState : function(active){
33750         this.active = active;
33751         if(!active){
33752             this.fireEvent("deactivate", this);
33753             return;
33754         }
33755         
33756         this.fireEvent("activate", this);
33757         // not sure if this should happen before or after..
33758         if (!this.layout) {
33759             return; // should not happen..
33760         }
33761         var reg = false;
33762         for (var r in this.layout.regions) {
33763             reg = this.layout.getRegion(r);
33764             if (reg.getActivePanel()) {
33765                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33766                 reg.setActivePanel(reg.getActivePanel());
33767                 continue;
33768             }
33769             if (!reg.panels.length) {
33770                 continue;
33771             }
33772             reg.showPanel(reg.getPanel(0));
33773         }
33774         
33775         
33776         
33777         
33778     },
33779     
33780     /**
33781      * Returns the nested BorderLayout for this panel
33782      * @return {Roo.BorderLayout} 
33783      */
33784     getLayout : function(){
33785         return this.layout;
33786     },
33787     
33788      /**
33789      * Adds a xtype elements to the layout of the nested panel
33790      * <pre><code>
33791
33792 panel.addxtype({
33793        xtype : 'ContentPanel',
33794        region: 'west',
33795        items: [ .... ]
33796    }
33797 );
33798
33799 panel.addxtype({
33800         xtype : 'NestedLayoutPanel',
33801         region: 'west',
33802         layout: {
33803            center: { },
33804            west: { }   
33805         },
33806         items : [ ... list of content panels or nested layout panels.. ]
33807    }
33808 );
33809 </code></pre>
33810      * @param {Object} cfg Xtype definition of item to add.
33811      */
33812     addxtype : function(cfg) {
33813         return this.layout.addxtype(cfg);
33814     
33815     }
33816 });
33817
33818 Roo.ScrollPanel = function(el, config, content){
33819     config = config || {};
33820     config.fitToFrame = true;
33821     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33822     
33823     this.el.dom.style.overflow = "hidden";
33824     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33825     this.el.removeClass("x-layout-inactive-content");
33826     this.el.on("mousewheel", this.onWheel, this);
33827
33828     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33829     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33830     up.unselectable(); down.unselectable();
33831     up.on("click", this.scrollUp, this);
33832     down.on("click", this.scrollDown, this);
33833     up.addClassOnOver("x-scroller-btn-over");
33834     down.addClassOnOver("x-scroller-btn-over");
33835     up.addClassOnClick("x-scroller-btn-click");
33836     down.addClassOnClick("x-scroller-btn-click");
33837     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33838
33839     this.resizeEl = this.el;
33840     this.el = wrap; this.up = up; this.down = down;
33841 };
33842
33843 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33844     increment : 100,
33845     wheelIncrement : 5,
33846     scrollUp : function(){
33847         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33848     },
33849
33850     scrollDown : function(){
33851         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33852     },
33853
33854     afterScroll : function(){
33855         var el = this.resizeEl;
33856         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33857         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33858         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33859     },
33860
33861     setSize : function(){
33862         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33863         this.afterScroll();
33864     },
33865
33866     onWheel : function(e){
33867         var d = e.getWheelDelta();
33868         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33869         this.afterScroll();
33870         e.stopEvent();
33871     },
33872
33873     setContent : function(content, loadScripts){
33874         this.resizeEl.update(content, loadScripts);
33875     }
33876
33877 });
33878
33879
33880
33881
33882
33883
33884
33885
33886
33887 /**
33888  * @class Roo.TreePanel
33889  * @extends Roo.ContentPanel
33890  * @constructor
33891  * Create a new TreePanel. - defaults to fit/scoll contents.
33892  * @param {String/Object} config A string to set only the panel's title, or a config object
33893  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33894  */
33895 Roo.TreePanel = function(config){
33896     var el = config.el;
33897     var tree = config.tree;
33898     delete config.tree; 
33899     delete config.el; // hopefull!
33900     
33901     // wrapper for IE7 strict & safari scroll issue
33902     
33903     var treeEl = el.createChild();
33904     config.resizeEl = treeEl;
33905     
33906     
33907     
33908     Roo.TreePanel.superclass.constructor.call(this, el, config);
33909  
33910  
33911     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33912     //console.log(tree);
33913     this.on('activate', function()
33914     {
33915         if (this.tree.rendered) {
33916             return;
33917         }
33918         //console.log('render tree');
33919         this.tree.render();
33920     });
33921     // this should not be needed.. - it's actually the 'el' that resizes?
33922     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33923     
33924     //this.on('resize',  function (cp, w, h) {
33925     //        this.tree.innerCt.setWidth(w);
33926     //        this.tree.innerCt.setHeight(h);
33927     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33928     //});
33929
33930         
33931     
33932 };
33933
33934 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33935     fitToFrame : true,
33936     autoScroll : true
33937 });
33938
33939
33940
33941
33942
33943
33944
33945
33946
33947
33948
33949 /*
33950  * Based on:
33951  * Ext JS Library 1.1.1
33952  * Copyright(c) 2006-2007, Ext JS, LLC.
33953  *
33954  * Originally Released Under LGPL - original licence link has changed is not relivant.
33955  *
33956  * Fork - LGPL
33957  * <script type="text/javascript">
33958  */
33959  
33960
33961 /**
33962  * @class Roo.ReaderLayout
33963  * @extends Roo.BorderLayout
33964  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33965  * center region containing two nested regions (a top one for a list view and one for item preview below),
33966  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33967  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33968  * expedites the setup of the overall layout and regions for this common application style.
33969  * Example:
33970  <pre><code>
33971 var reader = new Roo.ReaderLayout();
33972 var CP = Roo.ContentPanel;  // shortcut for adding
33973
33974 reader.beginUpdate();
33975 reader.add("north", new CP("north", "North"));
33976 reader.add("west", new CP("west", {title: "West"}));
33977 reader.add("east", new CP("east", {title: "East"}));
33978
33979 reader.regions.listView.add(new CP("listView", "List"));
33980 reader.regions.preview.add(new CP("preview", "Preview"));
33981 reader.endUpdate();
33982 </code></pre>
33983 * @constructor
33984 * Create a new ReaderLayout
33985 * @param {Object} config Configuration options
33986 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33987 * document.body if omitted)
33988 */
33989 Roo.ReaderLayout = function(config, renderTo){
33990     var c = config || {size:{}};
33991     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33992         north: c.north !== false ? Roo.apply({
33993             split:false,
33994             initialSize: 32,
33995             titlebar: false
33996         }, c.north) : false,
33997         west: c.west !== false ? Roo.apply({
33998             split:true,
33999             initialSize: 200,
34000             minSize: 175,
34001             maxSize: 400,
34002             titlebar: true,
34003             collapsible: true,
34004             animate: true,
34005             margins:{left:5,right:0,bottom:5,top:5},
34006             cmargins:{left:5,right:5,bottom:5,top:5}
34007         }, c.west) : false,
34008         east: c.east !== false ? Roo.apply({
34009             split:true,
34010             initialSize: 200,
34011             minSize: 175,
34012             maxSize: 400,
34013             titlebar: true,
34014             collapsible: true,
34015             animate: true,
34016             margins:{left:0,right:5,bottom:5,top:5},
34017             cmargins:{left:5,right:5,bottom:5,top:5}
34018         }, c.east) : false,
34019         center: Roo.apply({
34020             tabPosition: 'top',
34021             autoScroll:false,
34022             closeOnTab: true,
34023             titlebar:false,
34024             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
34025         }, c.center)
34026     });
34027
34028     this.el.addClass('x-reader');
34029
34030     this.beginUpdate();
34031
34032     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
34033         south: c.preview !== false ? Roo.apply({
34034             split:true,
34035             initialSize: 200,
34036             minSize: 100,
34037             autoScroll:true,
34038             collapsible:true,
34039             titlebar: true,
34040             cmargins:{top:5,left:0, right:0, bottom:0}
34041         }, c.preview) : false,
34042         center: Roo.apply({
34043             autoScroll:false,
34044             titlebar:false,
34045             minHeight:200
34046         }, c.listView)
34047     });
34048     this.add('center', new Roo.NestedLayoutPanel(inner,
34049             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
34050
34051     this.endUpdate();
34052
34053     this.regions.preview = inner.getRegion('south');
34054     this.regions.listView = inner.getRegion('center');
34055 };
34056
34057 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
34058  * Based on:
34059  * Ext JS Library 1.1.1
34060  * Copyright(c) 2006-2007, Ext JS, LLC.
34061  *
34062  * Originally Released Under LGPL - original licence link has changed is not relivant.
34063  *
34064  * Fork - LGPL
34065  * <script type="text/javascript">
34066  */
34067  
34068 /**
34069  * @class Roo.grid.Grid
34070  * @extends Roo.util.Observable
34071  * This class represents the primary interface of a component based grid control.
34072  * <br><br>Usage:<pre><code>
34073  var grid = new Roo.grid.Grid("my-container-id", {
34074      ds: myDataStore,
34075      cm: myColModel,
34076      selModel: mySelectionModel,
34077      autoSizeColumns: true,
34078      monitorWindowResize: false,
34079      trackMouseOver: true
34080  });
34081  // set any options
34082  grid.render();
34083  * </code></pre>
34084  * <b>Common Problems:</b><br/>
34085  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
34086  * element will correct this<br/>
34087  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
34088  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
34089  * are unpredictable.<br/>
34090  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
34091  * grid to calculate dimensions/offsets.<br/>
34092   * @constructor
34093  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
34094  * The container MUST have some type of size defined for the grid to fill. The container will be
34095  * automatically set to position relative if it isn't already.
34096  * @param {Object} config A config object that sets properties on this grid.
34097  */
34098 Roo.grid.Grid = function(container, config){
34099         // initialize the container
34100         this.container = Roo.get(container);
34101         this.container.update("");
34102         this.container.setStyle("overflow", "hidden");
34103     this.container.addClass('x-grid-container');
34104
34105     this.id = this.container.id;
34106
34107     Roo.apply(this, config);
34108     // check and correct shorthanded configs
34109     if(this.ds){
34110         this.dataSource = this.ds;
34111         delete this.ds;
34112     }
34113     if(this.cm){
34114         this.colModel = this.cm;
34115         delete this.cm;
34116     }
34117     if(this.sm){
34118         this.selModel = this.sm;
34119         delete this.sm;
34120     }
34121
34122     if (this.selModel) {
34123         this.selModel = Roo.factory(this.selModel, Roo.grid);
34124         this.sm = this.selModel;
34125         this.sm.xmodule = this.xmodule || false;
34126     }
34127     if (typeof(this.colModel.config) == 'undefined') {
34128         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34129         this.cm = this.colModel;
34130         this.cm.xmodule = this.xmodule || false;
34131     }
34132     if (this.dataSource) {
34133         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34134         this.ds = this.dataSource;
34135         this.ds.xmodule = this.xmodule || false;
34136          
34137     }
34138     
34139     
34140     
34141     if(this.width){
34142         this.container.setWidth(this.width);
34143     }
34144
34145     if(this.height){
34146         this.container.setHeight(this.height);
34147     }
34148     /** @private */
34149         this.addEvents({
34150         // raw events
34151         /**
34152          * @event click
34153          * The raw click event for the entire grid.
34154          * @param {Roo.EventObject} e
34155          */
34156         "click" : true,
34157         /**
34158          * @event dblclick
34159          * The raw dblclick event for the entire grid.
34160          * @param {Roo.EventObject} e
34161          */
34162         "dblclick" : true,
34163         /**
34164          * @event contextmenu
34165          * The raw contextmenu event for the entire grid.
34166          * @param {Roo.EventObject} e
34167          */
34168         "contextmenu" : true,
34169         /**
34170          * @event mousedown
34171          * The raw mousedown event for the entire grid.
34172          * @param {Roo.EventObject} e
34173          */
34174         "mousedown" : true,
34175         /**
34176          * @event mouseup
34177          * The raw mouseup event for the entire grid.
34178          * @param {Roo.EventObject} e
34179          */
34180         "mouseup" : true,
34181         /**
34182          * @event mouseover
34183          * The raw mouseover event for the entire grid.
34184          * @param {Roo.EventObject} e
34185          */
34186         "mouseover" : true,
34187         /**
34188          * @event mouseout
34189          * The raw mouseout event for the entire grid.
34190          * @param {Roo.EventObject} e
34191          */
34192         "mouseout" : true,
34193         /**
34194          * @event keypress
34195          * The raw keypress event for the entire grid.
34196          * @param {Roo.EventObject} e
34197          */
34198         "keypress" : true,
34199         /**
34200          * @event keydown
34201          * The raw keydown event for the entire grid.
34202          * @param {Roo.EventObject} e
34203          */
34204         "keydown" : true,
34205
34206         // custom events
34207
34208         /**
34209          * @event cellclick
34210          * Fires when a cell is clicked
34211          * @param {Grid} this
34212          * @param {Number} rowIndex
34213          * @param {Number} columnIndex
34214          * @param {Roo.EventObject} e
34215          */
34216         "cellclick" : true,
34217         /**
34218          * @event celldblclick
34219          * Fires when a cell is double clicked
34220          * @param {Grid} this
34221          * @param {Number} rowIndex
34222          * @param {Number} columnIndex
34223          * @param {Roo.EventObject} e
34224          */
34225         "celldblclick" : true,
34226         /**
34227          * @event rowclick
34228          * Fires when a row is clicked
34229          * @param {Grid} this
34230          * @param {Number} rowIndex
34231          * @param {Roo.EventObject} e
34232          */
34233         "rowclick" : true,
34234         /**
34235          * @event rowdblclick
34236          * Fires when a row is double clicked
34237          * @param {Grid} this
34238          * @param {Number} rowIndex
34239          * @param {Roo.EventObject} e
34240          */
34241         "rowdblclick" : true,
34242         /**
34243          * @event headerclick
34244          * Fires when a header is clicked
34245          * @param {Grid} this
34246          * @param {Number} columnIndex
34247          * @param {Roo.EventObject} e
34248          */
34249         "headerclick" : true,
34250         /**
34251          * @event headerdblclick
34252          * Fires when a header cell is double clicked
34253          * @param {Grid} this
34254          * @param {Number} columnIndex
34255          * @param {Roo.EventObject} e
34256          */
34257         "headerdblclick" : true,
34258         /**
34259          * @event rowcontextmenu
34260          * Fires when a row is right clicked
34261          * @param {Grid} this
34262          * @param {Number} rowIndex
34263          * @param {Roo.EventObject} e
34264          */
34265         "rowcontextmenu" : true,
34266         /**
34267          * @event cellcontextmenu
34268          * Fires when a cell is right clicked
34269          * @param {Grid} this
34270          * @param {Number} rowIndex
34271          * @param {Number} cellIndex
34272          * @param {Roo.EventObject} e
34273          */
34274          "cellcontextmenu" : true,
34275         /**
34276          * @event headercontextmenu
34277          * Fires when a header is right clicked
34278          * @param {Grid} this
34279          * @param {Number} columnIndex
34280          * @param {Roo.EventObject} e
34281          */
34282         "headercontextmenu" : true,
34283         /**
34284          * @event bodyscroll
34285          * Fires when the body element is scrolled
34286          * @param {Number} scrollLeft
34287          * @param {Number} scrollTop
34288          */
34289         "bodyscroll" : true,
34290         /**
34291          * @event columnresize
34292          * Fires when the user resizes a column
34293          * @param {Number} columnIndex
34294          * @param {Number} newSize
34295          */
34296         "columnresize" : true,
34297         /**
34298          * @event columnmove
34299          * Fires when the user moves a column
34300          * @param {Number} oldIndex
34301          * @param {Number} newIndex
34302          */
34303         "columnmove" : true,
34304         /**
34305          * @event startdrag
34306          * Fires when row(s) start being dragged
34307          * @param {Grid} this
34308          * @param {Roo.GridDD} dd The drag drop object
34309          * @param {event} e The raw browser event
34310          */
34311         "startdrag" : true,
34312         /**
34313          * @event enddrag
34314          * Fires when a drag operation is complete
34315          * @param {Grid} this
34316          * @param {Roo.GridDD} dd The drag drop object
34317          * @param {event} e The raw browser event
34318          */
34319         "enddrag" : true,
34320         /**
34321          * @event dragdrop
34322          * Fires when dragged row(s) are dropped on a valid DD target
34323          * @param {Grid} this
34324          * @param {Roo.GridDD} dd The drag drop object
34325          * @param {String} targetId The target drag drop object
34326          * @param {event} e The raw browser event
34327          */
34328         "dragdrop" : true,
34329         /**
34330          * @event dragover
34331          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34332          * @param {Grid} this
34333          * @param {Roo.GridDD} dd The drag drop object
34334          * @param {String} targetId The target drag drop object
34335          * @param {event} e The raw browser event
34336          */
34337         "dragover" : true,
34338         /**
34339          * @event dragenter
34340          *  Fires when the dragged row(s) first cross another DD target while being dragged
34341          * @param {Grid} this
34342          * @param {Roo.GridDD} dd The drag drop object
34343          * @param {String} targetId The target drag drop object
34344          * @param {event} e The raw browser event
34345          */
34346         "dragenter" : true,
34347         /**
34348          * @event dragout
34349          * Fires when the dragged row(s) leave another DD target while being dragged
34350          * @param {Grid} this
34351          * @param {Roo.GridDD} dd The drag drop object
34352          * @param {String} targetId The target drag drop object
34353          * @param {event} e The raw browser event
34354          */
34355         "dragout" : true,
34356         /**
34357          * @event rowclass
34358          * Fires when a row is rendered, so you can change add a style to it.
34359          * @param {GridView} gridview   The grid view
34360          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
34361          */
34362         'rowclass' : true,
34363
34364         /**
34365          * @event render
34366          * Fires when the grid is rendered
34367          * @param {Grid} grid
34368          */
34369         'render' : true
34370     });
34371
34372     Roo.grid.Grid.superclass.constructor.call(this);
34373 };
34374 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34375     
34376     /**
34377      * @cfg {String} ddGroup - drag drop group.
34378      */
34379
34380     /**
34381      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34382      */
34383     minColumnWidth : 25,
34384
34385     /**
34386      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34387      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34388      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34389      */
34390     autoSizeColumns : false,
34391
34392     /**
34393      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34394      */
34395     autoSizeHeaders : true,
34396
34397     /**
34398      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34399      */
34400     monitorWindowResize : true,
34401
34402     /**
34403      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34404      * rows measured to get a columns size. Default is 0 (all rows).
34405      */
34406     maxRowsToMeasure : 0,
34407
34408     /**
34409      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34410      */
34411     trackMouseOver : true,
34412
34413     /**
34414     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34415     */
34416     
34417     /**
34418     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34419     */
34420     enableDragDrop : false,
34421     
34422     /**
34423     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34424     */
34425     enableColumnMove : true,
34426     
34427     /**
34428     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34429     */
34430     enableColumnHide : true,
34431     
34432     /**
34433     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34434     */
34435     enableRowHeightSync : false,
34436     
34437     /**
34438     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34439     */
34440     stripeRows : true,
34441     
34442     /**
34443     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34444     */
34445     autoHeight : false,
34446
34447     /**
34448      * @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.
34449      */
34450     autoExpandColumn : false,
34451
34452     /**
34453     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34454     * Default is 50.
34455     */
34456     autoExpandMin : 50,
34457
34458     /**
34459     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34460     */
34461     autoExpandMax : 1000,
34462
34463     /**
34464     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34465     */
34466     view : null,
34467
34468     /**
34469     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34470     */
34471     loadMask : false,
34472     /**
34473     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
34474     */
34475     dropTarget: false,
34476     
34477    
34478     
34479     // private
34480     rendered : false,
34481
34482     /**
34483     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34484     * of a fixed width. Default is false.
34485     */
34486     /**
34487     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34488     */
34489     /**
34490      * Called once after all setup has been completed and the grid is ready to be rendered.
34491      * @return {Roo.grid.Grid} this
34492      */
34493     render : function()
34494     {
34495         var c = this.container;
34496         // try to detect autoHeight/width mode
34497         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34498             this.autoHeight = true;
34499         }
34500         var view = this.getView();
34501         view.init(this);
34502
34503         c.on("click", this.onClick, this);
34504         c.on("dblclick", this.onDblClick, this);
34505         c.on("contextmenu", this.onContextMenu, this);
34506         c.on("keydown", this.onKeyDown, this);
34507
34508         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34509
34510         this.getSelectionModel().init(this);
34511
34512         view.render();
34513
34514         if(this.loadMask){
34515             this.loadMask = new Roo.LoadMask(this.container,
34516                     Roo.apply({store:this.dataSource}, this.loadMask));
34517         }
34518         
34519         
34520         if (this.toolbar && this.toolbar.xtype) {
34521             this.toolbar.container = this.getView().getHeaderPanel(true);
34522             this.toolbar = new Roo.Toolbar(this.toolbar);
34523         }
34524         if (this.footer && this.footer.xtype) {
34525             this.footer.dataSource = this.getDataSource();
34526             this.footer.container = this.getView().getFooterPanel(true);
34527             this.footer = Roo.factory(this.footer, Roo);
34528         }
34529         if (this.dropTarget && this.dropTarget.xtype) {
34530             delete this.dropTarget.xtype;
34531             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34532         }
34533         
34534         
34535         this.rendered = true;
34536         this.fireEvent('render', this);
34537         return this;
34538     },
34539
34540         /**
34541          * Reconfigures the grid to use a different Store and Column Model.
34542          * The View will be bound to the new objects and refreshed.
34543          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34544          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34545          */
34546     reconfigure : function(dataSource, colModel){
34547         if(this.loadMask){
34548             this.loadMask.destroy();
34549             this.loadMask = new Roo.LoadMask(this.container,
34550                     Roo.apply({store:dataSource}, this.loadMask));
34551         }
34552         this.view.bind(dataSource, colModel);
34553         this.dataSource = dataSource;
34554         this.colModel = colModel;
34555         this.view.refresh(true);
34556     },
34557
34558     // private
34559     onKeyDown : function(e){
34560         this.fireEvent("keydown", e);
34561     },
34562
34563     /**
34564      * Destroy this grid.
34565      * @param {Boolean} removeEl True to remove the element
34566      */
34567     destroy : function(removeEl, keepListeners){
34568         if(this.loadMask){
34569             this.loadMask.destroy();
34570         }
34571         var c = this.container;
34572         c.removeAllListeners();
34573         this.view.destroy();
34574         this.colModel.purgeListeners();
34575         if(!keepListeners){
34576             this.purgeListeners();
34577         }
34578         c.update("");
34579         if(removeEl === true){
34580             c.remove();
34581         }
34582     },
34583
34584     // private
34585     processEvent : function(name, e){
34586         this.fireEvent(name, e);
34587         var t = e.getTarget();
34588         var v = this.view;
34589         var header = v.findHeaderIndex(t);
34590         if(header !== false){
34591             this.fireEvent("header" + name, this, header, e);
34592         }else{
34593             var row = v.findRowIndex(t);
34594             var cell = v.findCellIndex(t);
34595             if(row !== false){
34596                 this.fireEvent("row" + name, this, row, e);
34597                 if(cell !== false){
34598                     this.fireEvent("cell" + name, this, row, cell, e);
34599                 }
34600             }
34601         }
34602     },
34603
34604     // private
34605     onClick : function(e){
34606         this.processEvent("click", e);
34607     },
34608
34609     // private
34610     onContextMenu : function(e, t){
34611         this.processEvent("contextmenu", e);
34612     },
34613
34614     // private
34615     onDblClick : function(e){
34616         this.processEvent("dblclick", e);
34617     },
34618
34619     // private
34620     walkCells : function(row, col, step, fn, scope){
34621         var cm = this.colModel, clen = cm.getColumnCount();
34622         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34623         if(step < 0){
34624             if(col < 0){
34625                 row--;
34626                 first = false;
34627             }
34628             while(row >= 0){
34629                 if(!first){
34630                     col = clen-1;
34631                 }
34632                 first = false;
34633                 while(col >= 0){
34634                     if(fn.call(scope || this, row, col, cm) === true){
34635                         return [row, col];
34636                     }
34637                     col--;
34638                 }
34639                 row--;
34640             }
34641         } else {
34642             if(col >= clen){
34643                 row++;
34644                 first = false;
34645             }
34646             while(row < rlen){
34647                 if(!first){
34648                     col = 0;
34649                 }
34650                 first = false;
34651                 while(col < clen){
34652                     if(fn.call(scope || this, row, col, cm) === true){
34653                         return [row, col];
34654                     }
34655                     col++;
34656                 }
34657                 row++;
34658             }
34659         }
34660         return null;
34661     },
34662
34663     // private
34664     getSelections : function(){
34665         return this.selModel.getSelections();
34666     },
34667
34668     /**
34669      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34670      * but if manual update is required this method will initiate it.
34671      */
34672     autoSize : function(){
34673         if(this.rendered){
34674             this.view.layout();
34675             if(this.view.adjustForScroll){
34676                 this.view.adjustForScroll();
34677             }
34678         }
34679     },
34680
34681     /**
34682      * Returns the grid's underlying element.
34683      * @return {Element} The element
34684      */
34685     getGridEl : function(){
34686         return this.container;
34687     },
34688
34689     // private for compatibility, overridden by editor grid
34690     stopEditing : function(){},
34691
34692     /**
34693      * Returns the grid's SelectionModel.
34694      * @return {SelectionModel}
34695      */
34696     getSelectionModel : function(){
34697         if(!this.selModel){
34698             this.selModel = new Roo.grid.RowSelectionModel();
34699         }
34700         return this.selModel;
34701     },
34702
34703     /**
34704      * Returns the grid's DataSource.
34705      * @return {DataSource}
34706      */
34707     getDataSource : function(){
34708         return this.dataSource;
34709     },
34710
34711     /**
34712      * Returns the grid's ColumnModel.
34713      * @return {ColumnModel}
34714      */
34715     getColumnModel : function(){
34716         return this.colModel;
34717     },
34718
34719     /**
34720      * Returns the grid's GridView object.
34721      * @return {GridView}
34722      */
34723     getView : function(){
34724         if(!this.view){
34725             this.view = new Roo.grid.GridView(this.viewConfig);
34726         }
34727         return this.view;
34728     },
34729     /**
34730      * Called to get grid's drag proxy text, by default returns this.ddText.
34731      * @return {String}
34732      */
34733     getDragDropText : function(){
34734         var count = this.selModel.getCount();
34735         return String.format(this.ddText, count, count == 1 ? '' : 's');
34736     }
34737 });
34738 /**
34739  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34740  * %0 is replaced with the number of selected rows.
34741  * @type String
34742  */
34743 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34744  * Based on:
34745  * Ext JS Library 1.1.1
34746  * Copyright(c) 2006-2007, Ext JS, LLC.
34747  *
34748  * Originally Released Under LGPL - original licence link has changed is not relivant.
34749  *
34750  * Fork - LGPL
34751  * <script type="text/javascript">
34752  */
34753  
34754 Roo.grid.AbstractGridView = function(){
34755         this.grid = null;
34756         
34757         this.events = {
34758             "beforerowremoved" : true,
34759             "beforerowsinserted" : true,
34760             "beforerefresh" : true,
34761             "rowremoved" : true,
34762             "rowsinserted" : true,
34763             "rowupdated" : true,
34764             "refresh" : true
34765         };
34766     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34767 };
34768
34769 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34770     rowClass : "x-grid-row",
34771     cellClass : "x-grid-cell",
34772     tdClass : "x-grid-td",
34773     hdClass : "x-grid-hd",
34774     splitClass : "x-grid-hd-split",
34775     
34776         init: function(grid){
34777         this.grid = grid;
34778                 var cid = this.grid.getGridEl().id;
34779         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34780         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34781         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34782         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34783         },
34784         
34785         getColumnRenderers : function(){
34786         var renderers = [];
34787         var cm = this.grid.colModel;
34788         var colCount = cm.getColumnCount();
34789         for(var i = 0; i < colCount; i++){
34790             renderers[i] = cm.getRenderer(i);
34791         }
34792         return renderers;
34793     },
34794     
34795     getColumnIds : function(){
34796         var ids = [];
34797         var cm = this.grid.colModel;
34798         var colCount = cm.getColumnCount();
34799         for(var i = 0; i < colCount; i++){
34800             ids[i] = cm.getColumnId(i);
34801         }
34802         return ids;
34803     },
34804     
34805     getDataIndexes : function(){
34806         if(!this.indexMap){
34807             this.indexMap = this.buildIndexMap();
34808         }
34809         return this.indexMap.colToData;
34810     },
34811     
34812     getColumnIndexByDataIndex : function(dataIndex){
34813         if(!this.indexMap){
34814             this.indexMap = this.buildIndexMap();
34815         }
34816         return this.indexMap.dataToCol[dataIndex];
34817     },
34818     
34819     /**
34820      * Set a css style for a column dynamically. 
34821      * @param {Number} colIndex The index of the column
34822      * @param {String} name The css property name
34823      * @param {String} value The css value
34824      */
34825     setCSSStyle : function(colIndex, name, value){
34826         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34827         Roo.util.CSS.updateRule(selector, name, value);
34828     },
34829     
34830     generateRules : function(cm){
34831         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34832         Roo.util.CSS.removeStyleSheet(rulesId);
34833         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34834             var cid = cm.getColumnId(i);
34835             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34836                          this.tdSelector, cid, " {\n}\n",
34837                          this.hdSelector, cid, " {\n}\n",
34838                          this.splitSelector, cid, " {\n}\n");
34839         }
34840         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34841     }
34842 });/*
34843  * Based on:
34844  * Ext JS Library 1.1.1
34845  * Copyright(c) 2006-2007, Ext JS, LLC.
34846  *
34847  * Originally Released Under LGPL - original licence link has changed is not relivant.
34848  *
34849  * Fork - LGPL
34850  * <script type="text/javascript">
34851  */
34852
34853 // private
34854 // This is a support class used internally by the Grid components
34855 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34856     this.grid = grid;
34857     this.view = grid.getView();
34858     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34859     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34860     if(hd2){
34861         this.setHandleElId(Roo.id(hd));
34862         this.setOuterHandleElId(Roo.id(hd2));
34863     }
34864     this.scroll = false;
34865 };
34866 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34867     maxDragWidth: 120,
34868     getDragData : function(e){
34869         var t = Roo.lib.Event.getTarget(e);
34870         var h = this.view.findHeaderCell(t);
34871         if(h){
34872             return {ddel: h.firstChild, header:h};
34873         }
34874         return false;
34875     },
34876
34877     onInitDrag : function(e){
34878         this.view.headersDisabled = true;
34879         var clone = this.dragData.ddel.cloneNode(true);
34880         clone.id = Roo.id();
34881         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34882         this.proxy.update(clone);
34883         return true;
34884     },
34885
34886     afterValidDrop : function(){
34887         var v = this.view;
34888         setTimeout(function(){
34889             v.headersDisabled = false;
34890         }, 50);
34891     },
34892
34893     afterInvalidDrop : function(){
34894         var v = this.view;
34895         setTimeout(function(){
34896             v.headersDisabled = false;
34897         }, 50);
34898     }
34899 });
34900 /*
34901  * Based on:
34902  * Ext JS Library 1.1.1
34903  * Copyright(c) 2006-2007, Ext JS, LLC.
34904  *
34905  * Originally Released Under LGPL - original licence link has changed is not relivant.
34906  *
34907  * Fork - LGPL
34908  * <script type="text/javascript">
34909  */
34910 // private
34911 // This is a support class used internally by the Grid components
34912 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34913     this.grid = grid;
34914     this.view = grid.getView();
34915     // split the proxies so they don't interfere with mouse events
34916     this.proxyTop = Roo.DomHelper.append(document.body, {
34917         cls:"col-move-top", html:"&#160;"
34918     }, true);
34919     this.proxyBottom = Roo.DomHelper.append(document.body, {
34920         cls:"col-move-bottom", html:"&#160;"
34921     }, true);
34922     this.proxyTop.hide = this.proxyBottom.hide = function(){
34923         this.setLeftTop(-100,-100);
34924         this.setStyle("visibility", "hidden");
34925     };
34926     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34927     // temporarily disabled
34928     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34929     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34930 };
34931 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34932     proxyOffsets : [-4, -9],
34933     fly: Roo.Element.fly,
34934
34935     getTargetFromEvent : function(e){
34936         var t = Roo.lib.Event.getTarget(e);
34937         var cindex = this.view.findCellIndex(t);
34938         if(cindex !== false){
34939             return this.view.getHeaderCell(cindex);
34940         }
34941         return null;
34942     },
34943
34944     nextVisible : function(h){
34945         var v = this.view, cm = this.grid.colModel;
34946         h = h.nextSibling;
34947         while(h){
34948             if(!cm.isHidden(v.getCellIndex(h))){
34949                 return h;
34950             }
34951             h = h.nextSibling;
34952         }
34953         return null;
34954     },
34955
34956     prevVisible : function(h){
34957         var v = this.view, cm = this.grid.colModel;
34958         h = h.prevSibling;
34959         while(h){
34960             if(!cm.isHidden(v.getCellIndex(h))){
34961                 return h;
34962             }
34963             h = h.prevSibling;
34964         }
34965         return null;
34966     },
34967
34968     positionIndicator : function(h, n, e){
34969         var x = Roo.lib.Event.getPageX(e);
34970         var r = Roo.lib.Dom.getRegion(n.firstChild);
34971         var px, pt, py = r.top + this.proxyOffsets[1];
34972         if((r.right - x) <= (r.right-r.left)/2){
34973             px = r.right+this.view.borderWidth;
34974             pt = "after";
34975         }else{
34976             px = r.left;
34977             pt = "before";
34978         }
34979         var oldIndex = this.view.getCellIndex(h);
34980         var newIndex = this.view.getCellIndex(n);
34981
34982         if(this.grid.colModel.isFixed(newIndex)){
34983             return false;
34984         }
34985
34986         var locked = this.grid.colModel.isLocked(newIndex);
34987
34988         if(pt == "after"){
34989             newIndex++;
34990         }
34991         if(oldIndex < newIndex){
34992             newIndex--;
34993         }
34994         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34995             return false;
34996         }
34997         px +=  this.proxyOffsets[0];
34998         this.proxyTop.setLeftTop(px, py);
34999         this.proxyTop.show();
35000         if(!this.bottomOffset){
35001             this.bottomOffset = this.view.mainHd.getHeight();
35002         }
35003         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
35004         this.proxyBottom.show();
35005         return pt;
35006     },
35007
35008     onNodeEnter : function(n, dd, e, data){
35009         if(data.header != n){
35010             this.positionIndicator(data.header, n, e);
35011         }
35012     },
35013
35014     onNodeOver : function(n, dd, e, data){
35015         var result = false;
35016         if(data.header != n){
35017             result = this.positionIndicator(data.header, n, e);
35018         }
35019         if(!result){
35020             this.proxyTop.hide();
35021             this.proxyBottom.hide();
35022         }
35023         return result ? this.dropAllowed : this.dropNotAllowed;
35024     },
35025
35026     onNodeOut : function(n, dd, e, data){
35027         this.proxyTop.hide();
35028         this.proxyBottom.hide();
35029     },
35030
35031     onNodeDrop : function(n, dd, e, data){
35032         var h = data.header;
35033         if(h != n){
35034             var cm = this.grid.colModel;
35035             var x = Roo.lib.Event.getPageX(e);
35036             var r = Roo.lib.Dom.getRegion(n.firstChild);
35037             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
35038             var oldIndex = this.view.getCellIndex(h);
35039             var newIndex = this.view.getCellIndex(n);
35040             var locked = cm.isLocked(newIndex);
35041             if(pt == "after"){
35042                 newIndex++;
35043             }
35044             if(oldIndex < newIndex){
35045                 newIndex--;
35046             }
35047             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
35048                 return false;
35049             }
35050             cm.setLocked(oldIndex, locked, true);
35051             cm.moveColumn(oldIndex, newIndex);
35052             this.grid.fireEvent("columnmove", oldIndex, newIndex);
35053             return true;
35054         }
35055         return false;
35056     }
35057 });
35058 /*
35059  * Based on:
35060  * Ext JS Library 1.1.1
35061  * Copyright(c) 2006-2007, Ext JS, LLC.
35062  *
35063  * Originally Released Under LGPL - original licence link has changed is not relivant.
35064  *
35065  * Fork - LGPL
35066  * <script type="text/javascript">
35067  */
35068   
35069 /**
35070  * @class Roo.grid.GridView
35071  * @extends Roo.util.Observable
35072  *
35073  * @constructor
35074  * @param {Object} config
35075  */
35076 Roo.grid.GridView = function(config){
35077     Roo.grid.GridView.superclass.constructor.call(this);
35078     this.el = null;
35079
35080     Roo.apply(this, config);
35081 };
35082
35083 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
35084
35085     unselectable :  'unselectable="on"',
35086     unselectableCls :  'x-unselectable',
35087     
35088     
35089     rowClass : "x-grid-row",
35090
35091     cellClass : "x-grid-col",
35092
35093     tdClass : "x-grid-td",
35094
35095     hdClass : "x-grid-hd",
35096
35097     splitClass : "x-grid-split",
35098
35099     sortClasses : ["sort-asc", "sort-desc"],
35100
35101     enableMoveAnim : false,
35102
35103     hlColor: "C3DAF9",
35104
35105     dh : Roo.DomHelper,
35106
35107     fly : Roo.Element.fly,
35108
35109     css : Roo.util.CSS,
35110
35111     borderWidth: 1,
35112
35113     splitOffset: 3,
35114
35115     scrollIncrement : 22,
35116
35117     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35118
35119     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35120
35121     bind : function(ds, cm){
35122         if(this.ds){
35123             this.ds.un("load", this.onLoad, this);
35124             this.ds.un("datachanged", this.onDataChange, this);
35125             this.ds.un("add", this.onAdd, this);
35126             this.ds.un("remove", this.onRemove, this);
35127             this.ds.un("update", this.onUpdate, this);
35128             this.ds.un("clear", this.onClear, this);
35129         }
35130         if(ds){
35131             ds.on("load", this.onLoad, this);
35132             ds.on("datachanged", this.onDataChange, this);
35133             ds.on("add", this.onAdd, this);
35134             ds.on("remove", this.onRemove, this);
35135             ds.on("update", this.onUpdate, this);
35136             ds.on("clear", this.onClear, this);
35137         }
35138         this.ds = ds;
35139
35140         if(this.cm){
35141             this.cm.un("widthchange", this.onColWidthChange, this);
35142             this.cm.un("headerchange", this.onHeaderChange, this);
35143             this.cm.un("hiddenchange", this.onHiddenChange, this);
35144             this.cm.un("columnmoved", this.onColumnMove, this);
35145             this.cm.un("columnlockchange", this.onColumnLock, this);
35146         }
35147         if(cm){
35148             this.generateRules(cm);
35149             cm.on("widthchange", this.onColWidthChange, this);
35150             cm.on("headerchange", this.onHeaderChange, this);
35151             cm.on("hiddenchange", this.onHiddenChange, this);
35152             cm.on("columnmoved", this.onColumnMove, this);
35153             cm.on("columnlockchange", this.onColumnLock, this);
35154         }
35155         this.cm = cm;
35156     },
35157
35158     init: function(grid){
35159         Roo.grid.GridView.superclass.init.call(this, grid);
35160
35161         this.bind(grid.dataSource, grid.colModel);
35162
35163         grid.on("headerclick", this.handleHeaderClick, this);
35164
35165         if(grid.trackMouseOver){
35166             grid.on("mouseover", this.onRowOver, this);
35167             grid.on("mouseout", this.onRowOut, this);
35168         }
35169         grid.cancelTextSelection = function(){};
35170         this.gridId = grid.id;
35171
35172         var tpls = this.templates || {};
35173
35174         if(!tpls.master){
35175             tpls.master = new Roo.Template(
35176                '<div class="x-grid" hidefocus="true">',
35177                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35178                   '<div class="x-grid-topbar"></div>',
35179                   '<div class="x-grid-scroller"><div></div></div>',
35180                   '<div class="x-grid-locked">',
35181                       '<div class="x-grid-header">{lockedHeader}</div>',
35182                       '<div class="x-grid-body">{lockedBody}</div>',
35183                   "</div>",
35184                   '<div class="x-grid-viewport">',
35185                       '<div class="x-grid-header">{header}</div>',
35186                       '<div class="x-grid-body">{body}</div>',
35187                   "</div>",
35188                   '<div class="x-grid-bottombar"></div>',
35189                  
35190                   '<div class="x-grid-resize-proxy">&#160;</div>',
35191                "</div>"
35192             );
35193             tpls.master.disableformats = true;
35194         }
35195
35196         if(!tpls.header){
35197             tpls.header = new Roo.Template(
35198                '<table border="0" cellspacing="0" cellpadding="0">',
35199                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35200                "</table>{splits}"
35201             );
35202             tpls.header.disableformats = true;
35203         }
35204         tpls.header.compile();
35205
35206         if(!tpls.hcell){
35207             tpls.hcell = new Roo.Template(
35208                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35209                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35210                 "</div></td>"
35211              );
35212              tpls.hcell.disableFormats = true;
35213         }
35214         tpls.hcell.compile();
35215
35216         if(!tpls.hsplit){
35217             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
35218                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
35219             tpls.hsplit.disableFormats = true;
35220         }
35221         tpls.hsplit.compile();
35222
35223         if(!tpls.body){
35224             tpls.body = new Roo.Template(
35225                '<table border="0" cellspacing="0" cellpadding="0">',
35226                "<tbody>{rows}</tbody>",
35227                "</table>"
35228             );
35229             tpls.body.disableFormats = true;
35230         }
35231         tpls.body.compile();
35232
35233         if(!tpls.row){
35234             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35235             tpls.row.disableFormats = true;
35236         }
35237         tpls.row.compile();
35238
35239         if(!tpls.cell){
35240             tpls.cell = new Roo.Template(
35241                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35242                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
35243                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
35244                 "</td>"
35245             );
35246             tpls.cell.disableFormats = true;
35247         }
35248         tpls.cell.compile();
35249
35250         this.templates = tpls;
35251     },
35252
35253     // remap these for backwards compat
35254     onColWidthChange : function(){
35255         this.updateColumns.apply(this, arguments);
35256     },
35257     onHeaderChange : function(){
35258         this.updateHeaders.apply(this, arguments);
35259     }, 
35260     onHiddenChange : function(){
35261         this.handleHiddenChange.apply(this, arguments);
35262     },
35263     onColumnMove : function(){
35264         this.handleColumnMove.apply(this, arguments);
35265     },
35266     onColumnLock : function(){
35267         this.handleLockChange.apply(this, arguments);
35268     },
35269
35270     onDataChange : function(){
35271         this.refresh();
35272         this.updateHeaderSortState();
35273     },
35274
35275     onClear : function(){
35276         this.refresh();
35277     },
35278
35279     onUpdate : function(ds, record){
35280         this.refreshRow(record);
35281     },
35282
35283     refreshRow : function(record){
35284         var ds = this.ds, index;
35285         if(typeof record == 'number'){
35286             index = record;
35287             record = ds.getAt(index);
35288         }else{
35289             index = ds.indexOf(record);
35290         }
35291         this.insertRows(ds, index, index, true);
35292         this.onRemove(ds, record, index+1, true);
35293         this.syncRowHeights(index, index);
35294         this.layout();
35295         this.fireEvent("rowupdated", this, index, record);
35296     },
35297
35298     onAdd : function(ds, records, index){
35299         this.insertRows(ds, index, index + (records.length-1));
35300     },
35301
35302     onRemove : function(ds, record, index, isUpdate){
35303         if(isUpdate !== true){
35304             this.fireEvent("beforerowremoved", this, index, record);
35305         }
35306         var bt = this.getBodyTable(), lt = this.getLockedTable();
35307         if(bt.rows[index]){
35308             bt.firstChild.removeChild(bt.rows[index]);
35309         }
35310         if(lt.rows[index]){
35311             lt.firstChild.removeChild(lt.rows[index]);
35312         }
35313         if(isUpdate !== true){
35314             this.stripeRows(index);
35315             this.syncRowHeights(index, index);
35316             this.layout();
35317             this.fireEvent("rowremoved", this, index, record);
35318         }
35319     },
35320
35321     onLoad : function(){
35322         this.scrollToTop();
35323     },
35324
35325     /**
35326      * Scrolls the grid to the top
35327      */
35328     scrollToTop : function(){
35329         if(this.scroller){
35330             this.scroller.dom.scrollTop = 0;
35331             this.syncScroll();
35332         }
35333     },
35334
35335     /**
35336      * Gets a panel in the header of the grid that can be used for toolbars etc.
35337      * After modifying the contents of this panel a call to grid.autoSize() may be
35338      * required to register any changes in size.
35339      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35340      * @return Roo.Element
35341      */
35342     getHeaderPanel : function(doShow){
35343         if(doShow){
35344             this.headerPanel.show();
35345         }
35346         return this.headerPanel;
35347     },
35348
35349     /**
35350      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35351      * After modifying the contents of this panel a call to grid.autoSize() may be
35352      * required to register any changes in size.
35353      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35354      * @return Roo.Element
35355      */
35356     getFooterPanel : function(doShow){
35357         if(doShow){
35358             this.footerPanel.show();
35359         }
35360         return this.footerPanel;
35361     },
35362
35363     initElements : function(){
35364         var E = Roo.Element;
35365         var el = this.grid.getGridEl().dom.firstChild;
35366         var cs = el.childNodes;
35367
35368         this.el = new E(el);
35369         
35370          this.focusEl = new E(el.firstChild);
35371         this.focusEl.swallowEvent("click", true);
35372         
35373         this.headerPanel = new E(cs[1]);
35374         this.headerPanel.enableDisplayMode("block");
35375
35376         this.scroller = new E(cs[2]);
35377         this.scrollSizer = new E(this.scroller.dom.firstChild);
35378
35379         this.lockedWrap = new E(cs[3]);
35380         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35381         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35382
35383         this.mainWrap = new E(cs[4]);
35384         this.mainHd = new E(this.mainWrap.dom.firstChild);
35385         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35386
35387         this.footerPanel = new E(cs[5]);
35388         this.footerPanel.enableDisplayMode("block");
35389
35390         this.resizeProxy = new E(cs[6]);
35391
35392         this.headerSelector = String.format(
35393            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35394            this.lockedHd.id, this.mainHd.id
35395         );
35396
35397         this.splitterSelector = String.format(
35398            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35399            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35400         );
35401     },
35402     idToCssName : function(s)
35403     {
35404         return s.replace(/[^a-z0-9]+/ig, '-');
35405     },
35406
35407     getHeaderCell : function(index){
35408         return Roo.DomQuery.select(this.headerSelector)[index];
35409     },
35410
35411     getHeaderCellMeasure : function(index){
35412         return this.getHeaderCell(index).firstChild;
35413     },
35414
35415     getHeaderCellText : function(index){
35416         return this.getHeaderCell(index).firstChild.firstChild;
35417     },
35418
35419     getLockedTable : function(){
35420         return this.lockedBody.dom.firstChild;
35421     },
35422
35423     getBodyTable : function(){
35424         return this.mainBody.dom.firstChild;
35425     },
35426
35427     getLockedRow : function(index){
35428         return this.getLockedTable().rows[index];
35429     },
35430
35431     getRow : function(index){
35432         return this.getBodyTable().rows[index];
35433     },
35434
35435     getRowComposite : function(index){
35436         if(!this.rowEl){
35437             this.rowEl = new Roo.CompositeElementLite();
35438         }
35439         var els = [], lrow, mrow;
35440         if(lrow = this.getLockedRow(index)){
35441             els.push(lrow);
35442         }
35443         if(mrow = this.getRow(index)){
35444             els.push(mrow);
35445         }
35446         this.rowEl.elements = els;
35447         return this.rowEl;
35448     },
35449     /**
35450      * Gets the 'td' of the cell
35451      * 
35452      * @param {Integer} rowIndex row to select
35453      * @param {Integer} colIndex column to select
35454      * 
35455      * @return {Object} 
35456      */
35457     getCell : function(rowIndex, colIndex){
35458         var locked = this.cm.getLockedCount();
35459         var source;
35460         if(colIndex < locked){
35461             source = this.lockedBody.dom.firstChild;
35462         }else{
35463             source = this.mainBody.dom.firstChild;
35464             colIndex -= locked;
35465         }
35466         return source.rows[rowIndex].childNodes[colIndex];
35467     },
35468
35469     getCellText : function(rowIndex, colIndex){
35470         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35471     },
35472
35473     getCellBox : function(cell){
35474         var b = this.fly(cell).getBox();
35475         if(Roo.isOpera){ // opera fails to report the Y
35476             b.y = cell.offsetTop + this.mainBody.getY();
35477         }
35478         return b;
35479     },
35480
35481     getCellIndex : function(cell){
35482         var id = String(cell.className).match(this.cellRE);
35483         if(id){
35484             return parseInt(id[1], 10);
35485         }
35486         return 0;
35487     },
35488
35489     findHeaderIndex : function(n){
35490         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35491         return r ? this.getCellIndex(r) : false;
35492     },
35493
35494     findHeaderCell : function(n){
35495         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35496         return r ? r : false;
35497     },
35498
35499     findRowIndex : function(n){
35500         if(!n){
35501             return false;
35502         }
35503         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35504         return r ? r.rowIndex : false;
35505     },
35506
35507     findCellIndex : function(node){
35508         var stop = this.el.dom;
35509         while(node && node != stop){
35510             if(this.findRE.test(node.className)){
35511                 return this.getCellIndex(node);
35512             }
35513             node = node.parentNode;
35514         }
35515         return false;
35516     },
35517
35518     getColumnId : function(index){
35519         return this.cm.getColumnId(index);
35520     },
35521
35522     getSplitters : function()
35523     {
35524         if(this.splitterSelector){
35525            return Roo.DomQuery.select(this.splitterSelector);
35526         }else{
35527             return null;
35528       }
35529     },
35530
35531     getSplitter : function(index){
35532         return this.getSplitters()[index];
35533     },
35534
35535     onRowOver : function(e, t){
35536         var row;
35537         if((row = this.findRowIndex(t)) !== false){
35538             this.getRowComposite(row).addClass("x-grid-row-over");
35539         }
35540     },
35541
35542     onRowOut : function(e, t){
35543         var row;
35544         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35545             this.getRowComposite(row).removeClass("x-grid-row-over");
35546         }
35547     },
35548
35549     renderHeaders : function(){
35550         var cm = this.cm;
35551         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35552         var cb = [], lb = [], sb = [], lsb = [], p = {};
35553         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35554             p.cellId = "x-grid-hd-0-" + i;
35555             p.splitId = "x-grid-csplit-0-" + i;
35556             p.id = cm.getColumnId(i);
35557             p.title = cm.getColumnTooltip(i) || "";
35558             p.value = cm.getColumnHeader(i) || "";
35559             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35560             if(!cm.isLocked(i)){
35561                 cb[cb.length] = ct.apply(p);
35562                 sb[sb.length] = st.apply(p);
35563             }else{
35564                 lb[lb.length] = ct.apply(p);
35565                 lsb[lsb.length] = st.apply(p);
35566             }
35567         }
35568         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35569                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35570     },
35571
35572     updateHeaders : function(){
35573         var html = this.renderHeaders();
35574         this.lockedHd.update(html[0]);
35575         this.mainHd.update(html[1]);
35576     },
35577
35578     /**
35579      * Focuses the specified row.
35580      * @param {Number} row The row index
35581      */
35582     focusRow : function(row)
35583     {
35584         //Roo.log('GridView.focusRow');
35585         var x = this.scroller.dom.scrollLeft;
35586         this.focusCell(row, 0, false);
35587         this.scroller.dom.scrollLeft = x;
35588     },
35589
35590     /**
35591      * Focuses the specified cell.
35592      * @param {Number} row The row index
35593      * @param {Number} col The column index
35594      * @param {Boolean} hscroll false to disable horizontal scrolling
35595      */
35596     focusCell : function(row, col, hscroll)
35597     {
35598         //Roo.log('GridView.focusCell');
35599         var el = this.ensureVisible(row, col, hscroll);
35600         this.focusEl.alignTo(el, "tl-tl");
35601         if(Roo.isGecko){
35602             this.focusEl.focus();
35603         }else{
35604             this.focusEl.focus.defer(1, this.focusEl);
35605         }
35606     },
35607
35608     /**
35609      * Scrolls the specified cell into view
35610      * @param {Number} row The row index
35611      * @param {Number} col The column index
35612      * @param {Boolean} hscroll false to disable horizontal scrolling
35613      */
35614     ensureVisible : function(row, col, hscroll)
35615     {
35616         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35617         //return null; //disable for testing.
35618         if(typeof row != "number"){
35619             row = row.rowIndex;
35620         }
35621         if(row < 0 && row >= this.ds.getCount()){
35622             return  null;
35623         }
35624         col = (col !== undefined ? col : 0);
35625         var cm = this.grid.colModel;
35626         while(cm.isHidden(col)){
35627             col++;
35628         }
35629
35630         var el = this.getCell(row, col);
35631         if(!el){
35632             return null;
35633         }
35634         var c = this.scroller.dom;
35635
35636         var ctop = parseInt(el.offsetTop, 10);
35637         var cleft = parseInt(el.offsetLeft, 10);
35638         var cbot = ctop + el.offsetHeight;
35639         var cright = cleft + el.offsetWidth;
35640         
35641         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35642         var stop = parseInt(c.scrollTop, 10);
35643         var sleft = parseInt(c.scrollLeft, 10);
35644         var sbot = stop + ch;
35645         var sright = sleft + c.clientWidth;
35646         /*
35647         Roo.log('GridView.ensureVisible:' +
35648                 ' ctop:' + ctop +
35649                 ' c.clientHeight:' + c.clientHeight +
35650                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35651                 ' stop:' + stop +
35652                 ' cbot:' + cbot +
35653                 ' sbot:' + sbot +
35654                 ' ch:' + ch  
35655                 );
35656         */
35657         if(ctop < stop){
35658              c.scrollTop = ctop;
35659             //Roo.log("set scrolltop to ctop DISABLE?");
35660         }else if(cbot > sbot){
35661             //Roo.log("set scrolltop to cbot-ch");
35662             c.scrollTop = cbot-ch;
35663         }
35664         
35665         if(hscroll !== false){
35666             if(cleft < sleft){
35667                 c.scrollLeft = cleft;
35668             }else if(cright > sright){
35669                 c.scrollLeft = cright-c.clientWidth;
35670             }
35671         }
35672          
35673         return el;
35674     },
35675
35676     updateColumns : function(){
35677         this.grid.stopEditing();
35678         var cm = this.grid.colModel, colIds = this.getColumnIds();
35679         //var totalWidth = cm.getTotalWidth();
35680         var pos = 0;
35681         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35682             //if(cm.isHidden(i)) continue;
35683             var w = cm.getColumnWidth(i);
35684             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35685             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35686         }
35687         this.updateSplitters();
35688     },
35689
35690     generateRules : function(cm){
35691         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35692         Roo.util.CSS.removeStyleSheet(rulesId);
35693         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35694             var cid = cm.getColumnId(i);
35695             var align = '';
35696             if(cm.config[i].align){
35697                 align = 'text-align:'+cm.config[i].align+';';
35698             }
35699             var hidden = '';
35700             if(cm.isHidden(i)){
35701                 hidden = 'display:none;';
35702             }
35703             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35704             ruleBuf.push(
35705                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35706                     this.hdSelector, cid, " {\n", align, width, "}\n",
35707                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35708                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35709         }
35710         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35711     },
35712
35713     updateSplitters : function(){
35714         var cm = this.cm, s = this.getSplitters();
35715         if(s){ // splitters not created yet
35716             var pos = 0, locked = true;
35717             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35718                 if(cm.isHidden(i)) continue;
35719                 var w = cm.getColumnWidth(i); // make sure it's a number
35720                 if(!cm.isLocked(i) && locked){
35721                     pos = 0;
35722                     locked = false;
35723                 }
35724                 pos += w;
35725                 s[i].style.left = (pos-this.splitOffset) + "px";
35726             }
35727         }
35728     },
35729
35730     handleHiddenChange : function(colModel, colIndex, hidden){
35731         if(hidden){
35732             this.hideColumn(colIndex);
35733         }else{
35734             this.unhideColumn(colIndex);
35735         }
35736     },
35737
35738     hideColumn : function(colIndex){
35739         var cid = this.getColumnId(colIndex);
35740         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35741         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35742         if(Roo.isSafari){
35743             this.updateHeaders();
35744         }
35745         this.updateSplitters();
35746         this.layout();
35747     },
35748
35749     unhideColumn : function(colIndex){
35750         var cid = this.getColumnId(colIndex);
35751         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35752         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35753
35754         if(Roo.isSafari){
35755             this.updateHeaders();
35756         }
35757         this.updateSplitters();
35758         this.layout();
35759     },
35760
35761     insertRows : function(dm, firstRow, lastRow, isUpdate){
35762         if(firstRow == 0 && lastRow == dm.getCount()-1){
35763             this.refresh();
35764         }else{
35765             if(!isUpdate){
35766                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35767             }
35768             var s = this.getScrollState();
35769             var markup = this.renderRows(firstRow, lastRow);
35770             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35771             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35772             this.restoreScroll(s);
35773             if(!isUpdate){
35774                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35775                 this.syncRowHeights(firstRow, lastRow);
35776                 this.stripeRows(firstRow);
35777                 this.layout();
35778             }
35779         }
35780     },
35781
35782     bufferRows : function(markup, target, index){
35783         var before = null, trows = target.rows, tbody = target.tBodies[0];
35784         if(index < trows.length){
35785             before = trows[index];
35786         }
35787         var b = document.createElement("div");
35788         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35789         var rows = b.firstChild.rows;
35790         for(var i = 0, len = rows.length; i < len; i++){
35791             if(before){
35792                 tbody.insertBefore(rows[0], before);
35793             }else{
35794                 tbody.appendChild(rows[0]);
35795             }
35796         }
35797         b.innerHTML = "";
35798         b = null;
35799     },
35800
35801     deleteRows : function(dm, firstRow, lastRow){
35802         if(dm.getRowCount()<1){
35803             this.fireEvent("beforerefresh", this);
35804             this.mainBody.update("");
35805             this.lockedBody.update("");
35806             this.fireEvent("refresh", this);
35807         }else{
35808             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35809             var bt = this.getBodyTable();
35810             var tbody = bt.firstChild;
35811             var rows = bt.rows;
35812             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35813                 tbody.removeChild(rows[firstRow]);
35814             }
35815             this.stripeRows(firstRow);
35816             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35817         }
35818     },
35819
35820     updateRows : function(dataSource, firstRow, lastRow){
35821         var s = this.getScrollState();
35822         this.refresh();
35823         this.restoreScroll(s);
35824     },
35825
35826     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35827         if(!noRefresh){
35828            this.refresh();
35829         }
35830         this.updateHeaderSortState();
35831     },
35832
35833     getScrollState : function(){
35834         
35835         var sb = this.scroller.dom;
35836         return {left: sb.scrollLeft, top: sb.scrollTop};
35837     },
35838
35839     stripeRows : function(startRow){
35840         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35841             return;
35842         }
35843         startRow = startRow || 0;
35844         var rows = this.getBodyTable().rows;
35845         var lrows = this.getLockedTable().rows;
35846         var cls = ' x-grid-row-alt ';
35847         for(var i = startRow, len = rows.length; i < len; i++){
35848             var row = rows[i], lrow = lrows[i];
35849             var isAlt = ((i+1) % 2 == 0);
35850             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35851             if(isAlt == hasAlt){
35852                 continue;
35853             }
35854             if(isAlt){
35855                 row.className += " x-grid-row-alt";
35856             }else{
35857                 row.className = row.className.replace("x-grid-row-alt", "");
35858             }
35859             if(lrow){
35860                 lrow.className = row.className;
35861             }
35862         }
35863     },
35864
35865     restoreScroll : function(state){
35866         //Roo.log('GridView.restoreScroll');
35867         var sb = this.scroller.dom;
35868         sb.scrollLeft = state.left;
35869         sb.scrollTop = state.top;
35870         this.syncScroll();
35871     },
35872
35873     syncScroll : function(){
35874         //Roo.log('GridView.syncScroll');
35875         var sb = this.scroller.dom;
35876         var sh = this.mainHd.dom;
35877         var bs = this.mainBody.dom;
35878         var lv = this.lockedBody.dom;
35879         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35880         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35881     },
35882
35883     handleScroll : function(e){
35884         this.syncScroll();
35885         var sb = this.scroller.dom;
35886         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35887         e.stopEvent();
35888     },
35889
35890     handleWheel : function(e){
35891         var d = e.getWheelDelta();
35892         this.scroller.dom.scrollTop -= d*22;
35893         // set this here to prevent jumpy scrolling on large tables
35894         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35895         e.stopEvent();
35896     },
35897
35898     renderRows : function(startRow, endRow){
35899         // pull in all the crap needed to render rows
35900         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35901         var colCount = cm.getColumnCount();
35902
35903         if(ds.getCount() < 1){
35904             return ["", ""];
35905         }
35906
35907         // build a map for all the columns
35908         var cs = [];
35909         for(var i = 0; i < colCount; i++){
35910             var name = cm.getDataIndex(i);
35911             cs[i] = {
35912                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35913                 renderer : cm.getRenderer(i),
35914                 id : cm.getColumnId(i),
35915                 locked : cm.isLocked(i)
35916             };
35917         }
35918
35919         startRow = startRow || 0;
35920         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35921
35922         // records to render
35923         var rs = ds.getRange(startRow, endRow);
35924
35925         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35926     },
35927
35928     // As much as I hate to duplicate code, this was branched because FireFox really hates
35929     // [].join("") on strings. The performance difference was substantial enough to
35930     // branch this function
35931     doRender : Roo.isGecko ?
35932             function(cs, rs, ds, startRow, colCount, stripe){
35933                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35934                 // buffers
35935                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35936                 
35937                 var hasListener = this.grid.hasListener('rowclass');
35938                 var rowcfg = {};
35939                 for(var j = 0, len = rs.length; j < len; j++){
35940                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35941                     for(var i = 0; i < colCount; i++){
35942                         c = cs[i];
35943                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35944                         p.id = c.id;
35945                         p.css = p.attr = "";
35946                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35947                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35948                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35949                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35950                         }
35951                         var markup = ct.apply(p);
35952                         if(!c.locked){
35953                             cb+= markup;
35954                         }else{
35955                             lcb+= markup;
35956                         }
35957                     }
35958                     var alt = [];
35959                     if(stripe && ((rowIndex+1) % 2 == 0)){
35960                         alt.push("x-grid-row-alt")
35961                     }
35962                     if(r.dirty){
35963                         alt.push(  " x-grid-dirty-row");
35964                     }
35965                     rp.cells = lcb;
35966                     if(this.getRowClass){
35967                         alt.push(this.getRowClass(r, rowIndex));
35968                     }
35969                     if (hasListener) {
35970                         rowcfg = {
35971                              
35972                             record: r,
35973                             rowIndex : rowIndex,
35974                             rowClass : ''
35975                         }
35976                         this.grid.fireEvent('rowclass', this, rowcfg);
35977                         alt.push(rowcfg.rowClass);
35978                     }
35979                     rp.alt = alt.join(" ");
35980                     lbuf+= rt.apply(rp);
35981                     rp.cells = cb;
35982                     buf+=  rt.apply(rp);
35983                 }
35984                 return [lbuf, buf];
35985             } :
35986             function(cs, rs, ds, startRow, colCount, stripe){
35987                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35988                 // buffers
35989                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35990                 var hasListener = this.grid.hasListener('rowclass');
35991  
35992                 var rowcfg = {};
35993                 for(var j = 0, len = rs.length; j < len; j++){
35994                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35995                     for(var i = 0; i < colCount; i++){
35996                         c = cs[i];
35997                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35998                         p.id = c.id;
35999                         p.css = p.attr = "";
36000                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36001                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36002                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36003                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36004                         }
36005                         
36006                         var markup = ct.apply(p);
36007                         if(!c.locked){
36008                             cb[cb.length] = markup;
36009                         }else{
36010                             lcb[lcb.length] = markup;
36011                         }
36012                     }
36013                     var alt = [];
36014                     if(stripe && ((rowIndex+1) % 2 == 0)){
36015                         alt.push( "x-grid-row-alt");
36016                     }
36017                     if(r.dirty){
36018                         alt.push(" x-grid-dirty-row");
36019                     }
36020                     rp.cells = lcb;
36021                     if(this.getRowClass){
36022                         alt.push( this.getRowClass(r, rowIndex));
36023                     }
36024                     if (hasListener) {
36025                         rowcfg = {
36026                              
36027                             record: r,
36028                             rowIndex : rowIndex,
36029                             rowClass : ''
36030                         }
36031                         this.grid.fireEvent('rowclass', this, rowcfg);
36032                         alt.push(rowcfg.rowClass);
36033                     }
36034                     rp.alt = alt.join(" ");
36035                     rp.cells = lcb.join("");
36036                     lbuf[lbuf.length] = rt.apply(rp);
36037                     rp.cells = cb.join("");
36038                     buf[buf.length] =  rt.apply(rp);
36039                 }
36040                 return [lbuf.join(""), buf.join("")];
36041             },
36042
36043     renderBody : function(){
36044         var markup = this.renderRows();
36045         var bt = this.templates.body;
36046         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
36047     },
36048
36049     /**
36050      * Refreshes the grid
36051      * @param {Boolean} headersToo
36052      */
36053     refresh : function(headersToo){
36054         this.fireEvent("beforerefresh", this);
36055         this.grid.stopEditing();
36056         var result = this.renderBody();
36057         this.lockedBody.update(result[0]);
36058         this.mainBody.update(result[1]);
36059         if(headersToo === true){
36060             this.updateHeaders();
36061             this.updateColumns();
36062             this.updateSplitters();
36063             this.updateHeaderSortState();
36064         }
36065         this.syncRowHeights();
36066         this.layout();
36067         this.fireEvent("refresh", this);
36068     },
36069
36070     handleColumnMove : function(cm, oldIndex, newIndex){
36071         this.indexMap = null;
36072         var s = this.getScrollState();
36073         this.refresh(true);
36074         this.restoreScroll(s);
36075         this.afterMove(newIndex);
36076     },
36077
36078     afterMove : function(colIndex){
36079         if(this.enableMoveAnim && Roo.enableFx){
36080             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
36081         }
36082         // if multisort - fix sortOrder, and reload..
36083         if (this.grid.dataSource.multiSort) {
36084             // the we can call sort again..
36085             var dm = this.grid.dataSource;
36086             var cm = this.grid.colModel;
36087             var so = [];
36088             for(var i = 0; i < cm.config.length; i++ ) {
36089                 
36090                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
36091                     continue; // dont' bother, it's not in sort list or being set.
36092                 }
36093                 
36094                 so.push(cm.config[i].dataIndex);
36095             };
36096             dm.sortOrder = so;
36097             dm.load(dm.lastOptions);
36098             
36099             
36100         }
36101         
36102     },
36103
36104     updateCell : function(dm, rowIndex, dataIndex){
36105         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36106         if(typeof colIndex == "undefined"){ // not present in grid
36107             return;
36108         }
36109         var cm = this.grid.colModel;
36110         var cell = this.getCell(rowIndex, colIndex);
36111         var cellText = this.getCellText(rowIndex, colIndex);
36112
36113         var p = {
36114             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36115             id : cm.getColumnId(colIndex),
36116             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36117         };
36118         var renderer = cm.getRenderer(colIndex);
36119         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36120         if(typeof val == "undefined" || val === "") val = "&#160;";
36121         cellText.innerHTML = val;
36122         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36123         this.syncRowHeights(rowIndex, rowIndex);
36124     },
36125
36126     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36127         var maxWidth = 0;
36128         if(this.grid.autoSizeHeaders){
36129             var h = this.getHeaderCellMeasure(colIndex);
36130             maxWidth = Math.max(maxWidth, h.scrollWidth);
36131         }
36132         var tb, index;
36133         if(this.cm.isLocked(colIndex)){
36134             tb = this.getLockedTable();
36135             index = colIndex;
36136         }else{
36137             tb = this.getBodyTable();
36138             index = colIndex - this.cm.getLockedCount();
36139         }
36140         if(tb && tb.rows){
36141             var rows = tb.rows;
36142             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36143             for(var i = 0; i < stopIndex; i++){
36144                 var cell = rows[i].childNodes[index].firstChild;
36145                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36146             }
36147         }
36148         return maxWidth + /*margin for error in IE*/ 5;
36149     },
36150     /**
36151      * Autofit a column to its content.
36152      * @param {Number} colIndex
36153      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36154      */
36155      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36156          if(this.cm.isHidden(colIndex)){
36157              return; // can't calc a hidden column
36158          }
36159         if(forceMinSize){
36160             var cid = this.cm.getColumnId(colIndex);
36161             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36162            if(this.grid.autoSizeHeaders){
36163                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36164            }
36165         }
36166         var newWidth = this.calcColumnWidth(colIndex);
36167         this.cm.setColumnWidth(colIndex,
36168             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36169         if(!suppressEvent){
36170             this.grid.fireEvent("columnresize", colIndex, newWidth);
36171         }
36172     },
36173
36174     /**
36175      * Autofits all columns to their content and then expands to fit any extra space in the grid
36176      */
36177      autoSizeColumns : function(){
36178         var cm = this.grid.colModel;
36179         var colCount = cm.getColumnCount();
36180         for(var i = 0; i < colCount; i++){
36181             this.autoSizeColumn(i, true, true);
36182         }
36183         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36184             this.fitColumns();
36185         }else{
36186             this.updateColumns();
36187             this.layout();
36188         }
36189     },
36190
36191     /**
36192      * Autofits all columns to the grid's width proportionate with their current size
36193      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36194      */
36195     fitColumns : function(reserveScrollSpace){
36196         var cm = this.grid.colModel;
36197         var colCount = cm.getColumnCount();
36198         var cols = [];
36199         var width = 0;
36200         var i, w;
36201         for (i = 0; i < colCount; i++){
36202             if(!cm.isHidden(i) && !cm.isFixed(i)){
36203                 w = cm.getColumnWidth(i);
36204                 cols.push(i);
36205                 cols.push(w);
36206                 width += w;
36207             }
36208         }
36209         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36210         if(reserveScrollSpace){
36211             avail -= 17;
36212         }
36213         var frac = (avail - cm.getTotalWidth())/width;
36214         while (cols.length){
36215             w = cols.pop();
36216             i = cols.pop();
36217             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36218         }
36219         this.updateColumns();
36220         this.layout();
36221     },
36222
36223     onRowSelect : function(rowIndex){
36224         var row = this.getRowComposite(rowIndex);
36225         row.addClass("x-grid-row-selected");
36226     },
36227
36228     onRowDeselect : function(rowIndex){
36229         var row = this.getRowComposite(rowIndex);
36230         row.removeClass("x-grid-row-selected");
36231     },
36232
36233     onCellSelect : function(row, col){
36234         var cell = this.getCell(row, col);
36235         if(cell){
36236             Roo.fly(cell).addClass("x-grid-cell-selected");
36237         }
36238     },
36239
36240     onCellDeselect : function(row, col){
36241         var cell = this.getCell(row, col);
36242         if(cell){
36243             Roo.fly(cell).removeClass("x-grid-cell-selected");
36244         }
36245     },
36246
36247     updateHeaderSortState : function(){
36248         
36249         // sort state can be single { field: xxx, direction : yyy}
36250         // or   { xxx=>ASC , yyy : DESC ..... }
36251         
36252         var mstate = {};
36253         if (!this.ds.multiSort) { 
36254             var state = this.ds.getSortState();
36255             if(!state){
36256                 return;
36257             }
36258             mstate[state.field] = state.direction;
36259             // FIXME... - this is not used here.. but might be elsewhere..
36260             this.sortState = state;
36261             
36262         } else {
36263             mstate = this.ds.sortToggle;
36264         }
36265         //remove existing sort classes..
36266         
36267         var sc = this.sortClasses;
36268         var hds = this.el.select(this.headerSelector).removeClass(sc);
36269         
36270         for(var f in mstate) {
36271         
36272             var sortColumn = this.cm.findColumnIndex(f);
36273             
36274             if(sortColumn != -1){
36275                 var sortDir = mstate[f];        
36276                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36277             }
36278         }
36279         
36280          
36281         
36282     },
36283
36284
36285     handleHeaderClick : function(g, index){
36286         if(this.headersDisabled){
36287             return;
36288         }
36289         var dm = g.dataSource, cm = g.colModel;
36290         if(!cm.isSortable(index)){
36291             return;
36292         }
36293         g.stopEditing();
36294         
36295         if (dm.multiSort) {
36296             // update the sortOrder
36297             var so = [];
36298             for(var i = 0; i < cm.config.length; i++ ) {
36299                 
36300                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36301                     continue; // dont' bother, it's not in sort list or being set.
36302                 }
36303                 
36304                 so.push(cm.config[i].dataIndex);
36305             };
36306             dm.sortOrder = so;
36307         }
36308         
36309         
36310         dm.sort(cm.getDataIndex(index));
36311     },
36312
36313
36314     destroy : function(){
36315         if(this.colMenu){
36316             this.colMenu.removeAll();
36317             Roo.menu.MenuMgr.unregister(this.colMenu);
36318             this.colMenu.getEl().remove();
36319             delete this.colMenu;
36320         }
36321         if(this.hmenu){
36322             this.hmenu.removeAll();
36323             Roo.menu.MenuMgr.unregister(this.hmenu);
36324             this.hmenu.getEl().remove();
36325             delete this.hmenu;
36326         }
36327         if(this.grid.enableColumnMove){
36328             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36329             if(dds){
36330                 for(var dd in dds){
36331                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36332                         var elid = dds[dd].dragElId;
36333                         dds[dd].unreg();
36334                         Roo.get(elid).remove();
36335                     } else if(dds[dd].config.isTarget){
36336                         dds[dd].proxyTop.remove();
36337                         dds[dd].proxyBottom.remove();
36338                         dds[dd].unreg();
36339                     }
36340                     if(Roo.dd.DDM.locationCache[dd]){
36341                         delete Roo.dd.DDM.locationCache[dd];
36342                     }
36343                 }
36344                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36345             }
36346         }
36347         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36348         this.bind(null, null);
36349         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36350     },
36351
36352     handleLockChange : function(){
36353         this.refresh(true);
36354     },
36355
36356     onDenyColumnLock : function(){
36357
36358     },
36359
36360     onDenyColumnHide : function(){
36361
36362     },
36363
36364     handleHdMenuClick : function(item){
36365         var index = this.hdCtxIndex;
36366         var cm = this.cm, ds = this.ds;
36367         switch(item.id){
36368             case "asc":
36369                 ds.sort(cm.getDataIndex(index), "ASC");
36370                 break;
36371             case "desc":
36372                 ds.sort(cm.getDataIndex(index), "DESC");
36373                 break;
36374             case "lock":
36375                 var lc = cm.getLockedCount();
36376                 if(cm.getColumnCount(true) <= lc+1){
36377                     this.onDenyColumnLock();
36378                     return;
36379                 }
36380                 if(lc != index){
36381                     cm.setLocked(index, true, true);
36382                     cm.moveColumn(index, lc);
36383                     this.grid.fireEvent("columnmove", index, lc);
36384                 }else{
36385                     cm.setLocked(index, true);
36386                 }
36387             break;
36388             case "unlock":
36389                 var lc = cm.getLockedCount();
36390                 if((lc-1) != index){
36391                     cm.setLocked(index, false, true);
36392                     cm.moveColumn(index, lc-1);
36393                     this.grid.fireEvent("columnmove", index, lc-1);
36394                 }else{
36395                     cm.setLocked(index, false);
36396                 }
36397             break;
36398             default:
36399                 index = cm.getIndexById(item.id.substr(4));
36400                 if(index != -1){
36401                     if(item.checked && cm.getColumnCount(true) <= 1){
36402                         this.onDenyColumnHide();
36403                         return false;
36404                     }
36405                     cm.setHidden(index, item.checked);
36406                 }
36407         }
36408         return true;
36409     },
36410
36411     beforeColMenuShow : function(){
36412         var cm = this.cm,  colCount = cm.getColumnCount();
36413         this.colMenu.removeAll();
36414         for(var i = 0; i < colCount; i++){
36415             this.colMenu.add(new Roo.menu.CheckItem({
36416                 id: "col-"+cm.getColumnId(i),
36417                 text: cm.getColumnHeader(i),
36418                 checked: !cm.isHidden(i),
36419                 hideOnClick:false
36420             }));
36421         }
36422     },
36423
36424     handleHdCtx : function(g, index, e){
36425         e.stopEvent();
36426         var hd = this.getHeaderCell(index);
36427         this.hdCtxIndex = index;
36428         var ms = this.hmenu.items, cm = this.cm;
36429         ms.get("asc").setDisabled(!cm.isSortable(index));
36430         ms.get("desc").setDisabled(!cm.isSortable(index));
36431         if(this.grid.enableColLock !== false){
36432             ms.get("lock").setDisabled(cm.isLocked(index));
36433             ms.get("unlock").setDisabled(!cm.isLocked(index));
36434         }
36435         this.hmenu.show(hd, "tl-bl");
36436     },
36437
36438     handleHdOver : function(e){
36439         var hd = this.findHeaderCell(e.getTarget());
36440         if(hd && !this.headersDisabled){
36441             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36442                this.fly(hd).addClass("x-grid-hd-over");
36443             }
36444         }
36445     },
36446
36447     handleHdOut : function(e){
36448         var hd = this.findHeaderCell(e.getTarget());
36449         if(hd){
36450             this.fly(hd).removeClass("x-grid-hd-over");
36451         }
36452     },
36453
36454     handleSplitDblClick : function(e, t){
36455         var i = this.getCellIndex(t);
36456         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36457             this.autoSizeColumn(i, true);
36458             this.layout();
36459         }
36460     },
36461
36462     render : function(){
36463
36464         var cm = this.cm;
36465         var colCount = cm.getColumnCount();
36466
36467         if(this.grid.monitorWindowResize === true){
36468             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36469         }
36470         var header = this.renderHeaders();
36471         var body = this.templates.body.apply({rows:""});
36472         var html = this.templates.master.apply({
36473             lockedBody: body,
36474             body: body,
36475             lockedHeader: header[0],
36476             header: header[1]
36477         });
36478
36479         //this.updateColumns();
36480
36481         this.grid.getGridEl().dom.innerHTML = html;
36482
36483         this.initElements();
36484         
36485         // a kludge to fix the random scolling effect in webkit
36486         this.el.on("scroll", function() {
36487             this.el.dom.scrollTop=0; // hopefully not recursive..
36488         },this);
36489
36490         this.scroller.on("scroll", this.handleScroll, this);
36491         this.lockedBody.on("mousewheel", this.handleWheel, this);
36492         this.mainBody.on("mousewheel", this.handleWheel, this);
36493
36494         this.mainHd.on("mouseover", this.handleHdOver, this);
36495         this.mainHd.on("mouseout", this.handleHdOut, this);
36496         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36497                 {delegate: "."+this.splitClass});
36498
36499         this.lockedHd.on("mouseover", this.handleHdOver, this);
36500         this.lockedHd.on("mouseout", this.handleHdOut, this);
36501         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36502                 {delegate: "."+this.splitClass});
36503
36504         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36505             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36506         }
36507
36508         this.updateSplitters();
36509
36510         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36511             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36512             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36513         }
36514
36515         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36516             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36517             this.hmenu.add(
36518                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36519                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36520             );
36521             if(this.grid.enableColLock !== false){
36522                 this.hmenu.add('-',
36523                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36524                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36525                 );
36526             }
36527             if(this.grid.enableColumnHide !== false){
36528
36529                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36530                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36531                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36532
36533                 this.hmenu.add('-',
36534                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36535                 );
36536             }
36537             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36538
36539             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36540         }
36541
36542         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36543             this.dd = new Roo.grid.GridDragZone(this.grid, {
36544                 ddGroup : this.grid.ddGroup || 'GridDD'
36545             });
36546             
36547         }
36548
36549         /*
36550         for(var i = 0; i < colCount; i++){
36551             if(cm.isHidden(i)){
36552                 this.hideColumn(i);
36553             }
36554             if(cm.config[i].align){
36555                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36556                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36557             }
36558         }*/
36559         
36560         this.updateHeaderSortState();
36561
36562         this.beforeInitialResize();
36563         this.layout(true);
36564
36565         // two part rendering gives faster view to the user
36566         this.renderPhase2.defer(1, this);
36567     },
36568
36569     renderPhase2 : function(){
36570         // render the rows now
36571         this.refresh();
36572         if(this.grid.autoSizeColumns){
36573             this.autoSizeColumns();
36574         }
36575     },
36576
36577     beforeInitialResize : function(){
36578
36579     },
36580
36581     onColumnSplitterMoved : function(i, w){
36582         this.userResized = true;
36583         var cm = this.grid.colModel;
36584         cm.setColumnWidth(i, w, true);
36585         var cid = cm.getColumnId(i);
36586         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36587         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36588         this.updateSplitters();
36589         this.layout();
36590         this.grid.fireEvent("columnresize", i, w);
36591     },
36592
36593     syncRowHeights : function(startIndex, endIndex){
36594         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36595             startIndex = startIndex || 0;
36596             var mrows = this.getBodyTable().rows;
36597             var lrows = this.getLockedTable().rows;
36598             var len = mrows.length-1;
36599             endIndex = Math.min(endIndex || len, len);
36600             for(var i = startIndex; i <= endIndex; i++){
36601                 var m = mrows[i], l = lrows[i];
36602                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36603                 m.style.height = l.style.height = h + "px";
36604             }
36605         }
36606     },
36607
36608     layout : function(initialRender, is2ndPass){
36609         var g = this.grid;
36610         var auto = g.autoHeight;
36611         var scrollOffset = 16;
36612         var c = g.getGridEl(), cm = this.cm,
36613                 expandCol = g.autoExpandColumn,
36614                 gv = this;
36615         //c.beginMeasure();
36616
36617         if(!c.dom.offsetWidth){ // display:none?
36618             if(initialRender){
36619                 this.lockedWrap.show();
36620                 this.mainWrap.show();
36621             }
36622             return;
36623         }
36624
36625         var hasLock = this.cm.isLocked(0);
36626
36627         var tbh = this.headerPanel.getHeight();
36628         var bbh = this.footerPanel.getHeight();
36629
36630         if(auto){
36631             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36632             var newHeight = ch + c.getBorderWidth("tb");
36633             if(g.maxHeight){
36634                 newHeight = Math.min(g.maxHeight, newHeight);
36635             }
36636             c.setHeight(newHeight);
36637         }
36638
36639         if(g.autoWidth){
36640             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36641         }
36642
36643         var s = this.scroller;
36644
36645         var csize = c.getSize(true);
36646
36647         this.el.setSize(csize.width, csize.height);
36648
36649         this.headerPanel.setWidth(csize.width);
36650         this.footerPanel.setWidth(csize.width);
36651
36652         var hdHeight = this.mainHd.getHeight();
36653         var vw = csize.width;
36654         var vh = csize.height - (tbh + bbh);
36655
36656         s.setSize(vw, vh);
36657
36658         var bt = this.getBodyTable();
36659         var ltWidth = hasLock ?
36660                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36661
36662         var scrollHeight = bt.offsetHeight;
36663         var scrollWidth = ltWidth + bt.offsetWidth;
36664         var vscroll = false, hscroll = false;
36665
36666         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36667
36668         var lw = this.lockedWrap, mw = this.mainWrap;
36669         var lb = this.lockedBody, mb = this.mainBody;
36670
36671         setTimeout(function(){
36672             var t = s.dom.offsetTop;
36673             var w = s.dom.clientWidth,
36674                 h = s.dom.clientHeight;
36675
36676             lw.setTop(t);
36677             lw.setSize(ltWidth, h);
36678
36679             mw.setLeftTop(ltWidth, t);
36680             mw.setSize(w-ltWidth, h);
36681
36682             lb.setHeight(h-hdHeight);
36683             mb.setHeight(h-hdHeight);
36684
36685             if(is2ndPass !== true && !gv.userResized && expandCol){
36686                 // high speed resize without full column calculation
36687                 
36688                 var ci = cm.getIndexById(expandCol);
36689                 if (ci < 0) {
36690                     ci = cm.findColumnIndex(expandCol);
36691                 }
36692                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36693                 var expandId = cm.getColumnId(ci);
36694                 var  tw = cm.getTotalWidth(false);
36695                 var currentWidth = cm.getColumnWidth(ci);
36696                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36697                 if(currentWidth != cw){
36698                     cm.setColumnWidth(ci, cw, true);
36699                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36700                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36701                     gv.updateSplitters();
36702                     gv.layout(false, true);
36703                 }
36704             }
36705
36706             if(initialRender){
36707                 lw.show();
36708                 mw.show();
36709             }
36710             //c.endMeasure();
36711         }, 10);
36712     },
36713
36714     onWindowResize : function(){
36715         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36716             return;
36717         }
36718         this.layout();
36719     },
36720
36721     appendFooter : function(parentEl){
36722         return null;
36723     },
36724
36725     sortAscText : "Sort Ascending",
36726     sortDescText : "Sort Descending",
36727     lockText : "Lock Column",
36728     unlockText : "Unlock Column",
36729     columnsText : "Columns"
36730 });
36731
36732
36733 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36734     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36735     this.proxy.el.addClass('x-grid3-col-dd');
36736 };
36737
36738 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36739     handleMouseDown : function(e){
36740
36741     },
36742
36743     callHandleMouseDown : function(e){
36744         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36745     }
36746 });
36747 /*
36748  * Based on:
36749  * Ext JS Library 1.1.1
36750  * Copyright(c) 2006-2007, Ext JS, LLC.
36751  *
36752  * Originally Released Under LGPL - original licence link has changed is not relivant.
36753  *
36754  * Fork - LGPL
36755  * <script type="text/javascript">
36756  */
36757  
36758 // private
36759 // This is a support class used internally by the Grid components
36760 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36761     this.grid = grid;
36762     this.view = grid.getView();
36763     this.proxy = this.view.resizeProxy;
36764     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36765         "gridSplitters" + this.grid.getGridEl().id, {
36766         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36767     });
36768     this.setHandleElId(Roo.id(hd));
36769     this.setOuterHandleElId(Roo.id(hd2));
36770     this.scroll = false;
36771 };
36772 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36773     fly: Roo.Element.fly,
36774
36775     b4StartDrag : function(x, y){
36776         this.view.headersDisabled = true;
36777         this.proxy.setHeight(this.view.mainWrap.getHeight());
36778         var w = this.cm.getColumnWidth(this.cellIndex);
36779         var minw = Math.max(w-this.grid.minColumnWidth, 0);
36780         this.resetConstraints();
36781         this.setXConstraint(minw, 1000);
36782         this.setYConstraint(0, 0);
36783         this.minX = x - minw;
36784         this.maxX = x + 1000;
36785         this.startPos = x;
36786         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
36787     },
36788
36789
36790     handleMouseDown : function(e){
36791         ev = Roo.EventObject.setEvent(e);
36792         var t = this.fly(ev.getTarget());
36793         if(t.hasClass("x-grid-split")){
36794             this.cellIndex = this.view.getCellIndex(t.dom);
36795             this.split = t.dom;
36796             this.cm = this.grid.colModel;
36797             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
36798                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
36799             }
36800         }
36801     },
36802
36803     endDrag : function(e){
36804         this.view.headersDisabled = false;
36805         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
36806         var diff = endX - this.startPos;
36807         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
36808     },
36809
36810     autoOffset : function(){
36811         this.setDelta(0,0);
36812     }
36813 });/*
36814  * Based on:
36815  * Ext JS Library 1.1.1
36816  * Copyright(c) 2006-2007, Ext JS, LLC.
36817  *
36818  * Originally Released Under LGPL - original licence link has changed is not relivant.
36819  *
36820  * Fork - LGPL
36821  * <script type="text/javascript">
36822  */
36823  
36824 // private
36825 // This is a support class used internally by the Grid components
36826 Roo.grid.GridDragZone = function(grid, config){
36827     this.view = grid.getView();
36828     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
36829     if(this.view.lockedBody){
36830         this.setHandleElId(Roo.id(this.view.mainBody.dom));
36831         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
36832     }
36833     this.scroll = false;
36834     this.grid = grid;
36835     this.ddel = document.createElement('div');
36836     this.ddel.className = 'x-grid-dd-wrap';
36837 };
36838
36839 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
36840     ddGroup : "GridDD",
36841
36842     getDragData : function(e){
36843         var t = Roo.lib.Event.getTarget(e);
36844         var rowIndex = this.view.findRowIndex(t);
36845         var sm = this.grid.selModel;
36846             
36847         //Roo.log(rowIndex);
36848         
36849         if (sm.getSelectedCell) {
36850             // cell selection..
36851             if (!sm.getSelectedCell()) {
36852                 return false;
36853             }
36854             if (rowIndex != sm.getSelectedCell()[0]) {
36855                 return false;
36856             }
36857         
36858         }
36859         
36860         if(rowIndex !== false){
36861             
36862             // if editorgrid.. 
36863             
36864             
36865             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
36866                
36867             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
36868               //  
36869             //}
36870             if (e.hasModifier()){
36871                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
36872             }
36873             
36874             Roo.log("getDragData");
36875             
36876             return {
36877                 grid: this.grid,
36878                 ddel: this.ddel,
36879                 rowIndex: rowIndex,
36880                 selections:sm.getSelections ? sm.getSelections() : (
36881                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
36882                 )
36883             };
36884         }
36885         return false;
36886     },
36887
36888     onInitDrag : function(e){
36889         var data = this.dragData;
36890         this.ddel.innerHTML = this.grid.getDragDropText();
36891         this.proxy.update(this.ddel);
36892         // fire start drag?
36893     },
36894
36895     afterRepair : function(){
36896         this.dragging = false;
36897     },
36898
36899     getRepairXY : function(e, data){
36900         return false;
36901     },
36902
36903     onEndDrag : function(data, e){
36904         // fire end drag?
36905     },
36906
36907     onValidDrop : function(dd, e, id){
36908         // fire drag drop?
36909         this.hideProxy();
36910     },
36911
36912     beforeInvalidDrop : function(e, id){
36913
36914     }
36915 });/*
36916  * Based on:
36917  * Ext JS Library 1.1.1
36918  * Copyright(c) 2006-2007, Ext JS, LLC.
36919  *
36920  * Originally Released Under LGPL - original licence link has changed is not relivant.
36921  *
36922  * Fork - LGPL
36923  * <script type="text/javascript">
36924  */
36925  
36926
36927 /**
36928  * @class Roo.grid.ColumnModel
36929  * @extends Roo.util.Observable
36930  * This is the default implementation of a ColumnModel used by the Grid. It defines
36931  * the columns in the grid.
36932  * <br>Usage:<br>
36933  <pre><code>
36934  var colModel = new Roo.grid.ColumnModel([
36935         {header: "Ticker", width: 60, sortable: true, locked: true},
36936         {header: "Company Name", width: 150, sortable: true},
36937         {header: "Market Cap.", width: 100, sortable: true},
36938         {header: "$ Sales", width: 100, sortable: true, renderer: money},
36939         {header: "Employees", width: 100, sortable: true, resizable: false}
36940  ]);
36941  </code></pre>
36942  * <p>
36943  
36944  * The config options listed for this class are options which may appear in each
36945  * individual column definition.
36946  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
36947  * @constructor
36948  * @param {Object} config An Array of column config objects. See this class's
36949  * config objects for details.
36950 */
36951 Roo.grid.ColumnModel = function(config){
36952         /**
36953      * The config passed into the constructor
36954      */
36955     this.config = config;
36956     this.lookup = {};
36957
36958     // if no id, create one
36959     // if the column does not have a dataIndex mapping,
36960     // map it to the order it is in the config
36961     for(var i = 0, len = config.length; i < len; i++){
36962         var c = config[i];
36963         if(typeof c.dataIndex == "undefined"){
36964             c.dataIndex = i;
36965         }
36966         if(typeof c.renderer == "string"){
36967             c.renderer = Roo.util.Format[c.renderer];
36968         }
36969         if(typeof c.id == "undefined"){
36970             c.id = Roo.id();
36971         }
36972         if(c.editor && c.editor.xtype){
36973             c.editor  = Roo.factory(c.editor, Roo.grid);
36974         }
36975         if(c.editor && c.editor.isFormField){
36976             c.editor = new Roo.grid.GridEditor(c.editor);
36977         }
36978         this.lookup[c.id] = c;
36979     }
36980
36981     /**
36982      * The width of columns which have no width specified (defaults to 100)
36983      * @type Number
36984      */
36985     this.defaultWidth = 100;
36986
36987     /**
36988      * Default sortable of columns which have no sortable specified (defaults to false)
36989      * @type Boolean
36990      */
36991     this.defaultSortable = false;
36992
36993     this.addEvents({
36994         /**
36995              * @event widthchange
36996              * Fires when the width of a column changes.
36997              * @param {ColumnModel} this
36998              * @param {Number} columnIndex The column index
36999              * @param {Number} newWidth The new width
37000              */
37001             "widthchange": true,
37002         /**
37003              * @event headerchange
37004              * Fires when the text of a header changes.
37005              * @param {ColumnModel} this
37006              * @param {Number} columnIndex The column index
37007              * @param {Number} newText The new header text
37008              */
37009             "headerchange": true,
37010         /**
37011              * @event hiddenchange
37012              * Fires when a column is hidden or "unhidden".
37013              * @param {ColumnModel} this
37014              * @param {Number} columnIndex The column index
37015              * @param {Boolean} hidden true if hidden, false otherwise
37016              */
37017             "hiddenchange": true,
37018             /**
37019          * @event columnmoved
37020          * Fires when a column is moved.
37021          * @param {ColumnModel} this
37022          * @param {Number} oldIndex
37023          * @param {Number} newIndex
37024          */
37025         "columnmoved" : true,
37026         /**
37027          * @event columlockchange
37028          * Fires when a column's locked state is changed
37029          * @param {ColumnModel} this
37030          * @param {Number} colIndex
37031          * @param {Boolean} locked true if locked
37032          */
37033         "columnlockchange" : true
37034     });
37035     Roo.grid.ColumnModel.superclass.constructor.call(this);
37036 };
37037 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
37038     /**
37039      * @cfg {String} header The header text to display in the Grid view.
37040      */
37041     /**
37042      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
37043      * {@link Roo.data.Record} definition from which to draw the column's value. If not
37044      * specified, the column's index is used as an index into the Record's data Array.
37045      */
37046     /**
37047      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
37048      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
37049      */
37050     /**
37051      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
37052      * Defaults to the value of the {@link #defaultSortable} property.
37053      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
37054      */
37055     /**
37056      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
37057      */
37058     /**
37059      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
37060      */
37061     /**
37062      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
37063      */
37064     /**
37065      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
37066      */
37067     /**
37068      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
37069      * given the cell's data value. See {@link #setRenderer}. If not specified, the
37070      * default renderer uses the raw data value.
37071      */
37072        /**
37073      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
37074      */
37075     /**
37076      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
37077      */
37078
37079     /**
37080      * Returns the id of the column at the specified index.
37081      * @param {Number} index The column index
37082      * @return {String} the id
37083      */
37084     getColumnId : function(index){
37085         return this.config[index].id;
37086     },
37087
37088     /**
37089      * Returns the column for a specified id.
37090      * @param {String} id The column id
37091      * @return {Object} the column
37092      */
37093     getColumnById : function(id){
37094         return this.lookup[id];
37095     },
37096
37097     
37098     /**
37099      * Returns the column for a specified dataIndex.
37100      * @param {String} dataIndex The column dataIndex
37101      * @return {Object|Boolean} the column or false if not found
37102      */
37103     getColumnByDataIndex: function(dataIndex){
37104         var index = this.findColumnIndex(dataIndex);
37105         return index > -1 ? this.config[index] : false;
37106     },
37107     
37108     /**
37109      * Returns the index for a specified column id.
37110      * @param {String} id The column id
37111      * @return {Number} the index, or -1 if not found
37112      */
37113     getIndexById : function(id){
37114         for(var i = 0, len = this.config.length; i < len; i++){
37115             if(this.config[i].id == id){
37116                 return i;
37117             }
37118         }
37119         return -1;
37120     },
37121     
37122     /**
37123      * Returns the index for a specified column dataIndex.
37124      * @param {String} dataIndex The column dataIndex
37125      * @return {Number} the index, or -1 if not found
37126      */
37127     
37128     findColumnIndex : function(dataIndex){
37129         for(var i = 0, len = this.config.length; i < len; i++){
37130             if(this.config[i].dataIndex == dataIndex){
37131                 return i;
37132             }
37133         }
37134         return -1;
37135     },
37136     
37137     
37138     moveColumn : function(oldIndex, newIndex){
37139         var c = this.config[oldIndex];
37140         this.config.splice(oldIndex, 1);
37141         this.config.splice(newIndex, 0, c);
37142         this.dataMap = null;
37143         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37144     },
37145
37146     isLocked : function(colIndex){
37147         return this.config[colIndex].locked === true;
37148     },
37149
37150     setLocked : function(colIndex, value, suppressEvent){
37151         if(this.isLocked(colIndex) == value){
37152             return;
37153         }
37154         this.config[colIndex].locked = value;
37155         if(!suppressEvent){
37156             this.fireEvent("columnlockchange", this, colIndex, value);
37157         }
37158     },
37159
37160     getTotalLockedWidth : function(){
37161         var totalWidth = 0;
37162         for(var i = 0; i < this.config.length; i++){
37163             if(this.isLocked(i) && !this.isHidden(i)){
37164                 this.totalWidth += this.getColumnWidth(i);
37165             }
37166         }
37167         return totalWidth;
37168     },
37169
37170     getLockedCount : function(){
37171         for(var i = 0, len = this.config.length; i < len; i++){
37172             if(!this.isLocked(i)){
37173                 return i;
37174             }
37175         }
37176     },
37177
37178     /**
37179      * Returns the number of columns.
37180      * @return {Number}
37181      */
37182     getColumnCount : function(visibleOnly){
37183         if(visibleOnly === true){
37184             var c = 0;
37185             for(var i = 0, len = this.config.length; i < len; i++){
37186                 if(!this.isHidden(i)){
37187                     c++;
37188                 }
37189             }
37190             return c;
37191         }
37192         return this.config.length;
37193     },
37194
37195     /**
37196      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37197      * @param {Function} fn
37198      * @param {Object} scope (optional)
37199      * @return {Array} result
37200      */
37201     getColumnsBy : function(fn, scope){
37202         var r = [];
37203         for(var i = 0, len = this.config.length; i < len; i++){
37204             var c = this.config[i];
37205             if(fn.call(scope||this, c, i) === true){
37206                 r[r.length] = c;
37207             }
37208         }
37209         return r;
37210     },
37211
37212     /**
37213      * Returns true if the specified column is sortable.
37214      * @param {Number} col The column index
37215      * @return {Boolean}
37216      */
37217     isSortable : function(col){
37218         if(typeof this.config[col].sortable == "undefined"){
37219             return this.defaultSortable;
37220         }
37221         return this.config[col].sortable;
37222     },
37223
37224     /**
37225      * Returns the rendering (formatting) function defined for the column.
37226      * @param {Number} col The column index.
37227      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37228      */
37229     getRenderer : function(col){
37230         if(!this.config[col].renderer){
37231             return Roo.grid.ColumnModel.defaultRenderer;
37232         }
37233         return this.config[col].renderer;
37234     },
37235
37236     /**
37237      * Sets the rendering (formatting) function for a column.
37238      * @param {Number} col The column index
37239      * @param {Function} fn The function to use to process the cell's raw data
37240      * to return HTML markup for the grid view. The render function is called with
37241      * the following parameters:<ul>
37242      * <li>Data value.</li>
37243      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37244      * <li>css A CSS style string to apply to the table cell.</li>
37245      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37246      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37247      * <li>Row index</li>
37248      * <li>Column index</li>
37249      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37250      */
37251     setRenderer : function(col, fn){
37252         this.config[col].renderer = fn;
37253     },
37254
37255     /**
37256      * Returns the width for the specified column.
37257      * @param {Number} col The column index
37258      * @return {Number}
37259      */
37260     getColumnWidth : function(col){
37261         return this.config[col].width * 1 || this.defaultWidth;
37262     },
37263
37264     /**
37265      * Sets the width for a column.
37266      * @param {Number} col The column index
37267      * @param {Number} width The new width
37268      */
37269     setColumnWidth : function(col, width, suppressEvent){
37270         this.config[col].width = width;
37271         this.totalWidth = null;
37272         if(!suppressEvent){
37273              this.fireEvent("widthchange", this, col, width);
37274         }
37275     },
37276
37277     /**
37278      * Returns the total width of all columns.
37279      * @param {Boolean} includeHidden True to include hidden column widths
37280      * @return {Number}
37281      */
37282     getTotalWidth : function(includeHidden){
37283         if(!this.totalWidth){
37284             this.totalWidth = 0;
37285             for(var i = 0, len = this.config.length; i < len; i++){
37286                 if(includeHidden || !this.isHidden(i)){
37287                     this.totalWidth += this.getColumnWidth(i);
37288                 }
37289             }
37290         }
37291         return this.totalWidth;
37292     },
37293
37294     /**
37295      * Returns the header for the specified column.
37296      * @param {Number} col The column index
37297      * @return {String}
37298      */
37299     getColumnHeader : function(col){
37300         return this.config[col].header;
37301     },
37302
37303     /**
37304      * Sets the header for a column.
37305      * @param {Number} col The column index
37306      * @param {String} header The new header
37307      */
37308     setColumnHeader : function(col, header){
37309         this.config[col].header = header;
37310         this.fireEvent("headerchange", this, col, header);
37311     },
37312
37313     /**
37314      * Returns the tooltip for the specified column.
37315      * @param {Number} col The column index
37316      * @return {String}
37317      */
37318     getColumnTooltip : function(col){
37319             return this.config[col].tooltip;
37320     },
37321     /**
37322      * Sets the tooltip for a column.
37323      * @param {Number} col The column index
37324      * @param {String} tooltip The new tooltip
37325      */
37326     setColumnTooltip : function(col, tooltip){
37327             this.config[col].tooltip = tooltip;
37328     },
37329
37330     /**
37331      * Returns the dataIndex for the specified column.
37332      * @param {Number} col The column index
37333      * @return {Number}
37334      */
37335     getDataIndex : function(col){
37336         return this.config[col].dataIndex;
37337     },
37338
37339     /**
37340      * Sets the dataIndex for a column.
37341      * @param {Number} col The column index
37342      * @param {Number} dataIndex The new dataIndex
37343      */
37344     setDataIndex : function(col, dataIndex){
37345         this.config[col].dataIndex = dataIndex;
37346     },
37347
37348     
37349     
37350     /**
37351      * Returns true if the cell is editable.
37352      * @param {Number} colIndex The column index
37353      * @param {Number} rowIndex The row index
37354      * @return {Boolean}
37355      */
37356     isCellEditable : function(colIndex, rowIndex){
37357         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37358     },
37359
37360     /**
37361      * Returns the editor defined for the cell/column.
37362      * return false or null to disable editing.
37363      * @param {Number} colIndex The column index
37364      * @param {Number} rowIndex The row index
37365      * @return {Object}
37366      */
37367     getCellEditor : function(colIndex, rowIndex){
37368         return this.config[colIndex].editor;
37369     },
37370
37371     /**
37372      * Sets if a column is editable.
37373      * @param {Number} col The column index
37374      * @param {Boolean} editable True if the column is editable
37375      */
37376     setEditable : function(col, editable){
37377         this.config[col].editable = editable;
37378     },
37379
37380
37381     /**
37382      * Returns true if the column is hidden.
37383      * @param {Number} colIndex The column index
37384      * @return {Boolean}
37385      */
37386     isHidden : function(colIndex){
37387         return this.config[colIndex].hidden;
37388     },
37389
37390
37391     /**
37392      * Returns true if the column width cannot be changed
37393      */
37394     isFixed : function(colIndex){
37395         return this.config[colIndex].fixed;
37396     },
37397
37398     /**
37399      * Returns true if the column can be resized
37400      * @return {Boolean}
37401      */
37402     isResizable : function(colIndex){
37403         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37404     },
37405     /**
37406      * Sets if a column is hidden.
37407      * @param {Number} colIndex The column index
37408      * @param {Boolean} hidden True if the column is hidden
37409      */
37410     setHidden : function(colIndex, hidden){
37411         this.config[colIndex].hidden = hidden;
37412         this.totalWidth = null;
37413         this.fireEvent("hiddenchange", this, colIndex, hidden);
37414     },
37415
37416     /**
37417      * Sets the editor for a column.
37418      * @param {Number} col The column index
37419      * @param {Object} editor The editor object
37420      */
37421     setEditor : function(col, editor){
37422         this.config[col].editor = editor;
37423     }
37424 });
37425
37426 Roo.grid.ColumnModel.defaultRenderer = function(value){
37427         if(typeof value == "string" && value.length < 1){
37428             return "&#160;";
37429         }
37430         return value;
37431 };
37432
37433 // Alias for backwards compatibility
37434 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
37435 /*
37436  * Based on:
37437  * Ext JS Library 1.1.1
37438  * Copyright(c) 2006-2007, Ext JS, LLC.
37439  *
37440  * Originally Released Under LGPL - original licence link has changed is not relivant.
37441  *
37442  * Fork - LGPL
37443  * <script type="text/javascript">
37444  */
37445
37446 /**
37447  * @class Roo.grid.AbstractSelectionModel
37448  * @extends Roo.util.Observable
37449  * Abstract base class for grid SelectionModels.  It provides the interface that should be
37450  * implemented by descendant classes.  This class should not be directly instantiated.
37451  * @constructor
37452  */
37453 Roo.grid.AbstractSelectionModel = function(){
37454     this.locked = false;
37455     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
37456 };
37457
37458 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
37459     /** @ignore Called by the grid automatically. Do not call directly. */
37460     init : function(grid){
37461         this.grid = grid;
37462         this.initEvents();
37463     },
37464
37465     /**
37466      * Locks the selections.
37467      */
37468     lock : function(){
37469         this.locked = true;
37470     },
37471
37472     /**
37473      * Unlocks the selections.
37474      */
37475     unlock : function(){
37476         this.locked = false;
37477     },
37478
37479     /**
37480      * Returns true if the selections are locked.
37481      * @return {Boolean}
37482      */
37483     isLocked : function(){
37484         return this.locked;
37485     }
37486 });/*
37487  * Based on:
37488  * Ext JS Library 1.1.1
37489  * Copyright(c) 2006-2007, Ext JS, LLC.
37490  *
37491  * Originally Released Under LGPL - original licence link has changed is not relivant.
37492  *
37493  * Fork - LGPL
37494  * <script type="text/javascript">
37495  */
37496 /**
37497  * @extends Roo.grid.AbstractSelectionModel
37498  * @class Roo.grid.RowSelectionModel
37499  * The default SelectionModel used by {@link Roo.grid.Grid}.
37500  * It supports multiple selections and keyboard selection/navigation. 
37501  * @constructor
37502  * @param {Object} config
37503  */
37504 Roo.grid.RowSelectionModel = function(config){
37505     Roo.apply(this, config);
37506     this.selections = new Roo.util.MixedCollection(false, function(o){
37507         return o.id;
37508     });
37509
37510     this.last = false;
37511     this.lastActive = false;
37512
37513     this.addEvents({
37514         /**
37515              * @event selectionchange
37516              * Fires when the selection changes
37517              * @param {SelectionModel} this
37518              */
37519             "selectionchange" : true,
37520         /**
37521              * @event afterselectionchange
37522              * Fires after the selection changes (eg. by key press or clicking)
37523              * @param {SelectionModel} this
37524              */
37525             "afterselectionchange" : true,
37526         /**
37527              * @event beforerowselect
37528              * Fires when a row is selected being selected, return false to cancel.
37529              * @param {SelectionModel} this
37530              * @param {Number} rowIndex The selected index
37531              * @param {Boolean} keepExisting False if other selections will be cleared
37532              */
37533             "beforerowselect" : true,
37534         /**
37535              * @event rowselect
37536              * Fires when a row is selected.
37537              * @param {SelectionModel} this
37538              * @param {Number} rowIndex The selected index
37539              * @param {Roo.data.Record} r The record
37540              */
37541             "rowselect" : true,
37542         /**
37543              * @event rowdeselect
37544              * Fires when a row is deselected.
37545              * @param {SelectionModel} this
37546              * @param {Number} rowIndex The selected index
37547              */
37548         "rowdeselect" : true
37549     });
37550     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37551     this.locked = false;
37552 };
37553
37554 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37555     /**
37556      * @cfg {Boolean} singleSelect
37557      * True to allow selection of only one row at a time (defaults to false)
37558      */
37559     singleSelect : false,
37560
37561     // private
37562     initEvents : function(){
37563
37564         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37565             this.grid.on("mousedown", this.handleMouseDown, this);
37566         }else{ // allow click to work like normal
37567             this.grid.on("rowclick", this.handleDragableRowClick, this);
37568         }
37569
37570         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37571             "up" : function(e){
37572                 if(!e.shiftKey){
37573                     this.selectPrevious(e.shiftKey);
37574                 }else if(this.last !== false && this.lastActive !== false){
37575                     var last = this.last;
37576                     this.selectRange(this.last,  this.lastActive-1);
37577                     this.grid.getView().focusRow(this.lastActive);
37578                     if(last !== false){
37579                         this.last = last;
37580                     }
37581                 }else{
37582                     this.selectFirstRow();
37583                 }
37584                 this.fireEvent("afterselectionchange", this);
37585             },
37586             "down" : function(e){
37587                 if(!e.shiftKey){
37588                     this.selectNext(e.shiftKey);
37589                 }else if(this.last !== false && this.lastActive !== false){
37590                     var last = this.last;
37591                     this.selectRange(this.last,  this.lastActive+1);
37592                     this.grid.getView().focusRow(this.lastActive);
37593                     if(last !== false){
37594                         this.last = last;
37595                     }
37596                 }else{
37597                     this.selectFirstRow();
37598                 }
37599                 this.fireEvent("afterselectionchange", this);
37600             },
37601             scope: this
37602         });
37603
37604         var view = this.grid.view;
37605         view.on("refresh", this.onRefresh, this);
37606         view.on("rowupdated", this.onRowUpdated, this);
37607         view.on("rowremoved", this.onRemove, this);
37608     },
37609
37610     // private
37611     onRefresh : function(){
37612         var ds = this.grid.dataSource, i, v = this.grid.view;
37613         var s = this.selections;
37614         s.each(function(r){
37615             if((i = ds.indexOfId(r.id)) != -1){
37616                 v.onRowSelect(i);
37617             }else{
37618                 s.remove(r);
37619             }
37620         });
37621     },
37622
37623     // private
37624     onRemove : function(v, index, r){
37625         this.selections.remove(r);
37626     },
37627
37628     // private
37629     onRowUpdated : function(v, index, r){
37630         if(this.isSelected(r)){
37631             v.onRowSelect(index);
37632         }
37633     },
37634
37635     /**
37636      * Select records.
37637      * @param {Array} records The records to select
37638      * @param {Boolean} keepExisting (optional) True to keep existing selections
37639      */
37640     selectRecords : function(records, keepExisting){
37641         if(!keepExisting){
37642             this.clearSelections();
37643         }
37644         var ds = this.grid.dataSource;
37645         for(var i = 0, len = records.length; i < len; i++){
37646             this.selectRow(ds.indexOf(records[i]), true);
37647         }
37648     },
37649
37650     /**
37651      * Gets the number of selected rows.
37652      * @return {Number}
37653      */
37654     getCount : function(){
37655         return this.selections.length;
37656     },
37657
37658     /**
37659      * Selects the first row in the grid.
37660      */
37661     selectFirstRow : function(){
37662         this.selectRow(0);
37663     },
37664
37665     /**
37666      * Select the last row.
37667      * @param {Boolean} keepExisting (optional) True to keep existing selections
37668      */
37669     selectLastRow : function(keepExisting){
37670         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37671     },
37672
37673     /**
37674      * Selects the row immediately following the last selected row.
37675      * @param {Boolean} keepExisting (optional) True to keep existing selections
37676      */
37677     selectNext : function(keepExisting){
37678         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37679             this.selectRow(this.last+1, keepExisting);
37680             this.grid.getView().focusRow(this.last);
37681         }
37682     },
37683
37684     /**
37685      * Selects the row that precedes the last selected row.
37686      * @param {Boolean} keepExisting (optional) True to keep existing selections
37687      */
37688     selectPrevious : function(keepExisting){
37689         if(this.last){
37690             this.selectRow(this.last-1, keepExisting);
37691             this.grid.getView().focusRow(this.last);
37692         }
37693     },
37694
37695     /**
37696      * Returns the selected records
37697      * @return {Array} Array of selected records
37698      */
37699     getSelections : function(){
37700         return [].concat(this.selections.items);
37701     },
37702
37703     /**
37704      * Returns the first selected record.
37705      * @return {Record}
37706      */
37707     getSelected : function(){
37708         return this.selections.itemAt(0);
37709     },
37710
37711
37712     /**
37713      * Clears all selections.
37714      */
37715     clearSelections : function(fast){
37716         if(this.locked) return;
37717         if(fast !== true){
37718             var ds = this.grid.dataSource;
37719             var s = this.selections;
37720             s.each(function(r){
37721                 this.deselectRow(ds.indexOfId(r.id));
37722             }, this);
37723             s.clear();
37724         }else{
37725             this.selections.clear();
37726         }
37727         this.last = false;
37728     },
37729
37730
37731     /**
37732      * Selects all rows.
37733      */
37734     selectAll : function(){
37735         if(this.locked) return;
37736         this.selections.clear();
37737         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37738             this.selectRow(i, true);
37739         }
37740     },
37741
37742     /**
37743      * Returns True if there is a selection.
37744      * @return {Boolean}
37745      */
37746     hasSelection : function(){
37747         return this.selections.length > 0;
37748     },
37749
37750     /**
37751      * Returns True if the specified row is selected.
37752      * @param {Number/Record} record The record or index of the record to check
37753      * @return {Boolean}
37754      */
37755     isSelected : function(index){
37756         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37757         return (r && this.selections.key(r.id) ? true : false);
37758     },
37759
37760     /**
37761      * Returns True if the specified record id is selected.
37762      * @param {String} id The id of record to check
37763      * @return {Boolean}
37764      */
37765     isIdSelected : function(id){
37766         return (this.selections.key(id) ? true : false);
37767     },
37768
37769     // private
37770     handleMouseDown : function(e, t){
37771         var view = this.grid.getView(), rowIndex;
37772         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37773             return;
37774         };
37775         if(e.shiftKey && this.last !== false){
37776             var last = this.last;
37777             this.selectRange(last, rowIndex, e.ctrlKey);
37778             this.last = last; // reset the last
37779             view.focusRow(rowIndex);
37780         }else{
37781             var isSelected = this.isSelected(rowIndex);
37782             if(e.button !== 0 && isSelected){
37783                 view.focusRow(rowIndex);
37784             }else if(e.ctrlKey && isSelected){
37785                 this.deselectRow(rowIndex);
37786             }else if(!isSelected){
37787                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
37788                 view.focusRow(rowIndex);
37789             }
37790         }
37791         this.fireEvent("afterselectionchange", this);
37792     },
37793     // private
37794     handleDragableRowClick :  function(grid, rowIndex, e) 
37795     {
37796         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
37797             this.selectRow(rowIndex, false);
37798             grid.view.focusRow(rowIndex);
37799              this.fireEvent("afterselectionchange", this);
37800         }
37801     },
37802     
37803     /**
37804      * Selects multiple rows.
37805      * @param {Array} rows Array of the indexes of the row to select
37806      * @param {Boolean} keepExisting (optional) True to keep existing selections
37807      */
37808     selectRows : function(rows, keepExisting){
37809         if(!keepExisting){
37810             this.clearSelections();
37811         }
37812         for(var i = 0, len = rows.length; i < len; i++){
37813             this.selectRow(rows[i], true);
37814         }
37815     },
37816
37817     /**
37818      * Selects a range of rows. All rows in between startRow and endRow are also selected.
37819      * @param {Number} startRow The index of the first row in the range
37820      * @param {Number} endRow The index of the last row in the range
37821      * @param {Boolean} keepExisting (optional) True to retain existing selections
37822      */
37823     selectRange : function(startRow, endRow, keepExisting){
37824         if(this.locked) return;
37825         if(!keepExisting){
37826             this.clearSelections();
37827         }
37828         if(startRow <= endRow){
37829             for(var i = startRow; i <= endRow; i++){
37830                 this.selectRow(i, true);
37831             }
37832         }else{
37833             for(var i = startRow; i >= endRow; i--){
37834                 this.selectRow(i, true);
37835             }
37836         }
37837     },
37838
37839     /**
37840      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
37841      * @param {Number} startRow The index of the first row in the range
37842      * @param {Number} endRow The index of the last row in the range
37843      */
37844     deselectRange : function(startRow, endRow, preventViewNotify){
37845         if(this.locked) return;
37846         for(var i = startRow; i <= endRow; i++){
37847             this.deselectRow(i, preventViewNotify);
37848         }
37849     },
37850
37851     /**
37852      * Selects a row.
37853      * @param {Number} row The index of the row to select
37854      * @param {Boolean} keepExisting (optional) True to keep existing selections
37855      */
37856     selectRow : function(index, keepExisting, preventViewNotify){
37857         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
37858         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
37859             if(!keepExisting || this.singleSelect){
37860                 this.clearSelections();
37861             }
37862             var r = this.grid.dataSource.getAt(index);
37863             this.selections.add(r);
37864             this.last = this.lastActive = index;
37865             if(!preventViewNotify){
37866                 this.grid.getView().onRowSelect(index);
37867             }
37868             this.fireEvent("rowselect", this, index, r);
37869             this.fireEvent("selectionchange", this);
37870         }
37871     },
37872
37873     /**
37874      * Deselects a row.
37875      * @param {Number} row The index of the row to deselect
37876      */
37877     deselectRow : function(index, preventViewNotify){
37878         if(this.locked) return;
37879         if(this.last == index){
37880             this.last = false;
37881         }
37882         if(this.lastActive == index){
37883             this.lastActive = false;
37884         }
37885         var r = this.grid.dataSource.getAt(index);
37886         this.selections.remove(r);
37887         if(!preventViewNotify){
37888             this.grid.getView().onRowDeselect(index);
37889         }
37890         this.fireEvent("rowdeselect", this, index);
37891         this.fireEvent("selectionchange", this);
37892     },
37893
37894     // private
37895     restoreLast : function(){
37896         if(this._last){
37897             this.last = this._last;
37898         }
37899     },
37900
37901     // private
37902     acceptsNav : function(row, col, cm){
37903         return !cm.isHidden(col) && cm.isCellEditable(col, row);
37904     },
37905
37906     // private
37907     onEditorKey : function(field, e){
37908         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
37909         if(k == e.TAB){
37910             e.stopEvent();
37911             ed.completeEdit();
37912             if(e.shiftKey){
37913                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
37914             }else{
37915                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
37916             }
37917         }else if(k == e.ENTER && !e.ctrlKey){
37918             e.stopEvent();
37919             ed.completeEdit();
37920             if(e.shiftKey){
37921                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
37922             }else{
37923                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
37924             }
37925         }else if(k == e.ESC){
37926             ed.cancelEdit();
37927         }
37928         if(newCell){
37929             g.startEditing(newCell[0], newCell[1]);
37930         }
37931     }
37932 });/*
37933  * Based on:
37934  * Ext JS Library 1.1.1
37935  * Copyright(c) 2006-2007, Ext JS, LLC.
37936  *
37937  * Originally Released Under LGPL - original licence link has changed is not relivant.
37938  *
37939  * Fork - LGPL
37940  * <script type="text/javascript">
37941  */
37942 /**
37943  * @class Roo.grid.CellSelectionModel
37944  * @extends Roo.grid.AbstractSelectionModel
37945  * This class provides the basic implementation for cell selection in a grid.
37946  * @constructor
37947  * @param {Object} config The object containing the configuration of this model.
37948  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
37949  */
37950 Roo.grid.CellSelectionModel = function(config){
37951     Roo.apply(this, config);
37952
37953     this.selection = null;
37954
37955     this.addEvents({
37956         /**
37957              * @event beforerowselect
37958              * Fires before a cell is selected.
37959              * @param {SelectionModel} this
37960              * @param {Number} rowIndex The selected row index
37961              * @param {Number} colIndex The selected cell index
37962              */
37963             "beforecellselect" : true,
37964         /**
37965              * @event cellselect
37966              * Fires when a cell is selected.
37967              * @param {SelectionModel} this
37968              * @param {Number} rowIndex The selected row index
37969              * @param {Number} colIndex The selected cell index
37970              */
37971             "cellselect" : true,
37972         /**
37973              * @event selectionchange
37974              * Fires when the active selection changes.
37975              * @param {SelectionModel} this
37976              * @param {Object} selection null for no selection or an object (o) with two properties
37977                 <ul>
37978                 <li>o.record: the record object for the row the selection is in</li>
37979                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
37980                 </ul>
37981              */
37982             "selectionchange" : true,
37983         /**
37984              * @event tabend
37985              * Fires when the tab (or enter) was pressed on the last editable cell
37986              * You can use this to trigger add new row.
37987              * @param {SelectionModel} this
37988              */
37989             "tabend" : true,
37990          /**
37991              * @event beforeeditnext
37992              * Fires before the next editable sell is made active
37993              * You can use this to skip to another cell or fire the tabend
37994              *    if you set cell to false
37995              * @param {Object} eventdata object : { cell : [ row, col ] } 
37996              */
37997             "beforeeditnext" : true
37998     });
37999     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
38000 };
38001
38002 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
38003     
38004     enter_is_tab: false,
38005
38006     /** @ignore */
38007     initEvents : function(){
38008         this.grid.on("mousedown", this.handleMouseDown, this);
38009         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
38010         var view = this.grid.view;
38011         view.on("refresh", this.onViewChange, this);
38012         view.on("rowupdated", this.onRowUpdated, this);
38013         view.on("beforerowremoved", this.clearSelections, this);
38014         view.on("beforerowsinserted", this.clearSelections, this);
38015         if(this.grid.isEditor){
38016             this.grid.on("beforeedit", this.beforeEdit,  this);
38017         }
38018     },
38019
38020         //private
38021     beforeEdit : function(e){
38022         this.select(e.row, e.column, false, true, e.record);
38023     },
38024
38025         //private
38026     onRowUpdated : function(v, index, r){
38027         if(this.selection && this.selection.record == r){
38028             v.onCellSelect(index, this.selection.cell[1]);
38029         }
38030     },
38031
38032         //private
38033     onViewChange : function(){
38034         this.clearSelections(true);
38035     },
38036
38037         /**
38038          * Returns the currently selected cell,.
38039          * @return {Array} The selected cell (row, column) or null if none selected.
38040          */
38041     getSelectedCell : function(){
38042         return this.selection ? this.selection.cell : null;
38043     },
38044
38045     /**
38046      * Clears all selections.
38047      * @param {Boolean} true to prevent the gridview from being notified about the change.
38048      */
38049     clearSelections : function(preventNotify){
38050         var s = this.selection;
38051         if(s){
38052             if(preventNotify !== true){
38053                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
38054             }
38055             this.selection = null;
38056             this.fireEvent("selectionchange", this, null);
38057         }
38058     },
38059
38060     /**
38061      * Returns true if there is a selection.
38062      * @return {Boolean}
38063      */
38064     hasSelection : function(){
38065         return this.selection ? true : false;
38066     },
38067
38068     /** @ignore */
38069     handleMouseDown : function(e, t){
38070         var v = this.grid.getView();
38071         if(this.isLocked()){
38072             return;
38073         };
38074         var row = v.findRowIndex(t);
38075         var cell = v.findCellIndex(t);
38076         if(row !== false && cell !== false){
38077             this.select(row, cell);
38078         }
38079     },
38080
38081     /**
38082      * Selects a cell.
38083      * @param {Number} rowIndex
38084      * @param {Number} collIndex
38085      */
38086     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38087         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38088             this.clearSelections();
38089             r = r || this.grid.dataSource.getAt(rowIndex);
38090             this.selection = {
38091                 record : r,
38092                 cell : [rowIndex, colIndex]
38093             };
38094             if(!preventViewNotify){
38095                 var v = this.grid.getView();
38096                 v.onCellSelect(rowIndex, colIndex);
38097                 if(preventFocus !== true){
38098                     v.focusCell(rowIndex, colIndex);
38099                 }
38100             }
38101             this.fireEvent("cellselect", this, rowIndex, colIndex);
38102             this.fireEvent("selectionchange", this, this.selection);
38103         }
38104     },
38105
38106         //private
38107     isSelectable : function(rowIndex, colIndex, cm){
38108         return !cm.isHidden(colIndex);
38109     },
38110
38111     /** @ignore */
38112     handleKeyDown : function(e){
38113         //Roo.log('Cell Sel Model handleKeyDown');
38114         if(!e.isNavKeyPress()){
38115             return;
38116         }
38117         var g = this.grid, s = this.selection;
38118         if(!s){
38119             e.stopEvent();
38120             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38121             if(cell){
38122                 this.select(cell[0], cell[1]);
38123             }
38124             return;
38125         }
38126         var sm = this;
38127         var walk = function(row, col, step){
38128             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38129         };
38130         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38131         var newCell;
38132
38133       
38134
38135         switch(k){
38136             case e.TAB:
38137                 // handled by onEditorKey
38138                 if (g.isEditor && g.editing) {
38139                     return;
38140                 }
38141                 if(e.shiftKey) {
38142                     newCell = walk(r, c-1, -1);
38143                 } else {
38144                     newCell = walk(r, c+1, 1);
38145                 }
38146                 break;
38147             
38148             case e.DOWN:
38149                newCell = walk(r+1, c, 1);
38150                 break;
38151             
38152             case e.UP:
38153                 newCell = walk(r-1, c, -1);
38154                 break;
38155             
38156             case e.RIGHT:
38157                 newCell = walk(r, c+1, 1);
38158                 break;
38159             
38160             case e.LEFT:
38161                 newCell = walk(r, c-1, -1);
38162                 break;
38163             
38164             case e.ENTER:
38165                 
38166                 if(g.isEditor && !g.editing){
38167                    g.startEditing(r, c);
38168                    e.stopEvent();
38169                    return;
38170                 }
38171                 
38172                 
38173              break;
38174         };
38175         if(newCell){
38176             this.select(newCell[0], newCell[1]);
38177             e.stopEvent();
38178             
38179         }
38180     },
38181
38182     acceptsNav : function(row, col, cm){
38183         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38184     },
38185     /**
38186      * Selects a cell.
38187      * @param {Number} field (not used) - as it's normally used as a listener
38188      * @param {Number} e - event - fake it by using
38189      *
38190      * var e = Roo.EventObjectImpl.prototype;
38191      * e.keyCode = e.TAB
38192      *
38193      * 
38194      */
38195     onEditorKey : function(field, e){
38196         
38197         var k = e.getKey(),
38198             newCell,
38199             g = this.grid,
38200             ed = g.activeEditor,
38201             forward = false;
38202         ///Roo.log('onEditorKey' + k);
38203         
38204         
38205         if (this.enter_is_tab && k == e.ENTER) {
38206             k = e.TAB;
38207         }
38208         
38209         if(k == e.TAB){
38210             if(e.shiftKey){
38211                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38212             }else{
38213                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38214                 forward = true;
38215             }
38216             
38217             e.stopEvent();
38218             
38219         } else if(k == e.ENTER &&  !e.ctrlKey){
38220             ed.completeEdit();
38221             e.stopEvent();
38222             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38223         
38224                 } else if(k == e.ESC){
38225             ed.cancelEdit();
38226         }
38227                 
38228         if (newCell) {
38229             var ecall = { cell : newCell, forward : forward };
38230             this.fireEvent('beforeeditnext', ecall );
38231             newCell = ecall.cell;
38232                         forward = ecall.forward;
38233         }
38234                 
38235         if(newCell){
38236             //Roo.log('next cell after edit');
38237             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38238         } else if (forward) {
38239             // tabbed past last
38240             this.fireEvent.defer(100, this, ['tabend',this]);
38241         }
38242     }
38243 });/*
38244  * Based on:
38245  * Ext JS Library 1.1.1
38246  * Copyright(c) 2006-2007, Ext JS, LLC.
38247  *
38248  * Originally Released Under LGPL - original licence link has changed is not relivant.
38249  *
38250  * Fork - LGPL
38251  * <script type="text/javascript">
38252  */
38253  
38254 /**
38255  * @class Roo.grid.EditorGrid
38256  * @extends Roo.grid.Grid
38257  * Class for creating and editable grid.
38258  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38259  * The container MUST have some type of size defined for the grid to fill. The container will be 
38260  * automatically set to position relative if it isn't already.
38261  * @param {Object} dataSource The data model to bind to
38262  * @param {Object} colModel The column model with info about this grid's columns
38263  */
38264 Roo.grid.EditorGrid = function(container, config){
38265     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38266     this.getGridEl().addClass("xedit-grid");
38267
38268     if(!this.selModel){
38269         this.selModel = new Roo.grid.CellSelectionModel();
38270     }
38271
38272     this.activeEditor = null;
38273
38274         this.addEvents({
38275             /**
38276              * @event beforeedit
38277              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38278              * <ul style="padding:5px;padding-left:16px;">
38279              * <li>grid - This grid</li>
38280              * <li>record - The record being edited</li>
38281              * <li>field - The field name being edited</li>
38282              * <li>value - The value for the field being edited.</li>
38283              * <li>row - The grid row index</li>
38284              * <li>column - The grid column index</li>
38285              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38286              * </ul>
38287              * @param {Object} e An edit event (see above for description)
38288              */
38289             "beforeedit" : true,
38290             /**
38291              * @event afteredit
38292              * Fires after a cell is edited. <br />
38293              * <ul style="padding:5px;padding-left:16px;">
38294              * <li>grid - This grid</li>
38295              * <li>record - The record being edited</li>
38296              * <li>field - The field name being edited</li>
38297              * <li>value - The value being set</li>
38298              * <li>originalValue - The original value for the field, before the edit.</li>
38299              * <li>row - The grid row index</li>
38300              * <li>column - The grid column index</li>
38301              * </ul>
38302              * @param {Object} e An edit event (see above for description)
38303              */
38304             "afteredit" : true,
38305             /**
38306              * @event validateedit
38307              * Fires after a cell is edited, but before the value is set in the record. 
38308          * You can use this to modify the value being set in the field, Return false
38309              * to cancel the change. The edit event object has the following properties <br />
38310              * <ul style="padding:5px;padding-left:16px;">
38311          * <li>editor - This editor</li>
38312              * <li>grid - This grid</li>
38313              * <li>record - The record being edited</li>
38314              * <li>field - The field name being edited</li>
38315              * <li>value - The value being set</li>
38316              * <li>originalValue - The original value for the field, before the edit.</li>
38317              * <li>row - The grid row index</li>
38318              * <li>column - The grid column index</li>
38319              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38320              * </ul>
38321              * @param {Object} e An edit event (see above for description)
38322              */
38323             "validateedit" : true
38324         });
38325     this.on("bodyscroll", this.stopEditing,  this);
38326     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38327 };
38328
38329 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38330     /**
38331      * @cfg {Number} clicksToEdit
38332      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38333      */
38334     clicksToEdit: 2,
38335
38336     // private
38337     isEditor : true,
38338     // private
38339     trackMouseOver: false, // causes very odd FF errors
38340
38341     onCellDblClick : function(g, row, col){
38342         this.startEditing(row, col);
38343     },
38344
38345     onEditComplete : function(ed, value, startValue){
38346         this.editing = false;
38347         this.activeEditor = null;
38348         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38349         var r = ed.record;
38350         var field = this.colModel.getDataIndex(ed.col);
38351         var e = {
38352             grid: this,
38353             record: r,
38354             field: field,
38355             originalValue: startValue,
38356             value: value,
38357             row: ed.row,
38358             column: ed.col,
38359             cancel:false,
38360             editor: ed
38361         };
38362         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
38363         cell.show();
38364           
38365         if(String(value) !== String(startValue)){
38366             
38367             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38368                 r.set(field, e.value);
38369                 // if we are dealing with a combo box..
38370                 // then we also set the 'name' colum to be the displayField
38371                 if (ed.field.displayField && ed.field.name) {
38372                     r.set(ed.field.name, ed.field.el.dom.value);
38373                 }
38374                 
38375                 delete e.cancel; //?? why!!!
38376                 this.fireEvent("afteredit", e);
38377             }
38378         } else {
38379             this.fireEvent("afteredit", e); // always fire it!
38380         }
38381         this.view.focusCell(ed.row, ed.col);
38382     },
38383
38384     /**
38385      * Starts editing the specified for the specified row/column
38386      * @param {Number} rowIndex
38387      * @param {Number} colIndex
38388      */
38389     startEditing : function(row, col){
38390         this.stopEditing();
38391         if(this.colModel.isCellEditable(col, row)){
38392             this.view.ensureVisible(row, col, true);
38393           
38394             var r = this.dataSource.getAt(row);
38395             var field = this.colModel.getDataIndex(col);
38396             var cell = Roo.get(this.view.getCell(row,col));
38397             var e = {
38398                 grid: this,
38399                 record: r,
38400                 field: field,
38401                 value: r.data[field],
38402                 row: row,
38403                 column: col,
38404                 cancel:false 
38405             };
38406             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38407                 this.editing = true;
38408                 var ed = this.colModel.getCellEditor(col, row);
38409                 
38410                 if (!ed) {
38411                     return;
38412                 }
38413                 if(!ed.rendered){
38414                     ed.render(ed.parentEl || document.body);
38415                 }
38416                 ed.field.reset();
38417                
38418                 cell.hide();
38419                 
38420                 (function(){ // complex but required for focus issues in safari, ie and opera
38421                     ed.row = row;
38422                     ed.col = col;
38423                     ed.record = r;
38424                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38425                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
38426                     this.activeEditor = ed;
38427                     var v = r.data[field];
38428                     ed.startEdit(this.view.getCell(row, col), v);
38429                     // combo's with 'displayField and name set
38430                     if (ed.field.displayField && ed.field.name) {
38431                         ed.field.el.dom.value = r.data[ed.field.name];
38432                     }
38433                     
38434                     
38435                 }).defer(50, this);
38436             }
38437         }
38438     },
38439         
38440     /**
38441      * Stops any active editing
38442      */
38443     stopEditing : function(){
38444         if(this.activeEditor){
38445             this.activeEditor.completeEdit();
38446         }
38447         this.activeEditor = null;
38448     },
38449         
38450          /**
38451      * Called to get grid's drag proxy text, by default returns this.ddText.
38452      * @return {String}
38453      */
38454     getDragDropText : function(){
38455         var count = this.selModel.getSelectedCell() ? 1 : 0;
38456         return String.format(this.ddText, count, count == 1 ? '' : 's');
38457     }
38458         
38459 });/*
38460  * Based on:
38461  * Ext JS Library 1.1.1
38462  * Copyright(c) 2006-2007, Ext JS, LLC.
38463  *
38464  * Originally Released Under LGPL - original licence link has changed is not relivant.
38465  *
38466  * Fork - LGPL
38467  * <script type="text/javascript">
38468  */
38469
38470 // private - not really -- you end up using it !
38471 // This is a support class used internally by the Grid components
38472
38473 /**
38474  * @class Roo.grid.GridEditor
38475  * @extends Roo.Editor
38476  * Class for creating and editable grid elements.
38477  * @param {Object} config any settings (must include field)
38478  */
38479 Roo.grid.GridEditor = function(field, config){
38480     if (!config && field.field) {
38481         config = field;
38482         field = Roo.factory(config.field, Roo.form);
38483     }
38484     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
38485     field.monitorTab = false;
38486 };
38487
38488 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
38489     
38490     /**
38491      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
38492      */
38493     
38494     alignment: "tl-tl",
38495     autoSize: "width",
38496     hideEl : false,
38497     cls: "x-small-editor x-grid-editor",
38498     shim:false,
38499     shadow:"frame"
38500 });/*
38501  * Based on:
38502  * Ext JS Library 1.1.1
38503  * Copyright(c) 2006-2007, Ext JS, LLC.
38504  *
38505  * Originally Released Under LGPL - original licence link has changed is not relivant.
38506  *
38507  * Fork - LGPL
38508  * <script type="text/javascript">
38509  */
38510   
38511
38512   
38513 Roo.grid.PropertyRecord = Roo.data.Record.create([
38514     {name:'name',type:'string'},  'value'
38515 ]);
38516
38517
38518 Roo.grid.PropertyStore = function(grid, source){
38519     this.grid = grid;
38520     this.store = new Roo.data.Store({
38521         recordType : Roo.grid.PropertyRecord
38522     });
38523     this.store.on('update', this.onUpdate,  this);
38524     if(source){
38525         this.setSource(source);
38526     }
38527     Roo.grid.PropertyStore.superclass.constructor.call(this);
38528 };
38529
38530
38531
38532 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38533     setSource : function(o){
38534         this.source = o;
38535         this.store.removeAll();
38536         var data = [];
38537         for(var k in o){
38538             if(this.isEditableValue(o[k])){
38539                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38540             }
38541         }
38542         this.store.loadRecords({records: data}, {}, true);
38543     },
38544
38545     onUpdate : function(ds, record, type){
38546         if(type == Roo.data.Record.EDIT){
38547             var v = record.data['value'];
38548             var oldValue = record.modified['value'];
38549             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38550                 this.source[record.id] = v;
38551                 record.commit();
38552                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38553             }else{
38554                 record.reject();
38555             }
38556         }
38557     },
38558
38559     getProperty : function(row){
38560        return this.store.getAt(row);
38561     },
38562
38563     isEditableValue: function(val){
38564         if(val && val instanceof Date){
38565             return true;
38566         }else if(typeof val == 'object' || typeof val == 'function'){
38567             return false;
38568         }
38569         return true;
38570     },
38571
38572     setValue : function(prop, value){
38573         this.source[prop] = value;
38574         this.store.getById(prop).set('value', value);
38575     },
38576
38577     getSource : function(){
38578         return this.source;
38579     }
38580 });
38581
38582 Roo.grid.PropertyColumnModel = function(grid, store){
38583     this.grid = grid;
38584     var g = Roo.grid;
38585     g.PropertyColumnModel.superclass.constructor.call(this, [
38586         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38587         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38588     ]);
38589     this.store = store;
38590     this.bselect = Roo.DomHelper.append(document.body, {
38591         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38592             {tag: 'option', value: 'true', html: 'true'},
38593             {tag: 'option', value: 'false', html: 'false'}
38594         ]
38595     });
38596     Roo.id(this.bselect);
38597     var f = Roo.form;
38598     this.editors = {
38599         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38600         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38601         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38602         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38603         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38604     };
38605     this.renderCellDelegate = this.renderCell.createDelegate(this);
38606     this.renderPropDelegate = this.renderProp.createDelegate(this);
38607 };
38608
38609 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38610     
38611     
38612     nameText : 'Name',
38613     valueText : 'Value',
38614     
38615     dateFormat : 'm/j/Y',
38616     
38617     
38618     renderDate : function(dateVal){
38619         return dateVal.dateFormat(this.dateFormat);
38620     },
38621
38622     renderBool : function(bVal){
38623         return bVal ? 'true' : 'false';
38624     },
38625
38626     isCellEditable : function(colIndex, rowIndex){
38627         return colIndex == 1;
38628     },
38629
38630     getRenderer : function(col){
38631         return col == 1 ?
38632             this.renderCellDelegate : this.renderPropDelegate;
38633     },
38634
38635     renderProp : function(v){
38636         return this.getPropertyName(v);
38637     },
38638
38639     renderCell : function(val){
38640         var rv = val;
38641         if(val instanceof Date){
38642             rv = this.renderDate(val);
38643         }else if(typeof val == 'boolean'){
38644             rv = this.renderBool(val);
38645         }
38646         return Roo.util.Format.htmlEncode(rv);
38647     },
38648
38649     getPropertyName : function(name){
38650         var pn = this.grid.propertyNames;
38651         return pn && pn[name] ? pn[name] : name;
38652     },
38653
38654     getCellEditor : function(colIndex, rowIndex){
38655         var p = this.store.getProperty(rowIndex);
38656         var n = p.data['name'], val = p.data['value'];
38657         
38658         if(typeof(this.grid.customEditors[n]) == 'string'){
38659             return this.editors[this.grid.customEditors[n]];
38660         }
38661         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38662             return this.grid.customEditors[n];
38663         }
38664         if(val instanceof Date){
38665             return this.editors['date'];
38666         }else if(typeof val == 'number'){
38667             return this.editors['number'];
38668         }else if(typeof val == 'boolean'){
38669             return this.editors['boolean'];
38670         }else{
38671             return this.editors['string'];
38672         }
38673     }
38674 });
38675
38676 /**
38677  * @class Roo.grid.PropertyGrid
38678  * @extends Roo.grid.EditorGrid
38679  * This class represents the  interface of a component based property grid control.
38680  * <br><br>Usage:<pre><code>
38681  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38682       
38683  });
38684  // set any options
38685  grid.render();
38686  * </code></pre>
38687   
38688  * @constructor
38689  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38690  * The container MUST have some type of size defined for the grid to fill. The container will be
38691  * automatically set to position relative if it isn't already.
38692  * @param {Object} config A config object that sets properties on this grid.
38693  */
38694 Roo.grid.PropertyGrid = function(container, config){
38695     config = config || {};
38696     var store = new Roo.grid.PropertyStore(this);
38697     this.store = store;
38698     var cm = new Roo.grid.PropertyColumnModel(this, store);
38699     store.store.sort('name', 'ASC');
38700     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38701         ds: store.store,
38702         cm: cm,
38703         enableColLock:false,
38704         enableColumnMove:false,
38705         stripeRows:false,
38706         trackMouseOver: false,
38707         clicksToEdit:1
38708     }, config));
38709     this.getGridEl().addClass('x-props-grid');
38710     this.lastEditRow = null;
38711     this.on('columnresize', this.onColumnResize, this);
38712     this.addEvents({
38713          /**
38714              * @event beforepropertychange
38715              * Fires before a property changes (return false to stop?)
38716              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38717              * @param {String} id Record Id
38718              * @param {String} newval New Value
38719          * @param {String} oldval Old Value
38720              */
38721         "beforepropertychange": true,
38722         /**
38723              * @event propertychange
38724              * Fires after a property changes
38725              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38726              * @param {String} id Record Id
38727              * @param {String} newval New Value
38728          * @param {String} oldval Old Value
38729              */
38730         "propertychange": true
38731     });
38732     this.customEditors = this.customEditors || {};
38733 };
38734 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38735     
38736      /**
38737      * @cfg {Object} customEditors map of colnames=> custom editors.
38738      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38739      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38740      * false disables editing of the field.
38741          */
38742     
38743       /**
38744      * @cfg {Object} propertyNames map of property Names to their displayed value
38745          */
38746     
38747     render : function(){
38748         Roo.grid.PropertyGrid.superclass.render.call(this);
38749         this.autoSize.defer(100, this);
38750     },
38751
38752     autoSize : function(){
38753         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38754         if(this.view){
38755             this.view.fitColumns();
38756         }
38757     },
38758
38759     onColumnResize : function(){
38760         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38761         this.autoSize();
38762     },
38763     /**
38764      * Sets the data for the Grid
38765      * accepts a Key => Value object of all the elements avaiable.
38766      * @param {Object} data  to appear in grid.
38767      */
38768     setSource : function(source){
38769         this.store.setSource(source);
38770         //this.autoSize();
38771     },
38772     /**
38773      * Gets all the data from the grid.
38774      * @return {Object} data  data stored in grid
38775      */
38776     getSource : function(){
38777         return this.store.getSource();
38778     }
38779 });/*
38780  * Based on:
38781  * Ext JS Library 1.1.1
38782  * Copyright(c) 2006-2007, Ext JS, LLC.
38783  *
38784  * Originally Released Under LGPL - original licence link has changed is not relivant.
38785  *
38786  * Fork - LGPL
38787  * <script type="text/javascript">
38788  */
38789  
38790 /**
38791  * @class Roo.LoadMask
38792  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38793  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38794  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38795  * element's UpdateManager load indicator and will be destroyed after the initial load.
38796  * @constructor
38797  * Create a new LoadMask
38798  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38799  * @param {Object} config The config object
38800  */
38801 Roo.LoadMask = function(el, config){
38802     this.el = Roo.get(el);
38803     Roo.apply(this, config);
38804     if(this.store){
38805         this.store.on('beforeload', this.onBeforeLoad, this);
38806         this.store.on('load', this.onLoad, this);
38807         this.store.on('loadexception', this.onLoadException, this);
38808         this.removeMask = false;
38809     }else{
38810         var um = this.el.getUpdateManager();
38811         um.showLoadIndicator = false; // disable the default indicator
38812         um.on('beforeupdate', this.onBeforeLoad, this);
38813         um.on('update', this.onLoad, this);
38814         um.on('failure', this.onLoad, this);
38815         this.removeMask = true;
38816     }
38817 };
38818
38819 Roo.LoadMask.prototype = {
38820     /**
38821      * @cfg {Boolean} removeMask
38822      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38823      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38824      */
38825     /**
38826      * @cfg {String} msg
38827      * The text to display in a centered loading message box (defaults to 'Loading...')
38828      */
38829     msg : 'Loading...',
38830     /**
38831      * @cfg {String} msgCls
38832      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38833      */
38834     msgCls : 'x-mask-loading',
38835
38836     /**
38837      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38838      * @type Boolean
38839      */
38840     disabled: false,
38841
38842     /**
38843      * Disables the mask to prevent it from being displayed
38844      */
38845     disable : function(){
38846        this.disabled = true;
38847     },
38848
38849     /**
38850      * Enables the mask so that it can be displayed
38851      */
38852     enable : function(){
38853         this.disabled = false;
38854     },
38855     
38856     onLoadException : function()
38857     {
38858         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38859             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38860         }
38861         this.el.unmask(this.removeMask);
38862     },
38863     // private
38864     onLoad : function()
38865     {
38866         this.el.unmask(this.removeMask);
38867     },
38868
38869     // private
38870     onBeforeLoad : function(){
38871         if(!this.disabled){
38872             this.el.mask(this.msg, this.msgCls);
38873         }
38874     },
38875
38876     // private
38877     destroy : function(){
38878         if(this.store){
38879             this.store.un('beforeload', this.onBeforeLoad, this);
38880             this.store.un('load', this.onLoad, this);
38881             this.store.un('loadexception', this.onLoadException, this);
38882         }else{
38883             var um = this.el.getUpdateManager();
38884             um.un('beforeupdate', this.onBeforeLoad, this);
38885             um.un('update', this.onLoad, this);
38886             um.un('failure', this.onLoad, this);
38887         }
38888     }
38889 };/*
38890  * Based on:
38891  * Ext JS Library 1.1.1
38892  * Copyright(c) 2006-2007, Ext JS, LLC.
38893  *
38894  * Originally Released Under LGPL - original licence link has changed is not relivant.
38895  *
38896  * Fork - LGPL
38897  * <script type="text/javascript">
38898  */
38899
38900
38901 /**
38902  * @class Roo.XTemplate
38903  * @extends Roo.Template
38904  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38905 <pre><code>
38906 var t = new Roo.XTemplate(
38907         '&lt;select name="{name}"&gt;',
38908                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38909         '&lt;/select&gt;'
38910 );
38911  
38912 // then append, applying the master template values
38913  </code></pre>
38914  *
38915  * Supported features:
38916  *
38917  *  Tags:
38918
38919 <pre><code>
38920       {a_variable} - output encoded.
38921       {a_variable.format:("Y-m-d")} - call a method on the variable
38922       {a_variable:raw} - unencoded output
38923       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38924       {a_variable:this.method_on_template(...)} - call a method on the template object.
38925  
38926 </code></pre>
38927  *  The tpl tag:
38928 <pre><code>
38929         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38930         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38931         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38932         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38933   
38934         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38935         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38936 </code></pre>
38937  *      
38938  */
38939 Roo.XTemplate = function()
38940 {
38941     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38942     if (this.html) {
38943         this.compile();
38944     }
38945 };
38946
38947
38948 Roo.extend(Roo.XTemplate, Roo.Template, {
38949
38950     /**
38951      * The various sub templates
38952      */
38953     tpls : false,
38954     /**
38955      *
38956      * basic tag replacing syntax
38957      * WORD:WORD()
38958      *
38959      * // you can fake an object call by doing this
38960      *  x.t:(test,tesT) 
38961      * 
38962      */
38963     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38964
38965     /**
38966      * compile the template
38967      *
38968      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38969      *
38970      */
38971     compile: function()
38972     {
38973         var s = this.html;
38974      
38975         s = ['<tpl>', s, '</tpl>'].join('');
38976     
38977         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38978             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38979             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38980             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38981             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38982             m,
38983             id     = 0,
38984             tpls   = [];
38985     
38986         while(true == !!(m = s.match(re))){
38987             var forMatch   = m[0].match(nameRe),
38988                 ifMatch   = m[0].match(ifRe),
38989                 execMatch   = m[0].match(execRe),
38990                 namedMatch   = m[0].match(namedRe),
38991                 
38992                 exp  = null, 
38993                 fn   = null,
38994                 exec = null,
38995                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38996                 
38997             if (ifMatch) {
38998                 // if - puts fn into test..
38999                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
39000                 if(exp){
39001                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
39002                 }
39003             }
39004             
39005             if (execMatch) {
39006                 // exec - calls a function... returns empty if true is  returned.
39007                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
39008                 if(exp){
39009                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
39010                 }
39011             }
39012             
39013             
39014             if (name) {
39015                 // for = 
39016                 switch(name){
39017                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
39018                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
39019                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
39020                 }
39021             }
39022             var uid = namedMatch ? namedMatch[1] : id;
39023             
39024             
39025             tpls.push({
39026                 id:     namedMatch ? namedMatch[1] : id,
39027                 target: name,
39028                 exec:   exec,
39029                 test:   fn,
39030                 body:   m[1] || ''
39031             });
39032             if (namedMatch) {
39033                 s = s.replace(m[0], '');
39034             } else { 
39035                 s = s.replace(m[0], '{xtpl'+ id + '}');
39036             }
39037             ++id;
39038         }
39039         this.tpls = [];
39040         for(var i = tpls.length-1; i >= 0; --i){
39041             this.compileTpl(tpls[i]);
39042             this.tpls[tpls[i].id] = tpls[i];
39043         }
39044         this.master = tpls[tpls.length-1];
39045         return this;
39046     },
39047     /**
39048      * same as applyTemplate, except it's done to one of the subTemplates
39049      * when using named templates, you can do:
39050      *
39051      * var str = pl.applySubTemplate('your-name', values);
39052      *
39053      * 
39054      * @param {Number} id of the template
39055      * @param {Object} values to apply to template
39056      * @param {Object} parent (normaly the instance of this object)
39057      */
39058     applySubTemplate : function(id, values, parent)
39059     {
39060         
39061         
39062         var t = this.tpls[id];
39063         
39064         
39065         try { 
39066             if(t.test && !t.test.call(this, values, parent)){
39067                 return '';
39068             }
39069         } catch(e) {
39070             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
39071             Roo.log(e.toString());
39072             Roo.log(t.test);
39073             return ''
39074         }
39075         try { 
39076             
39077             if(t.exec && t.exec.call(this, values, parent)){
39078                 return '';
39079             }
39080         } catch(e) {
39081             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
39082             Roo.log(e.toString());
39083             Roo.log(t.exec);
39084             return ''
39085         }
39086         try {
39087             var vs = t.target ? t.target.call(this, values, parent) : values;
39088             parent = t.target ? values : parent;
39089             if(t.target && vs instanceof Array){
39090                 var buf = [];
39091                 for(var i = 0, len = vs.length; i < len; i++){
39092                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
39093                 }
39094                 return buf.join('');
39095             }
39096             return t.compiled.call(this, vs, parent);
39097         } catch (e) {
39098             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
39099             Roo.log(e.toString());
39100             Roo.log(t.compiled);
39101             return '';
39102         }
39103     },
39104
39105     compileTpl : function(tpl)
39106     {
39107         var fm = Roo.util.Format;
39108         var useF = this.disableFormats !== true;
39109         var sep = Roo.isGecko ? "+" : ",";
39110         var undef = function(str) {
39111             Roo.log("Property not found :"  + str);
39112             return '';
39113         };
39114         
39115         var fn = function(m, name, format, args)
39116         {
39117             //Roo.log(arguments);
39118             args = args ? args.replace(/\\'/g,"'") : args;
39119             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
39120             if (typeof(format) == 'undefined') {
39121                 format= 'htmlEncode';
39122             }
39123             if (format == 'raw' ) {
39124                 format = false;
39125             }
39126             
39127             if(name.substr(0, 4) == 'xtpl'){
39128                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
39129             }
39130             
39131             // build an array of options to determine if value is undefined..
39132             
39133             // basically get 'xxxx.yyyy' then do
39134             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
39135             //    (function () { Roo.log("Property not found"); return ''; })() :
39136             //    ......
39137             
39138             var udef_ar = [];
39139             var lookfor = '';
39140             Roo.each(name.split('.'), function(st) {
39141                 lookfor += (lookfor.length ? '.': '') + st;
39142                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
39143             });
39144             
39145             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
39146             
39147             
39148             if(format && useF){
39149                 
39150                 args = args ? ',' + args : "";
39151                  
39152                 if(format.substr(0, 5) != "this."){
39153                     format = "fm." + format + '(';
39154                 }else{
39155                     format = 'this.call("'+ format.substr(5) + '", ';
39156                     args = ", values";
39157                 }
39158                 
39159                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
39160             }
39161              
39162             if (args.length) {
39163                 // called with xxyx.yuu:(test,test)
39164                 // change to ()
39165                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
39166             }
39167             // raw.. - :raw modifier..
39168             return "'"+ sep + udef_st  + name + ")"+sep+"'";
39169             
39170         };
39171         var body;
39172         // branched to use + in gecko and [].join() in others
39173         if(Roo.isGecko){
39174             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
39175                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
39176                     "';};};";
39177         }else{
39178             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
39179             body.push(tpl.body.replace(/(\r\n|\n)/g,
39180                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
39181             body.push("'].join('');};};");
39182             body = body.join('');
39183         }
39184         
39185         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
39186        
39187         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
39188         eval(body);
39189         
39190         return this;
39191     },
39192
39193     applyTemplate : function(values){
39194         return this.master.compiled.call(this, values, {});
39195         //var s = this.subs;
39196     },
39197
39198     apply : function(){
39199         return this.applyTemplate.apply(this, arguments);
39200     }
39201
39202  });
39203
39204 Roo.XTemplate.from = function(el){
39205     el = Roo.getDom(el);
39206     return new Roo.XTemplate(el.value || el.innerHTML);
39207 };/*
39208  * Original code for Roojs - LGPL
39209  * <script type="text/javascript">
39210  */
39211  
39212 /**
39213  * @class Roo.XComponent
39214  * A delayed Element creator...
39215  * Or a way to group chunks of interface together.
39216  * 
39217  * Mypart.xyx = new Roo.XComponent({
39218
39219     parent : 'Mypart.xyz', // empty == document.element.!!
39220     order : '001',
39221     name : 'xxxx'
39222     region : 'xxxx'
39223     disabled : function() {} 
39224      
39225     tree : function() { // return an tree of xtype declared components
39226         var MODULE = this;
39227         return 
39228         {
39229             xtype : 'NestedLayoutPanel',
39230             // technicall
39231         }
39232      ]
39233  *})
39234  *
39235  *
39236  * It can be used to build a big heiracy, with parent etc.
39237  * or you can just use this to render a single compoent to a dom element
39238  * MYPART.render(Roo.Element | String(id) | dom_element )
39239  * 
39240  * @extends Roo.util.Observable
39241  * @constructor
39242  * @param cfg {Object} configuration of component
39243  * 
39244  */
39245 Roo.XComponent = function(cfg) {
39246     Roo.apply(this, cfg);
39247     this.addEvents({ 
39248         /**
39249              * @event built
39250              * Fires when this the componnt is built
39251              * @param {Roo.XComponent} c the component
39252              */
39253         'built' : true
39254         
39255     });
39256     this.region = this.region || 'center'; // default..
39257     Roo.XComponent.register(this);
39258     this.modules = false;
39259     this.el = false; // where the layout goes..
39260     
39261     
39262 }
39263 Roo.extend(Roo.XComponent, Roo.util.Observable, {
39264     /**
39265      * @property el
39266      * The created element (with Roo.factory())
39267      * @type {Roo.Layout}
39268      */
39269     el  : false,
39270     
39271     /**
39272      * @property el
39273      * for BC  - use el in new code
39274      * @type {Roo.Layout}
39275      */
39276     panel : false,
39277     
39278     /**
39279      * @property layout
39280      * for BC  - use el in new code
39281      * @type {Roo.Layout}
39282      */
39283     layout : false,
39284     
39285      /**
39286      * @cfg {Function|boolean} disabled
39287      * If this module is disabled by some rule, return true from the funtion
39288      */
39289     disabled : false,
39290     
39291     /**
39292      * @cfg {String} parent 
39293      * Name of parent element which it get xtype added to..
39294      */
39295     parent: false,
39296     
39297     /**
39298      * @cfg {String} order
39299      * Used to set the order in which elements are created (usefull for multiple tabs)
39300      */
39301     
39302     order : false,
39303     /**
39304      * @cfg {String} name
39305      * String to display while loading.
39306      */
39307     name : false,
39308     /**
39309      * @cfg {String} region
39310      * Region to render component to (defaults to center)
39311      */
39312     region : 'center',
39313     
39314     /**
39315      * @cfg {Array} items
39316      * A single item array - the first element is the root of the tree..
39317      * It's done this way to stay compatible with the Xtype system...
39318      */
39319     items : false,
39320     
39321     /**
39322      * @property _tree
39323      * The method that retuns the tree of parts that make up this compoennt 
39324      * @type {function}
39325      */
39326     _tree  : false,
39327     
39328      /**
39329      * render
39330      * render element to dom or tree
39331      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
39332      */
39333     
39334     render : function(el)
39335     {
39336         
39337         el = el || false;
39338         var hp = this.parent ? 1 : 0;
39339         
39340         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
39341             // if parent is a '#.....' string, then let's use that..
39342             var ename = this.parent.substr(1)
39343             this.parent = false;
39344             el = Roo.get(ename);
39345             if (!el) {
39346                 Roo.log("Warning - element can not be found :#" + ename );
39347                 return;
39348             }
39349         }
39350         
39351         
39352         if (!this.parent) {
39353             
39354             el = el ? Roo.get(el) : false;      
39355             
39356             // it's a top level one..
39357             this.parent =  {
39358                 el : new Roo.BorderLayout(el || document.body, {
39359                 
39360                      center: {
39361                          titlebar: false,
39362                          autoScroll:false,
39363                          closeOnTab: true,
39364                          tabPosition: 'top',
39365                           //resizeTabs: true,
39366                          alwaysShowTabs: el && hp? false :  true,
39367                          hideTabs: el || !hp ? true :  false,
39368                          minTabWidth: 140
39369                      }
39370                  })
39371             }
39372         }
39373         
39374                 if (!this.parent.el) {
39375                         // probably an old style ctor, which has been disabled.
39376                         return;
39377                         
39378                 }
39379                 // The 'tree' method is  '_tree now' 
39380             
39381         var tree = this._tree ? this._tree() : this.tree();
39382         tree.region = tree.region || this.region;
39383         this.el = this.parent.el.addxtype(tree);
39384         this.fireEvent('built', this);
39385         
39386         this.panel = this.el;
39387         this.layout = this.panel.layout;
39388                 this.parentLayout = this.parent.layout  || false;  
39389          
39390     }
39391     
39392 });
39393
39394 Roo.apply(Roo.XComponent, {
39395     /**
39396      * @property  hideProgress
39397      * true to disable the building progress bar.. usefull on single page renders.
39398      * @type Boolean
39399      */
39400     hideProgress : false,
39401     /**
39402      * @property  buildCompleted
39403      * True when the builder has completed building the interface.
39404      * @type Boolean
39405      */
39406     buildCompleted : false,
39407      
39408     /**
39409      * @property  topModule
39410      * the upper most module - uses document.element as it's constructor.
39411      * @type Object
39412      */
39413      
39414     topModule  : false,
39415       
39416     /**
39417      * @property  modules
39418      * array of modules to be created by registration system.
39419      * @type {Array} of Roo.XComponent
39420      */
39421     
39422     modules : [],
39423     /**
39424      * @property  elmodules
39425      * array of modules to be created by which use #ID 
39426      * @type {Array} of Roo.XComponent
39427      */
39428      
39429     elmodules : [],
39430
39431     
39432     /**
39433      * Register components to be built later.
39434      *
39435      * This solves the following issues
39436      * - Building is not done on page load, but after an authentication process has occured.
39437      * - Interface elements are registered on page load
39438      * - Parent Interface elements may not be loaded before child, so this handles that..
39439      * 
39440      *
39441      * example:
39442      * 
39443      * MyApp.register({
39444           order : '000001',
39445           module : 'Pman.Tab.projectMgr',
39446           region : 'center',
39447           parent : 'Pman.layout',
39448           disabled : false,  // or use a function..
39449         })
39450      
39451      * * @param {Object} details about module
39452      */
39453     register : function(obj) {
39454                 
39455         Roo.XComponent.event.fireEvent('register', obj);
39456         switch(typeof(obj.disabled) ) {
39457                 
39458             case 'undefined':
39459                 break;
39460             
39461             case 'function':
39462                 if ( obj.disabled() ) {
39463                         return;
39464                 }
39465                 break;
39466             
39467             default:
39468                 if (obj.disabled) {
39469                         return;
39470                 }
39471                 break;
39472         }
39473                 
39474         this.modules.push(obj);
39475          
39476     },
39477     /**
39478      * convert a string to an object..
39479      * eg. 'AAA.BBB' -> finds AAA.BBB
39480
39481      */
39482     
39483     toObject : function(str)
39484     {
39485         if (!str || typeof(str) == 'object') {
39486             return str;
39487         }
39488         if (str.substring(0,1) == '#') {
39489             return str;
39490         }
39491
39492         var ar = str.split('.');
39493         var rt, o;
39494         rt = ar.shift();
39495             /** eval:var:o */
39496         try {
39497             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
39498         } catch (e) {
39499             throw "Module not found : " + str;
39500         }
39501         
39502         if (o === false) {
39503             throw "Module not found : " + str;
39504         }
39505         Roo.each(ar, function(e) {
39506             if (typeof(o[e]) == 'undefined') {
39507                 throw "Module not found : " + str;
39508             }
39509             o = o[e];
39510         });
39511         
39512         return o;
39513         
39514     },
39515     
39516     
39517     /**
39518      * move modules into their correct place in the tree..
39519      * 
39520      */
39521     preBuild : function ()
39522     {
39523         var _t = this;
39524         Roo.each(this.modules , function (obj)
39525         {
39526             Roo.XComponent.event.fireEvent('beforebuild', obj);
39527             
39528             var opar = obj.parent;
39529             try { 
39530                 obj.parent = this.toObject(opar);
39531             } catch(e) {
39532                 Roo.log("parent:toObject failed: " + e.toString());
39533                 return;
39534             }
39535             
39536             if (!obj.parent) {
39537                 Roo.debug && Roo.log("GOT top level module");
39538                 Roo.debug && Roo.log(obj);
39539                 obj.modules = new Roo.util.MixedCollection(false, 
39540                     function(o) { return o.order + '' }
39541                 );
39542                 this.topModule = obj;
39543                 return;
39544             }
39545                         // parent is a string (usually a dom element name..)
39546             if (typeof(obj.parent) == 'string') {
39547                 this.elmodules.push(obj);
39548                 return;
39549             }
39550             if (obj.parent.constructor != Roo.XComponent) {
39551                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
39552             }
39553             if (!obj.parent.modules) {
39554                 obj.parent.modules = new Roo.util.MixedCollection(false, 
39555                     function(o) { return o.order + '' }
39556                 );
39557             }
39558             if (obj.parent.disabled) {
39559                 obj.disabled = true;
39560             }
39561             obj.parent.modules.add(obj);
39562         }, this);
39563     },
39564     
39565      /**
39566      * make a list of modules to build.
39567      * @return {Array} list of modules. 
39568      */ 
39569     
39570     buildOrder : function()
39571     {
39572         var _this = this;
39573         var cmp = function(a,b) {   
39574             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
39575         };
39576         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
39577             throw "No top level modules to build";
39578         }
39579         
39580         // make a flat list in order of modules to build.
39581         var mods = this.topModule ? [ this.topModule ] : [];
39582                 
39583         // elmodules (is a list of DOM based modules )
39584         Roo.each(this.elmodules, function(e) {
39585             mods.push(e)
39586         });
39587
39588         
39589         // add modules to their parents..
39590         var addMod = function(m) {
39591             Roo.debug && Roo.log("build Order: add: " + m.name);
39592             
39593         mods.push(m);
39594         if (m.modules && !m.disabled) {
39595             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39596             m.modules.keySort('ASC',  cmp );
39597             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39598
39599             m.modules.each(addMod);
39600         } else {
39601             Roo.debug && Roo.log("build Order: no child modules");
39602             }
39603             // not sure if this is used any more..
39604             if (m.finalize) {
39605                 m.finalize.name = m.name + " (clean up) ";
39606                 mods.push(m.finalize);
39607             }
39608             
39609         }
39610         if (this.topModule) { 
39611             this.topModule.modules.keySort('ASC',  cmp );
39612             this.topModule.modules.each(addMod);
39613         }
39614         return mods;
39615     },
39616     
39617      /**
39618      * Build the registered modules.
39619      * @param {Object} parent element.
39620      * @param {Function} optional method to call after module has been added.
39621      * 
39622      */ 
39623    
39624     build : function() 
39625     {
39626         
39627         this.preBuild();
39628         var mods = this.buildOrder();
39629       
39630         //this.allmods = mods;
39631         //Roo.debug && Roo.log(mods);
39632         //return;
39633         if (!mods.length) { // should not happen
39634             throw "NO modules!!!";
39635         }
39636         
39637         
39638         var msg = "Building Interface...";
39639         // flash it up as modal - so we store the mask!?
39640         if (!this.hideProgress) {
39641             Roo.MessageBox.show({ title: 'loading' });
39642             Roo.MessageBox.show({
39643                title: "Please wait...",
39644                msg: msg,
39645                width:450,
39646                progress:true,
39647                closable:false,
39648                modal: false
39649               
39650             });
39651         }
39652         var total = mods.length;
39653         
39654         var _this = this;
39655         var progressRun = function() {
39656             if (!mods.length) {
39657                 Roo.debug && Roo.log('hide?');
39658                 if (!this.hideProgress) {
39659                     Roo.MessageBox.hide();
39660                 }
39661                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39662                 
39663                 // THE END...
39664                 return false;   
39665             }
39666             
39667             var m = mods.shift();
39668             
39669             
39670             Roo.debug && Roo.log(m);
39671             // not sure if this is supported any more.. - modules that are are just function
39672             if (typeof(m) == 'function') { 
39673                 m.call(this);
39674                 return progressRun.defer(10, _this);
39675             } 
39676             
39677             
39678             msg = "Building Interface " + (total  - mods.length) + 
39679                     " of " + total + 
39680                     (m.name ? (' - ' + m.name) : '');
39681                         Roo.debug && Roo.log(msg);
39682             if (!this.hideProgress) { 
39683                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39684             }
39685             
39686          
39687             // is the module disabled?
39688             var disabled = (typeof(m.disabled) == 'function') ?
39689                 m.disabled.call(m.module.disabled) : m.disabled;    
39690             
39691             
39692             if (disabled) {
39693                 return progressRun(); // we do not update the display!
39694             }
39695             
39696             // now build 
39697             
39698                         
39699                         
39700             m.render();
39701             // it's 10 on top level, and 1 on others??? why...
39702             return progressRun.defer(10, _this);
39703              
39704         }
39705         progressRun.defer(1, _this);
39706      
39707         
39708         
39709     },
39710         
39711         
39712         /**
39713          * Event Object.
39714          *
39715          *
39716          */
39717         event: false, 
39718     /**
39719          * wrapper for event.on - aliased later..  
39720          * Typically use to register a event handler for register:
39721          *
39722          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39723          *
39724          */
39725     on : false
39726    
39727     
39728     
39729 });
39730
39731 Roo.XComponent.event = new Roo.util.Observable({
39732                 events : { 
39733                         /**
39734                          * @event register
39735                          * Fires when an Component is registered,
39736                          * set the disable property on the Component to stop registration.
39737                          * @param {Roo.XComponent} c the component being registerd.
39738                          * 
39739                          */
39740                         'register' : true,
39741             /**
39742                          * @event beforebuild
39743                          * Fires before each Component is built
39744                          * can be used to apply permissions.
39745                          * @param {Roo.XComponent} c the component being registerd.
39746                          * 
39747                          */
39748                         'beforebuild' : true,
39749                         /**
39750                          * @event buildcomplete
39751                          * Fires on the top level element when all elements have been built
39752                          * @param {Roo.XComponent} the top level component.
39753                          */
39754                         'buildcomplete' : true
39755                         
39756                 }
39757 });
39758
39759 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39760  //<script type="text/javascript">
39761
39762
39763 /**
39764  * @class Roo.Login
39765  * @extends Roo.LayoutDialog
39766  * A generic Login Dialog..... - only one needed in theory!?!?
39767  *
39768  * Fires XComponent builder on success...
39769  * 
39770  * Sends 
39771  *    username,password, lang = for login actions.
39772  *    check = 1 for periodic checking that sesion is valid.
39773  *    passwordRequest = email request password
39774  *    logout = 1 = to logout
39775  * 
39776  * Affects: (this id="????" elements)
39777  *   loading  (removed) (used to indicate application is loading)
39778  *   loading-mask (hides) (used to hide application when it's building loading)
39779  *   
39780  * 
39781  * Usage: 
39782  *    
39783  * 
39784  * Myapp.login = Roo.Login({
39785      url: xxxx,
39786    
39787      realm : 'Myapp', 
39788      
39789      
39790      method : 'POST',
39791      
39792      
39793      * 
39794  })
39795  * 
39796  * 
39797  * 
39798  **/
39799  
39800 Roo.Login = function(cfg)
39801 {
39802     this.addEvents({
39803         'refreshed' : true
39804     });
39805     
39806     Roo.apply(this,cfg);
39807     
39808     Roo.onReady(function() {
39809         this.onLoad();
39810     }, this);
39811     // call parent..
39812     
39813    
39814     Roo.Login.superclass.constructor.call(this, this);
39815     //this.addxtype(this.items[0]);
39816     
39817     
39818 }
39819
39820
39821 Roo.extend(Roo.Login, Roo.LayoutDialog, {
39822     
39823     /**
39824      * @cfg {String} method
39825      * Method used to query for login details.
39826      */
39827     
39828     method : 'POST',
39829     /**
39830      * @cfg {String} url
39831      * URL to query login data. - eg. baseURL + '/Login.php'
39832      */
39833     url : '',
39834     
39835     /**
39836      * @property user
39837      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
39838      * @type {Object} 
39839      */
39840     user : false,
39841     /**
39842      * @property checkFails
39843      * Number of times we have attempted to get authentication check, and failed.
39844      * @type {Number} 
39845      */
39846     checkFails : 0,
39847       /**
39848      * @property intervalID
39849      * The window interval that does the constant login checking.
39850      * @type {Number} 
39851      */
39852     intervalID : 0,
39853     
39854     
39855     onLoad : function() // called on page load...
39856     {
39857         // load 
39858          
39859         if (Roo.get('loading')) { // clear any loading indicator..
39860             Roo.get('loading').remove();
39861         }
39862         
39863         //this.switchLang('en'); // set the language to english..
39864        
39865         this.check({
39866             success:  function(response, opts)  {  // check successfull...
39867             
39868                 var res = this.processResponse(response);
39869                 this.checkFails =0;
39870                 if (!res.success) { // error!
39871                     this.checkFails = 5;
39872                     //console.log('call failure');
39873                     return this.failure(response,opts);
39874                 }
39875                 
39876                 if (!res.data.id) { // id=0 == login failure.
39877                     return this.show();
39878                 }
39879                 
39880                               
39881                         //console.log(success);
39882                 this.fillAuth(res.data);   
39883                 this.checkFails =0;
39884                 Roo.XComponent.build();
39885             },
39886             failure : this.show
39887         });
39888         
39889     }, 
39890     
39891     
39892     check: function(cfg) // called every so often to refresh cookie etc..
39893     {
39894         if (cfg.again) { // could be undefined..
39895             this.checkFails++;
39896         } else {
39897             this.checkFails = 0;
39898         }
39899         var _this = this;
39900         if (this.sending) {
39901             if ( this.checkFails > 4) {
39902                 Roo.MessageBox.alert("Error",  
39903                     "Error getting authentication status. - try reloading, or wait a while", function() {
39904                         _this.sending = false;
39905                     }); 
39906                 return;
39907             }
39908             cfg.again = true;
39909             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
39910             return;
39911         }
39912         this.sending = true;
39913         
39914         Roo.Ajax.request({  
39915             url: this.url,
39916             params: {
39917                 getAuthUser: true
39918             },  
39919             method: this.method,
39920             success:  cfg.success || this.success,
39921             failure : cfg.failure || this.failure,
39922             scope : this,
39923             callCfg : cfg
39924               
39925         });  
39926     }, 
39927     
39928     
39929     logout: function()
39930     {
39931         window.onbeforeunload = function() { }; // false does not work for IE..
39932         this.user = false;
39933         var _this = this;
39934         
39935         Roo.Ajax.request({  
39936             url: this.url,
39937             params: {
39938                 logout: 1
39939             },  
39940             method: 'GET',
39941             failure : function() {
39942                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
39943                     document.location = document.location.toString() + '?ts=' + Math.random();
39944                 });
39945                 
39946             },
39947             success : function() {
39948                 _this.user = false;
39949                 this.checkFails =0;
39950                 // fixme..
39951                 document.location = document.location.toString() + '?ts=' + Math.random();
39952             }
39953               
39954               
39955         }); 
39956     },
39957     
39958     processResponse : function (response)
39959     {
39960         var res = '';
39961         try {
39962             res = Roo.decode(response.responseText);
39963             // oops...
39964             if (typeof(res) != 'object') {
39965                 res = { success : false, errorMsg : res, errors : true };
39966             }
39967             if (typeof(res.success) == 'undefined') {
39968                 res.success = false;
39969             }
39970             
39971         } catch(e) {
39972             res = { success : false,  errorMsg : response.responseText, errors : true };
39973         }
39974         return res;
39975     },
39976     
39977     success : function(response, opts)  // check successfull...
39978     {  
39979         this.sending = false;
39980         var res = this.processResponse(response);
39981         if (!res.success) {
39982             return this.failure(response, opts);
39983         }
39984         if (!res.data || !res.data.id) {
39985             return this.failure(response,opts);
39986         }
39987         //console.log(res);
39988         this.fillAuth(res.data);
39989         
39990         this.checkFails =0;
39991         
39992     },
39993     
39994     
39995     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
39996     {
39997         this.authUser = -1;
39998         this.sending = false;
39999         var res = this.processResponse(response);
40000         //console.log(res);
40001         if ( this.checkFails > 2) {
40002         
40003             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
40004                 "Error getting authentication status. - try reloading"); 
40005             return;
40006         }
40007         opts.callCfg.again = true;
40008         this.check.defer(1000, this, [ opts.callCfg ]);
40009         return;  
40010     },
40011     
40012     
40013     
40014     fillAuth: function(au) {
40015         this.startAuthCheck();
40016         this.authUserId = au.id;
40017         this.authUser = au;
40018         this.lastChecked = new Date();
40019         this.fireEvent('refreshed', au);
40020         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
40021         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
40022         au.lang = au.lang || 'en';
40023         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
40024         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
40025         this.switchLang(au.lang );
40026         
40027      
40028         // open system... - -on setyp..
40029         if (this.authUserId  < 0) {
40030             Roo.MessageBox.alert("Warning", 
40031                 "This is an open system - please set up a admin user with a password.");  
40032         }
40033          
40034         //Pman.onload(); // which should do nothing if it's a re-auth result...
40035         
40036              
40037     },
40038     
40039     startAuthCheck : function() // starter for timeout checking..
40040     {
40041         if (this.intervalID) { // timer already in place...
40042             return false;
40043         }
40044         var _this = this;
40045         this.intervalID =  window.setInterval(function() {
40046               _this.check(false);
40047             }, 120000); // every 120 secs = 2mins..
40048         
40049         
40050     },
40051          
40052     
40053     switchLang : function (lang) 
40054     {
40055         _T = typeof(_T) == 'undefined' ? false : _T;
40056           if (!_T || !lang.length) {
40057             return;
40058         }
40059         
40060         if (!_T && lang != 'en') {
40061             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
40062             return;
40063         }
40064         
40065         if (typeof(_T.en) == 'undefined') {
40066             _T.en = {};
40067             Roo.apply(_T.en, _T);
40068         }
40069         
40070         if (typeof(_T[lang]) == 'undefined') {
40071             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
40072             return;
40073         }
40074         
40075         
40076         Roo.apply(_T, _T[lang]);
40077         // just need to set the text values for everything...
40078         var _this = this;
40079         /* this will not work ...
40080         if (this.form) { 
40081             
40082                
40083             function formLabel(name, val) {
40084                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
40085             }
40086             
40087             formLabel('password', "Password"+':');
40088             formLabel('username', "Email Address"+':');
40089             formLabel('lang', "Language"+':');
40090             this.dialog.setTitle("Login");
40091             this.dialog.buttons[0].setText("Forgot Password");
40092             this.dialog.buttons[1].setText("Login");
40093         }
40094         */
40095         
40096         
40097     },
40098     
40099     
40100     title: "Login",
40101     modal: true,
40102     width:  350,
40103     //height: 230,
40104     height: 180,
40105     shadow: true,
40106     minWidth:200,
40107     minHeight:180,
40108     //proxyDrag: true,
40109     closable: false,
40110     draggable: false,
40111     collapsible: false,
40112     resizable: false,
40113     center: {  // needed??
40114         autoScroll:false,
40115         titlebar: false,
40116        // tabPosition: 'top',
40117         hideTabs: true,
40118         closeOnTab: true,
40119         alwaysShowTabs: false
40120     } ,
40121     listeners : {
40122         
40123         show  : function(dlg)
40124         {
40125             //console.log(this);
40126             this.form = this.layout.getRegion('center').activePanel.form;
40127             this.form.dialog = dlg;
40128             this.buttons[0].form = this.form;
40129             this.buttons[0].dialog = dlg;
40130             this.buttons[1].form = this.form;
40131             this.buttons[1].dialog = dlg;
40132            
40133            //this.resizeToLogo.defer(1000,this);
40134             // this is all related to resizing for logos..
40135             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
40136            //// if (!sz) {
40137              //   this.resizeToLogo.defer(1000,this);
40138              //   return;
40139            // }
40140             //var w = Ext.lib.Dom.getViewWidth() - 100;
40141             //var h = Ext.lib.Dom.getViewHeight() - 100;
40142             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
40143             //this.center();
40144             if (this.disabled) {
40145                 this.hide();
40146                 return;
40147             }
40148             
40149             if (this.user.id < 0) { // used for inital setup situations.
40150                 return;
40151             }
40152             
40153             if (this.intervalID) {
40154                 // remove the timer
40155                 window.clearInterval(this.intervalID);
40156                 this.intervalID = false;
40157             }
40158             
40159             
40160             if (Roo.get('loading')) {
40161                 Roo.get('loading').remove();
40162             }
40163             if (Roo.get('loading-mask')) {
40164                 Roo.get('loading-mask').hide();
40165             }
40166             
40167             //incomming._node = tnode;
40168             this.form.reset();
40169             //this.dialog.modal = !modal;
40170             //this.dialog.show();
40171             this.el.unmask(); 
40172             
40173             
40174             this.form.setValues({
40175                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
40176                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
40177             });
40178             
40179             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
40180             if (this.form.findField('username').getValue().length > 0 ){
40181                 this.form.findField('password').focus();
40182             } else {
40183                this.form.findField('username').focus();
40184             }
40185     
40186         }
40187     },
40188     items : [
40189          {
40190        
40191             xtype : 'ContentPanel',
40192             xns : Roo,
40193             region: 'center',
40194             fitToFrame : true,
40195             
40196             items : [
40197     
40198                 {
40199                
40200                     xtype : 'Form',
40201                     xns : Roo.form,
40202                     labelWidth: 100,
40203                     style : 'margin: 10px;',
40204                     
40205                     listeners : {
40206                         actionfailed : function(f, act) {
40207                             // form can return { errors: .... }
40208                                 
40209                             //act.result.errors // invalid form element list...
40210                             //act.result.errorMsg// invalid form element list...
40211                             
40212                             this.dialog.el.unmask();
40213                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
40214                                         "Login failed - communication error - try again.");
40215                                       
40216                         },
40217                         actioncomplete: function(re, act) {
40218                              
40219                             Roo.state.Manager.set(
40220                                 this.dialog.realm + '.username',  
40221                                     this.findField('username').getValue()
40222                             );
40223                             Roo.state.Manager.set(
40224                                 this.dialog.realm + '.lang',  
40225                                 this.findField('lang').getValue() 
40226                             );
40227                             
40228                             this.dialog.fillAuth(act.result.data);
40229                               
40230                             this.dialog.hide();
40231                             
40232                             if (Roo.get('loading-mask')) {
40233                                 Roo.get('loading-mask').show();
40234                             }
40235                             Roo.XComponent.build();
40236                             
40237                              
40238                             
40239                         }
40240                     },
40241                     items : [
40242                         {
40243                             xtype : 'TextField',
40244                             xns : Roo.form,
40245                             fieldLabel: "Email Address",
40246                             name: 'username',
40247                             width:200,
40248                             autoCreate : {tag: "input", type: "text", size: "20"}
40249                         },
40250                         {
40251                             xtype : 'TextField',
40252                             xns : Roo.form,
40253                             fieldLabel: "Password",
40254                             inputType: 'password',
40255                             name: 'password',
40256                             width:200,
40257                             autoCreate : {tag: "input", type: "text", size: "20"},
40258                             listeners : {
40259                                 specialkey : function(e,ev) {
40260                                     if (ev.keyCode == 13) {
40261                                         this.form.dialog.el.mask("Logging in");
40262                                         this.form.doAction('submit', {
40263                                             url: this.form.dialog.url,
40264                                             method: this.form.dialog.method
40265                                         });
40266                                     }
40267                                 }
40268                             }  
40269                         },
40270                         {
40271                             xtype : 'ComboBox',
40272                             xns : Roo.form,
40273                             fieldLabel: "Language",
40274                             name : 'langdisp',
40275                             store: {
40276                                 xtype : 'SimpleStore',
40277                                 fields: ['lang', 'ldisp'],
40278                                 data : [
40279                                     [ 'en', 'English' ],
40280                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
40281                                     [ 'zh_CN', '\u7C21\u4E2D' ]
40282                                 ]
40283                             },
40284                             
40285                             valueField : 'lang',
40286                             hiddenName:  'lang',
40287                             width: 200,
40288                             displayField:'ldisp',
40289                             typeAhead: false,
40290                             editable: false,
40291                             mode: 'local',
40292                             triggerAction: 'all',
40293                             emptyText:'Select a Language...',
40294                             selectOnFocus:true,
40295                             listeners : {
40296                                 select :  function(cb, rec, ix) {
40297                                     this.form.switchLang(rec.data.lang);
40298                                 }
40299                             }
40300                         
40301                         }
40302                     ]
40303                 }
40304                   
40305                 
40306             ]
40307         }
40308     ],
40309     buttons : [
40310         {
40311             xtype : 'Button',
40312             xns : 'Roo',
40313             text : "Forgot Password",
40314             listeners : {
40315                 click : function() {
40316                     //console.log(this);
40317                     var n = this.form.findField('username').getValue();
40318                     if (!n.length) {
40319                         Roo.MessageBox.alert("Error", "Fill in your email address");
40320                         return;
40321                     }
40322                     Roo.Ajax.request({
40323                         url: this.dialog.url,
40324                         params: {
40325                             passwordRequest: n
40326                         },
40327                         method: this.dialog.method,
40328                         success:  function(response, opts)  {  // check successfull...
40329                         
40330                             var res = this.dialog.processResponse(response);
40331                             if (!res.success) { // error!
40332                                Roo.MessageBox.alert("Error" ,
40333                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
40334                                return;
40335                             }
40336                             Roo.MessageBox.alert("Notice" ,
40337                                 "Please check you email for the Password Reset message");
40338                         },
40339                         failure : function() {
40340                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
40341                         }
40342                         
40343                     });
40344                 }
40345             }
40346         },
40347         {
40348             xtype : 'Button',
40349             xns : 'Roo',
40350             text : "Login",
40351             listeners : {
40352                 
40353                 click : function () {
40354                         
40355                     this.dialog.el.mask("Logging in");
40356                     this.form.doAction('submit', {
40357                             url: this.dialog.url,
40358                             method: this.dialog.method
40359                     });
40360                 }
40361             }
40362         }
40363     ]
40364   
40365   
40366 })
40367  
40368
40369
40370