roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
3949          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3950          * @param {Event} e The event
3951          * @param {Object} data An object containing arbitrary data supplied by the drag source
3952          */
3953          "out" : true,
3954          
3955         /**
3956          * @event drop
3957          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3958          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3959          * implementation that does something to process the drop event and returns true so that the drag source's
3960          * repair action does not run.
3961          * 
3962          * IMPORTANT : it should set this.success
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967         */
3968          "drop" : true
3969     });
3970             
3971      
3972     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3973         this.el.dom, 
3974         this.ddGroup || this.group,
3975         {
3976             isTarget: true,
3977             listeners : listeners || {} 
3978            
3979         
3980         }
3981     );
3982
3983 };
3984
3985 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986     /**
3987      * @cfg {String} overClass
3988      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3989      */
3990      /**
3991      * @cfg {String} ddGroup
3992      * The drag drop group to handle drop events for
3993      */
3994      
3995     /**
3996      * @cfg {String} dropAllowed
3997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998      */
3999     dropAllowed : "x-dd-drop-ok",
4000     /**
4001      * @cfg {String} dropNotAllowed
4002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003      */
4004     dropNotAllowed : "x-dd-drop-nodrop",
4005     /**
4006      * @cfg {boolean} success
4007      * set this after drop listener.. 
4008      */
4009     success : false,
4010     /**
4011      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4012      * if the drop point is valid for over/enter..
4013      */
4014     valid : false,
4015     // private
4016     isTarget : true,
4017
4018     // private
4019     isNotifyTarget : true,
4020     
4021     /**
4022      * @hide
4023      */
4024     notifyEnter : function(dd, e, data)
4025     {
4026         this.valid = true;
4027         this.fireEvent('enter', dd, e, data);
4028         if(this.overClass){
4029             this.el.addClass(this.overClass);
4030         }
4031         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4032             this.valid ? this.dropAllowed : this.dropNotAllowed
4033         );
4034     },
4035
4036     /**
4037      * @hide
4038      */
4039     notifyOver : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('over', dd, e, data);
4043         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4044             this.valid ? this.dropAllowed : this.dropNotAllowed
4045         );
4046     },
4047
4048     /**
4049      * @hide
4050      */
4051     notifyOut : function(dd, e, data)
4052     {
4053         this.fireEvent('out', dd, e, data);
4054         if(this.overClass){
4055             this.el.removeClass(this.overClass);
4056         }
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyDrop : function(dd, e, data)
4063     {
4064         this.success = false;
4065         this.fireEvent('drop', dd, e, data);
4066         return this.success;
4067     }
4068 });/*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079
4080 /**
4081  * @class Roo.dd.DragZone
4082  * @extends Roo.dd.DragSource
4083  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4084  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085  * @constructor
4086  * @param {String/HTMLElement/Element} el The container element
4087  * @param {Object} config
4088  */
4089 Roo.dd.DragZone = function(el, config){
4090     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4091     if(this.containerScroll){
4092         Roo.dd.ScrollManager.register(this.el);
4093     }
4094 };
4095
4096 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097     /**
4098      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4099      * for auto scrolling during drag operations.
4100      */
4101     /**
4102      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4103      * method after a failed drop (defaults to "c3daf9" - light blue)
4104      */
4105
4106     /**
4107      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4108      * for a valid target to drag based on the mouse down. Override this method
4109      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4110      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4111      * @param {EventObject} e The mouse down event
4112      * @return {Object} The dragData
4113      */
4114     getDragData : function(e){
4115         return Roo.dd.Registry.getHandleFromEvent(e);
4116     },
4117     
4118     /**
4119      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4120      * this.dragData.ddel
4121      * @param {Number} x The x position of the click on the dragged object
4122      * @param {Number} y The y position of the click on the dragged object
4123      * @return {Boolean} true to continue the drag, false to cancel
4124      */
4125     onInitDrag : function(x, y){
4126         this.proxy.update(this.dragData.ddel.cloneNode(true));
4127         this.onStartDrag(x, y);
4128         return true;
4129     },
4130     
4131     /**
4132      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4133      */
4134     afterRepair : function(){
4135         if(Roo.enableFx){
4136             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137         }
4138         this.dragging = false;
4139     },
4140
4141     /**
4142      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4143      * the XY of this.dragData.ddel
4144      * @param {EventObject} e The mouse up event
4145      * @return {Array} The xy location (e.g. [100, 200])
4146      */
4147     getRepairXY : function(e){
4148         return Roo.Element.fly(this.dragData.ddel).getXY();  
4149     }
4150 });/*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160 /**
4161  * @class Roo.dd.DropZone
4162  * @extends Roo.dd.DropTarget
4163  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4164  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165  * @constructor
4166  * @param {String/HTMLElement/Element} el The container element
4167  * @param {Object} config
4168  */
4169 Roo.dd.DropZone = function(el, config){
4170     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4171 };
4172
4173 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174     /**
4175      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4176      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4177      * provide your own custom lookup.
4178      * @param {Event} e The event
4179      * @return {Object} data The custom data
4180      */
4181     getTargetFromEvent : function(e){
4182         return Roo.dd.Registry.getTargetFromEvent(e);
4183     },
4184
4185     /**
4186      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4187      * that it has registered.  This method has no default implementation and should be overridden to provide
4188      * node-specific processing if necessary.
4189      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4190      * {@link #getTargetFromEvent} for this node)
4191      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4192      * @param {Event} e The event
4193      * @param {Object} data An object containing arbitrary data supplied by the drag source
4194      */
4195     onNodeEnter : function(n, dd, e, data){
4196         
4197     },
4198
4199     /**
4200      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4201      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4202      * overridden to provide the proper feedback.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4209      * underlying {@link Roo.dd.StatusProxy} can be updated
4210      */
4211     onNodeOver : function(n, dd, e, data){
4212         return this.dropAllowed;
4213     },
4214
4215     /**
4216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4217      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4218      * node-specific processing if necessary.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      */
4225     onNodeOut : function(n, dd, e, data){
4226         
4227     },
4228
4229     /**
4230      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4231      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4232      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4233      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4234      * {@link #getTargetFromEvent} for this node)
4235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4236      * @param {Event} e The event
4237      * @param {Object} data An object containing arbitrary data supplied by the drag source
4238      * @return {Boolean} True if the drop was valid, else false
4239      */
4240     onNodeDrop : function(n, dd, e, data){
4241         return false;
4242     },
4243
4244     /**
4245      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4246      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4247      * it should be overridden to provide the proper feedback if necessary.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4252      * underlying {@link Roo.dd.StatusProxy} can be updated
4253      */
4254     onContainerOver : function(dd, e, data){
4255         return this.dropNotAllowed;
4256     },
4257
4258     /**
4259      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4260      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4261      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4262      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {Boolean} True if the drop was valid, else false
4267      */
4268     onContainerDrop : function(dd, e, data){
4269         return false;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4274      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4275      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4276      * you should override this method and provide a custom implementation.
4277      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4278      * @param {Event} e The event
4279      * @param {Object} data An object containing arbitrary data supplied by the drag source
4280      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4281      * underlying {@link Roo.dd.StatusProxy} can be updated
4282      */
4283     notifyEnter : function(dd, e, data){
4284         return this.dropNotAllowed;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4289      * This method will be called on every mouse movement while the drag source is over the drop zone.
4290      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4291      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4292      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4293      * registered node, it will call {@link #onContainerOver}.
4294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4295      * @param {Event} e The event
4296      * @param {Object} data An object containing arbitrary data supplied by the drag source
4297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4298      * underlying {@link Roo.dd.StatusProxy} can be updated
4299      */
4300     notifyOver : function(dd, e, data){
4301         var n = this.getTargetFromEvent(e);
4302         if(!n){ // not over valid drop target
4303             if(this.lastOverNode){
4304                 this.onNodeOut(this.lastOverNode, dd, e, data);
4305                 this.lastOverNode = null;
4306             }
4307             return this.onContainerOver(dd, e, data);
4308         }
4309         if(this.lastOverNode != n){
4310             if(this.lastOverNode){
4311                 this.onNodeOut(this.lastOverNode, dd, e, data);
4312             }
4313             this.onNodeEnter(n, dd, e, data);
4314             this.lastOverNode = n;
4315         }
4316         return this.onNodeOver(n, dd, e, data);
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4321      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4322      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326      */
4327     notifyOut : function(dd, e, data){
4328         if(this.lastOverNode){
4329             this.onNodeOut(this.lastOverNode, dd, e, data);
4330             this.lastOverNode = null;
4331         }
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4336      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4337      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4338      * otherwise it will call {@link #onContainerDrop}.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag source
4342      * @return {Boolean} True if the drop was valid, else false
4343      */
4344     notifyDrop : function(dd, e, data){
4345         if(this.lastOverNode){
4346             this.onNodeOut(this.lastOverNode, dd, e, data);
4347             this.lastOverNode = null;
4348         }
4349         var n = this.getTargetFromEvent(e);
4350         return n ?
4351             this.onNodeDrop(n, dd, e, data) :
4352             this.onContainerDrop(dd, e, data);
4353     },
4354
4355     // private
4356     triggerCacheRefresh : function(){
4357         Roo.dd.DDM.refreshCache(this.groups);
4358     }  
4359 });/*
4360  * Based on:
4361  * Ext JS Library 1.1.1
4362  * Copyright(c) 2006-2007, Ext JS, LLC.
4363  *
4364  * Originally Released Under LGPL - original licence link has changed is not relivant.
4365  *
4366  * Fork - LGPL
4367  * <script type="text/javascript">
4368  */
4369
4370
4371 /**
4372  * @class Roo.data.SortTypes
4373  * @singleton
4374  * Defines the default sorting (casting?) comparison functions used when sorting data.
4375  */
4376 Roo.data.SortTypes = {
4377     /**
4378      * Default sort that does nothing
4379      * @param {Mixed} s The value being converted
4380      * @return {Mixed} The comparison value
4381      */
4382     none : function(s){
4383         return s;
4384     },
4385     
4386     /**
4387      * The regular expression used to strip tags
4388      * @type {RegExp}
4389      * @property
4390      */
4391     stripTagsRE : /<\/?[^>]+>/gi,
4392     
4393     /**
4394      * Strips all HTML tags to sort on text only
4395      * @param {Mixed} s The value being converted
4396      * @return {String} The comparison value
4397      */
4398     asText : function(s){
4399         return String(s).replace(this.stripTagsRE, "");
4400     },
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only - Case insensitive
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asUCText : function(s){
4408         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Case insensitive string
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCString : function(s) {
4417         return String(s).toUpperCase();
4418     },
4419     
4420     /**
4421      * Date sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Number} The comparison value
4424      */
4425     asDate : function(s) {
4426         if(!s){
4427             return 0;
4428         }
4429         if(s instanceof Date){
4430             return s.getTime();
4431         }
4432         return Date.parse(String(s));
4433     },
4434     
4435     /**
4436      * Float sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Float} The comparison value
4439      */
4440     asFloat : function(s) {
4441         var val = parseFloat(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     },
4445     
4446     /**
4447      * Integer sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Number} The comparison value
4450      */
4451     asInt : function(s) {
4452         var val = parseInt(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     }
4456 };/*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466
4467 /**
4468 * @class Roo.data.Record
4469  * Instances of this class encapsulate both record <em>definition</em> information, and record
4470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4471  * to access Records cached in an {@link Roo.data.Store} object.<br>
4472  * <p>
4473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4475  * objects.<br>
4476  * <p>
4477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478  * @constructor
4479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4480  * {@link #create}. The parameters are the same.
4481  * @param {Array} data An associative Array of data values keyed by the field name.
4482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4484  * not specified an integer id is generated.
4485  */
4486 Roo.data.Record = function(data, id){
4487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4488     this.data = data;
4489 };
4490
4491 /**
4492  * Generate a constructor for a specific record layout.
4493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4495  * Each field definition object may contain the following properties: <ul>
4496  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4503  * this may be omitted.</p></li>
4504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4505  * <ul><li>auto (Default, implies no conversion)</li>
4506  * <li>string</li>
4507  * <li>int</li>
4508  * <li>float</li>
4509  * <li>boolean</li>
4510  * <li>date</li></ul></p></li>
4511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4514  * by the Reader into an object that will be stored in the Record. It is passed the
4515  * following parameters:<ul>
4516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517  * </ul></p></li>
4518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519  * </ul>
4520  * <br>usage:<br><pre><code>
4521 var TopicRecord = Roo.data.Record.create(
4522     {name: 'title', mapping: 'topic_title'},
4523     {name: 'author', mapping: 'username'},
4524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4526     {name: 'lastPoster', mapping: 'user2'},
4527     {name: 'excerpt', mapping: 'post_text'}
4528 );
4529
4530 var myNewRecord = new TopicRecord({
4531     title: 'Do my job please',
4532     author: 'noobie',
4533     totalPosts: 1,
4534     lastPost: new Date(),
4535     lastPoster: 'Animal',
4536     excerpt: 'No way dude!'
4537 });
4538 myStore.add(myNewRecord);
4539 </code></pre>
4540  * @method create
4541  * @static
4542  */
4543 Roo.data.Record.create = function(o){
4544     var f = function(){
4545         f.superclass.constructor.apply(this, arguments);
4546     };
4547     Roo.extend(f, Roo.data.Record);
4548     var p = f.prototype;
4549     p.fields = new Roo.util.MixedCollection(false, function(field){
4550         return field.name;
4551     });
4552     for(var i = 0, len = o.length; i < len; i++){
4553         p.fields.add(new Roo.data.Field(o[i]));
4554     }
4555     f.getField = function(name){
4556         return p.fields.get(name);  
4557     };
4558     return f;
4559 };
4560
4561 Roo.data.Record.AUTO_ID = 1000;
4562 Roo.data.Record.EDIT = 'edit';
4563 Roo.data.Record.REJECT = 'reject';
4564 Roo.data.Record.COMMIT = 'commit';
4565
4566 Roo.data.Record.prototype = {
4567     /**
4568      * Readonly flag - true if this record has been modified.
4569      * @type Boolean
4570      */
4571     dirty : false,
4572     editing : false,
4573     error: null,
4574     modified: null,
4575
4576     // private
4577     join : function(store){
4578         this.store = store;
4579     },
4580
4581     /**
4582      * Set the named field to the specified value.
4583      * @param {String} name The name of the field to set.
4584      * @param {Object} value The value to set the field to.
4585      */
4586     set : function(name, value){
4587         if(this.data[name] == value){
4588             return;
4589         }
4590         this.dirty = true;
4591         if(!this.modified){
4592             this.modified = {};
4593         }
4594         if(typeof this.modified[name] == 'undefined'){
4595             this.modified[name] = this.data[name];
4596         }
4597         this.data[name] = value;
4598         if(!this.editing && this.store){
4599             this.store.afterEdit(this);
4600         }       
4601     },
4602
4603     /**
4604      * Get the value of the named field.
4605      * @param {String} name The name of the field to get the value of.
4606      * @return {Object} The value of the field.
4607      */
4608     get : function(name){
4609         return this.data[name]; 
4610     },
4611
4612     // private
4613     beginEdit : function(){
4614         this.editing = true;
4615         this.modified = {}; 
4616     },
4617
4618     // private
4619     cancelEdit : function(){
4620         this.editing = false;
4621         delete this.modified;
4622     },
4623
4624     // private
4625     endEdit : function(){
4626         this.editing = false;
4627         if(this.dirty && this.store){
4628             this.store.afterEdit(this);
4629         }
4630     },
4631
4632     /**
4633      * Usually called by the {@link Roo.data.Store} which owns the Record.
4634      * Rejects all changes made to the Record since either creation, or the last commit operation.
4635      * Modified fields are reverted to their original values.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of reject operations.
4639      */
4640     reject : function(){
4641         var m = this.modified;
4642         for(var n in m){
4643             if(typeof m[n] != "function"){
4644                 this.data[n] = m[n];
4645             }
4646         }
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterReject(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Commits all changes made to the Record since either creation, or the last commit operation.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of commit operations.
4661      */
4662     commit : function(){
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterCommit(this);
4668         }
4669     },
4670
4671     // private
4672     hasError : function(){
4673         return this.error != null;
4674     },
4675
4676     // private
4677     clearError : function(){
4678         this.error = null;
4679     },
4680
4681     /**
4682      * Creates a copy of this record.
4683      * @param {String} id (optional) A new record id if you don't want to use this record's id
4684      * @return {Record}
4685      */
4686     copy : function(newId) {
4687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4688     }
4689 };/*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700
4701
4702 /**
4703  * @class Roo.data.Store
4704  * @extends Roo.util.Observable
4705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707  * <p>
4708  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4709  * has no knowledge of the format of the data returned by the Proxy.<br>
4710  * <p>
4711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4712  * instances from the data object. These records are cached and made available through accessor functions.
4713  * @constructor
4714  * Creates a new Store.
4715  * @param {Object} config A config object containing the objects needed for the Store to access data,
4716  * and read the data into Records.
4717  */
4718 Roo.data.Store = function(config){
4719     this.data = new Roo.util.MixedCollection(false);
4720     this.data.getKey = function(o){
4721         return o.id;
4722     };
4723     this.baseParams = {};
4724     // private
4725     this.paramNames = {
4726         "start" : "start",
4727         "limit" : "limit",
4728         "sort" : "sort",
4729         "dir" : "dir",
4730         "multisort" : "_multisort"
4731     };
4732
4733     if(config && config.data){
4734         this.inlineData = config.data;
4735         delete config.data;
4736     }
4737
4738     Roo.apply(this, config);
4739     
4740     if(this.reader){ // reader passed
4741         this.reader = Roo.factory(this.reader, Roo.data);
4742         this.reader.xmodule = this.xmodule || false;
4743         if(!this.recordType){
4744             this.recordType = this.reader.recordType;
4745         }
4746         if(this.reader.onMetaChange){
4747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4748         }
4749     }
4750
4751     if(this.recordType){
4752         this.fields = this.recordType.prototype.fields;
4753     }
4754     this.modified = [];
4755
4756     this.addEvents({
4757         /**
4758          * @event datachanged
4759          * Fires when the data cache has changed, and a widget which is using this Store
4760          * as a Record cache should refresh its view.
4761          * @param {Store} this
4762          */
4763         datachanged : true,
4764         /**
4765          * @event metachange
4766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4767          * @param {Store} this
4768          * @param {Object} meta The JSON metadata
4769          */
4770         metachange : true,
4771         /**
4772          * @event add
4773          * Fires when Records have been added to the Store
4774          * @param {Store} this
4775          * @param {Roo.data.Record[]} records The array of Records added
4776          * @param {Number} index The index at which the record(s) were added
4777          */
4778         add : true,
4779         /**
4780          * @event remove
4781          * Fires when a Record has been removed from the Store
4782          * @param {Store} this
4783          * @param {Roo.data.Record} record The Record that was removed
4784          * @param {Number} index The index at which the record was removed
4785          */
4786         remove : true,
4787         /**
4788          * @event update
4789          * Fires when a Record has been updated
4790          * @param {Store} this
4791          * @param {Roo.data.Record} record The Record that was updated
4792          * @param {String} operation The update operation being performed.  Value may be one of:
4793          * <pre><code>
4794  Roo.data.Record.EDIT
4795  Roo.data.Record.REJECT
4796  Roo.data.Record.COMMIT
4797          * </code></pre>
4798          */
4799         update : true,
4800         /**
4801          * @event clear
4802          * Fires when the data cache has been cleared.
4803          * @param {Store} this
4804          */
4805         clear : true,
4806         /**
4807          * @event beforeload
4808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4809          * the load action will be canceled.
4810          * @param {Store} this
4811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4812          */
4813         beforeload : true,
4814         /**
4815          * @event beforeloadadd
4816          * Fires after a new set of Records has been loaded.
4817          * @param {Store} this
4818          * @param {Roo.data.Record[]} records The Records that were loaded
4819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4820          */
4821         beforeloadadd : true,
4822         /**
4823          * @event load
4824          * Fires after a new set of Records has been loaded, before they are added to the store.
4825          * @param {Store} this
4826          * @param {Roo.data.Record[]} records The Records that were loaded
4827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4828          * @params {Object} return from reader
4829          */
4830         load : true,
4831         /**
4832          * @event loadexception
4833          * Fires if an exception occurs in the Proxy during loading.
4834          * Called with the signature of the Proxy's "loadexception" event.
4835          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4836          * 
4837          * @param {Proxy} 
4838          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4839          * @param {Object} load options 
4840          * @param {Object} jsonData from your request (normally this contains the Exception)
4841          */
4842         loadexception : true
4843     });
4844     
4845     if(this.proxy){
4846         this.proxy = Roo.factory(this.proxy, Roo.data);
4847         this.proxy.xmodule = this.xmodule || false;
4848         this.relayEvents(this.proxy,  ["loadexception"]);
4849     }
4850     this.sortToggle = {};
4851     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4852
4853     Roo.data.Store.superclass.constructor.call(this);
4854
4855     if(this.inlineData){
4856         this.loadData(this.inlineData);
4857         delete this.inlineData;
4858     }
4859 };
4860
4861 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4862      /**
4863     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4864     * without a remote query - used by combo/forms at present.
4865     */
4866     
4867     /**
4868     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4869     */
4870     /**
4871     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4872     */
4873     /**
4874     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4875     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4876     */
4877     /**
4878     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4879     * on any HTTP request
4880     */
4881     /**
4882     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4883     */
4884     /**
4885     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4886     */
4887     multiSort: false,
4888     /**
4889     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4890     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4891     */
4892     remoteSort : false,
4893
4894     /**
4895     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4896      * loaded or when a record is removed. (defaults to false).
4897     */
4898     pruneModifiedRecords : false,
4899
4900     // private
4901     lastOptions : null,
4902
4903     /**
4904      * Add Records to the Store and fires the add event.
4905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4906      */
4907     add : function(records){
4908         records = [].concat(records);
4909         for(var i = 0, len = records.length; i < len; i++){
4910             records[i].join(this);
4911         }
4912         var index = this.data.length;
4913         this.data.addAll(records);
4914         this.fireEvent("add", this, records, index);
4915     },
4916
4917     /**
4918      * Remove a Record from the Store and fires the remove event.
4919      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4920      */
4921     remove : function(record){
4922         var index = this.data.indexOf(record);
4923         this.data.removeAt(index);
4924         if(this.pruneModifiedRecords){
4925             this.modified.remove(record);
4926         }
4927         this.fireEvent("remove", this, record, index);
4928     },
4929
4930     /**
4931      * Remove all Records from the Store and fires the clear event.
4932      */
4933     removeAll : function(){
4934         this.data.clear();
4935         if(this.pruneModifiedRecords){
4936             this.modified = [];
4937         }
4938         this.fireEvent("clear", this);
4939     },
4940
4941     /**
4942      * Inserts Records to the Store at the given index and fires the add event.
4943      * @param {Number} index The start index at which to insert the passed Records.
4944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4945      */
4946     insert : function(index, records){
4947         records = [].concat(records);
4948         for(var i = 0, len = records.length; i < len; i++){
4949             this.data.insert(index, records[i]);
4950             records[i].join(this);
4951         }
4952         this.fireEvent("add", this, records, index);
4953     },
4954
4955     /**
4956      * Get the index within the cache of the passed Record.
4957      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4958      * @return {Number} The index of the passed Record. Returns -1 if not found.
4959      */
4960     indexOf : function(record){
4961         return this.data.indexOf(record);
4962     },
4963
4964     /**
4965      * Get the index within the cache of the Record with the passed id.
4966      * @param {String} id The id of the Record to find.
4967      * @return {Number} The index of the Record. Returns -1 if not found.
4968      */
4969     indexOfId : function(id){
4970         return this.data.indexOfKey(id);
4971     },
4972
4973     /**
4974      * Get the Record with the specified id.
4975      * @param {String} id The id of the Record to find.
4976      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4977      */
4978     getById : function(id){
4979         return this.data.key(id);
4980     },
4981
4982     /**
4983      * Get the Record at the specified index.
4984      * @param {Number} index The index of the Record to find.
4985      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4986      */
4987     getAt : function(index){
4988         return this.data.itemAt(index);
4989     },
4990
4991     /**
4992      * Returns a range of Records between specified indices.
4993      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4994      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4995      * @return {Roo.data.Record[]} An array of Records
4996      */
4997     getRange : function(start, end){
4998         return this.data.getRange(start, end);
4999     },
5000
5001     // private
5002     storeOptions : function(o){
5003         o = Roo.apply({}, o);
5004         delete o.callback;
5005         delete o.scope;
5006         this.lastOptions = o;
5007     },
5008
5009     /**
5010      * Loads the Record cache from the configured Proxy using the configured Reader.
5011      * <p>
5012      * If using remote paging, then the first load call must specify the <em>start</em>
5013      * and <em>limit</em> properties in the options.params property to establish the initial
5014      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5015      * <p>
5016      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5017      * and this call will return before the new data has been loaded. Perform any post-processing
5018      * in a callback function, or in a "load" event handler.</strong>
5019      * <p>
5020      * @param {Object} options An object containing properties which control loading options:<ul>
5021      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5022      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5023      * passed the following arguments:<ul>
5024      * <li>r : Roo.data.Record[]</li>
5025      * <li>options: Options object from the load call</li>
5026      * <li>success: Boolean success indicator</li></ul></li>
5027      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5028      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5029      * </ul>
5030      */
5031     load : function(options){
5032         options = options || {};
5033         if(this.fireEvent("beforeload", this, options) !== false){
5034             this.storeOptions(options);
5035             var p = Roo.apply(options.params || {}, this.baseParams);
5036             // if meta was not loaded from remote source.. try requesting it.
5037             if (!this.reader.metaFromRemote) {
5038                 p._requestMeta = 1;
5039             }
5040             if(this.sortInfo && this.remoteSort){
5041                 var pn = this.paramNames;
5042                 p[pn["sort"]] = this.sortInfo.field;
5043                 p[pn["dir"]] = this.sortInfo.direction;
5044             }
5045             if (this.multiSort) {
5046                 var pn = this.paramNames;
5047                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5048             }
5049             
5050             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5051         }
5052     },
5053
5054     /**
5055      * Reloads the Record cache from the configured Proxy using the configured Reader and
5056      * the options from the last load operation performed.
5057      * @param {Object} options (optional) An object containing properties which may override the options
5058      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5059      * the most recently used options are reused).
5060      */
5061     reload : function(options){
5062         this.load(Roo.applyIf(options||{}, this.lastOptions));
5063     },
5064
5065     // private
5066     // Called as a callback by the Reader during a load operation.
5067     loadRecords : function(o, options, success){
5068         if(!o || success === false){
5069             if(success !== false){
5070                 this.fireEvent("load", this, [], options, o);
5071             }
5072             if(options.callback){
5073                 options.callback.call(options.scope || this, [], options, false);
5074             }
5075             return;
5076         }
5077         // if data returned failure - throw an exception.
5078         if (o.success === false) {
5079             // show a message if no listener is registered.
5080             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
5081                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
5082             }
5083             // loadmask wil be hooked into this..
5084             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
5085             return;
5086         }
5087         var r = o.records, t = o.totalRecords || r.length;
5088         
5089         this.fireEvent("beforeloadadd", this, r, options, o);
5090         
5091         if(!options || options.add !== true){
5092             if(this.pruneModifiedRecords){
5093                 this.modified = [];
5094             }
5095             for(var i = 0, len = r.length; i < len; i++){
5096                 r[i].join(this);
5097             }
5098             if(this.snapshot){
5099                 this.data = this.snapshot;
5100                 delete this.snapshot;
5101             }
5102             this.data.clear();
5103             this.data.addAll(r);
5104             this.totalLength = t;
5105             this.applySort();
5106             this.fireEvent("datachanged", this);
5107         }else{
5108             this.totalLength = Math.max(t, this.data.length+r.length);
5109             this.add(r);
5110         }
5111         this.fireEvent("load", this, r, options, o);
5112         if(options.callback){
5113             options.callback.call(options.scope || this, r, options, true);
5114         }
5115     },
5116
5117
5118     /**
5119      * Loads data from a passed data block. A Reader which understands the format of the data
5120      * must have been configured in the constructor.
5121      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5122      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5123      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5124      */
5125     loadData : function(o, append){
5126         var r = this.reader.readRecords(o);
5127         this.loadRecords(r, {add: append}, true);
5128     },
5129
5130     /**
5131      * Gets the number of cached records.
5132      * <p>
5133      * <em>If using paging, this may not be the total size of the dataset. If the data object
5134      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5135      * the data set size</em>
5136      */
5137     getCount : function(){
5138         return this.data.length || 0;
5139     },
5140
5141     /**
5142      * Gets the total number of records in the dataset as returned by the server.
5143      * <p>
5144      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5145      * the dataset size</em>
5146      */
5147     getTotalCount : function(){
5148         return this.totalLength || 0;
5149     },
5150
5151     /**
5152      * Returns the sort state of the Store as an object with two properties:
5153      * <pre><code>
5154  field {String} The name of the field by which the Records are sorted
5155  direction {String} The sort order, "ASC" or "DESC"
5156      * </code></pre>
5157      */
5158     getSortState : function(){
5159         return this.sortInfo;
5160     },
5161
5162     // private
5163     applySort : function(){
5164         if(this.sortInfo && !this.remoteSort){
5165             var s = this.sortInfo, f = s.field;
5166             var st = this.fields.get(f).sortType;
5167             var fn = function(r1, r2){
5168                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5169                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5170             };
5171             this.data.sort(s.direction, fn);
5172             if(this.snapshot && this.snapshot != this.data){
5173                 this.snapshot.sort(s.direction, fn);
5174             }
5175         }
5176     },
5177
5178     /**
5179      * Sets the default sort column and order to be used by the next load operation.
5180      * @param {String} fieldName The name of the field to sort by.
5181      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5182      */
5183     setDefaultSort : function(field, dir){
5184         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5185     },
5186
5187     /**
5188      * Sort the Records.
5189      * If remote sorting is used, the sort is performed on the server, and the cache is
5190      * reloaded. If local sorting is used, the cache is sorted internally.
5191      * @param {String} fieldName The name of the field to sort by.
5192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5193      */
5194     sort : function(fieldName, dir){
5195         var f = this.fields.get(fieldName);
5196         if(!dir){
5197             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5198             
5199             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5200                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5201             }else{
5202                 dir = f.sortDir;
5203             }
5204         }
5205         this.sortToggle[f.name] = dir;
5206         this.sortInfo = {field: f.name, direction: dir};
5207         if(!this.remoteSort){
5208             this.applySort();
5209             this.fireEvent("datachanged", this);
5210         }else{
5211             this.load(this.lastOptions);
5212         }
5213     },
5214
5215     /**
5216      * Calls the specified function for each of the Records in the cache.
5217      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5218      * Returning <em>false</em> aborts and exits the iteration.
5219      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5220      */
5221     each : function(fn, scope){
5222         this.data.each(fn, scope);
5223     },
5224
5225     /**
5226      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5227      * (e.g., during paging).
5228      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5229      */
5230     getModifiedRecords : function(){
5231         return this.modified;
5232     },
5233
5234     // private
5235     createFilterFn : function(property, value, anyMatch){
5236         if(!value.exec){ // not a regex
5237             value = String(value);
5238             if(value.length == 0){
5239                 return false;
5240             }
5241             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5242         }
5243         return function(r){
5244             return value.test(r.data[property]);
5245         };
5246     },
5247
5248     /**
5249      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5250      * @param {String} property A field on your records
5251      * @param {Number} start The record index to start at (defaults to 0)
5252      * @param {Number} end The last record index to include (defaults to length - 1)
5253      * @return {Number} The sum
5254      */
5255     sum : function(property, start, end){
5256         var rs = this.data.items, v = 0;
5257         start = start || 0;
5258         end = (end || end === 0) ? end : rs.length-1;
5259
5260         for(var i = start; i <= end; i++){
5261             v += (rs[i].data[property] || 0);
5262         }
5263         return v;
5264     },
5265
5266     /**
5267      * Filter the records by a specified property.
5268      * @param {String} field A field on your records
5269      * @param {String/RegExp} value Either a string that the field
5270      * should start with or a RegExp to test against the field
5271      * @param {Boolean} anyMatch True to match any part not just the beginning
5272      */
5273     filter : function(property, value, anyMatch){
5274         var fn = this.createFilterFn(property, value, anyMatch);
5275         return fn ? this.filterBy(fn) : this.clearFilter();
5276     },
5277
5278     /**
5279      * Filter by a function. The specified function will be called with each
5280      * record in this data source. If the function returns true the record is included,
5281      * otherwise it is filtered.
5282      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5283      * @param {Object} scope (optional) The scope of the function (defaults to this)
5284      */
5285     filterBy : function(fn, scope){
5286         this.snapshot = this.snapshot || this.data;
5287         this.data = this.queryBy(fn, scope||this);
5288         this.fireEvent("datachanged", this);
5289     },
5290
5291     /**
5292      * Query the records by a specified property.
5293      * @param {String} field A field on your records
5294      * @param {String/RegExp} value Either a string that the field
5295      * should start with or a RegExp to test against the field
5296      * @param {Boolean} anyMatch True to match any part not just the beginning
5297      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      */
5299     query : function(property, value, anyMatch){
5300         var fn = this.createFilterFn(property, value, anyMatch);
5301         return fn ? this.queryBy(fn) : this.data.clone();
5302     },
5303
5304     /**
5305      * Query by a function. The specified function will be called with each
5306      * record in this data source. If the function returns true the record is included
5307      * in the results.
5308      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5309      * @param {Object} scope (optional) The scope of the function (defaults to this)
5310       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5311      **/
5312     queryBy : function(fn, scope){
5313         var data = this.snapshot || this.data;
5314         return data.filterBy(fn, scope||this);
5315     },
5316
5317     /**
5318      * Collects unique values for a particular dataIndex from this store.
5319      * @param {String} dataIndex The property to collect
5320      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5321      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5322      * @return {Array} An array of the unique values
5323      **/
5324     collect : function(dataIndex, allowNull, bypassFilter){
5325         var d = (bypassFilter === true && this.snapshot) ?
5326                 this.snapshot.items : this.data.items;
5327         var v, sv, r = [], l = {};
5328         for(var i = 0, len = d.length; i < len; i++){
5329             v = d[i].data[dataIndex];
5330             sv = String(v);
5331             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5332                 l[sv] = true;
5333                 r[r.length] = v;
5334             }
5335         }
5336         return r;
5337     },
5338
5339     /**
5340      * Revert to a view of the Record cache with no filtering applied.
5341      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5342      */
5343     clearFilter : function(suppressEvent){
5344         if(this.snapshot && this.snapshot != this.data){
5345             this.data = this.snapshot;
5346             delete this.snapshot;
5347             if(suppressEvent !== true){
5348                 this.fireEvent("datachanged", this);
5349             }
5350         }
5351     },
5352
5353     // private
5354     afterEdit : function(record){
5355         if(this.modified.indexOf(record) == -1){
5356             this.modified.push(record);
5357         }
5358         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5359     },
5360     
5361     // private
5362     afterReject : function(record){
5363         this.modified.remove(record);
5364         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5365     },
5366
5367     // private
5368     afterCommit : function(record){
5369         this.modified.remove(record);
5370         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5371     },
5372
5373     /**
5374      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5375      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5376      */
5377     commitChanges : function(){
5378         var m = this.modified.slice(0);
5379         this.modified = [];
5380         for(var i = 0, len = m.length; i < len; i++){
5381             m[i].commit();
5382         }
5383     },
5384
5385     /**
5386      * Cancel outstanding changes on all changed records.
5387      */
5388     rejectChanges : function(){
5389         var m = this.modified.slice(0);
5390         this.modified = [];
5391         for(var i = 0, len = m.length; i < len; i++){
5392             m[i].reject();
5393         }
5394     },
5395
5396     onMetaChange : function(meta, rtype, o){
5397         this.recordType = rtype;
5398         this.fields = rtype.prototype.fields;
5399         delete this.snapshot;
5400         this.sortInfo = meta.sortInfo || this.sortInfo;
5401         this.modified = [];
5402         this.fireEvent('metachange', this, this.reader.meta);
5403     }
5404 });/*
5405  * Based on:
5406  * Ext JS Library 1.1.1
5407  * Copyright(c) 2006-2007, Ext JS, LLC.
5408  *
5409  * Originally Released Under LGPL - original licence link has changed is not relivant.
5410  *
5411  * Fork - LGPL
5412  * <script type="text/javascript">
5413  */
5414
5415 /**
5416  * @class Roo.data.SimpleStore
5417  * @extends Roo.data.Store
5418  * Small helper class to make creating Stores from Array data easier.
5419  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5420  * @cfg {Array} fields An array of field definition objects, or field name strings.
5421  * @cfg {Array} data The multi-dimensional array of data
5422  * @constructor
5423  * @param {Object} config
5424  */
5425 Roo.data.SimpleStore = function(config){
5426     Roo.data.SimpleStore.superclass.constructor.call(this, {
5427         isLocal : true,
5428         reader: new Roo.data.ArrayReader({
5429                 id: config.id
5430             },
5431             Roo.data.Record.create(config.fields)
5432         ),
5433         proxy : new Roo.data.MemoryProxy(config.data)
5434     });
5435     this.load();
5436 };
5437 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5438  * Based on:
5439  * Ext JS Library 1.1.1
5440  * Copyright(c) 2006-2007, Ext JS, LLC.
5441  *
5442  * Originally Released Under LGPL - original licence link has changed is not relivant.
5443  *
5444  * Fork - LGPL
5445  * <script type="text/javascript">
5446  */
5447
5448 /**
5449 /**
5450  * @extends Roo.data.Store
5451  * @class Roo.data.JsonStore
5452  * Small helper class to make creating Stores for JSON data easier. <br/>
5453 <pre><code>
5454 var store = new Roo.data.JsonStore({
5455     url: 'get-images.php',
5456     root: 'images',
5457     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5458 });
5459 </code></pre>
5460  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5461  * JsonReader and HttpProxy (unless inline data is provided).</b>
5462  * @cfg {Array} fields An array of field definition objects, or field name strings.
5463  * @constructor
5464  * @param {Object} config
5465  */
5466 Roo.data.JsonStore = function(c){
5467     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5468         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5469         reader: new Roo.data.JsonReader(c, c.fields)
5470     }));
5471 };
5472 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5473  * Based on:
5474  * Ext JS Library 1.1.1
5475  * Copyright(c) 2006-2007, Ext JS, LLC.
5476  *
5477  * Originally Released Under LGPL - original licence link has changed is not relivant.
5478  *
5479  * Fork - LGPL
5480  * <script type="text/javascript">
5481  */
5482
5483  
5484 Roo.data.Field = function(config){
5485     if(typeof config == "string"){
5486         config = {name: config};
5487     }
5488     Roo.apply(this, config);
5489     
5490     if(!this.type){
5491         this.type = "auto";
5492     }
5493     
5494     var st = Roo.data.SortTypes;
5495     // named sortTypes are supported, here we look them up
5496     if(typeof this.sortType == "string"){
5497         this.sortType = st[this.sortType];
5498     }
5499     
5500     // set default sortType for strings and dates
5501     if(!this.sortType){
5502         switch(this.type){
5503             case "string":
5504                 this.sortType = st.asUCString;
5505                 break;
5506             case "date":
5507                 this.sortType = st.asDate;
5508                 break;
5509             default:
5510                 this.sortType = st.none;
5511         }
5512     }
5513
5514     // define once
5515     var stripRe = /[\$,%]/g;
5516
5517     // prebuilt conversion function for this field, instead of
5518     // switching every time we're reading a value
5519     if(!this.convert){
5520         var cv, dateFormat = this.dateFormat;
5521         switch(this.type){
5522             case "":
5523             case "auto":
5524             case undefined:
5525                 cv = function(v){ return v; };
5526                 break;
5527             case "string":
5528                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5529                 break;
5530             case "int":
5531                 cv = function(v){
5532                     return v !== undefined && v !== null && v !== '' ?
5533                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5534                     };
5535                 break;
5536             case "float":
5537                 cv = function(v){
5538                     return v !== undefined && v !== null && v !== '' ?
5539                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5540                     };
5541                 break;
5542             case "bool":
5543             case "boolean":
5544                 cv = function(v){ return v === true || v === "true" || v == 1; };
5545                 break;
5546             case "date":
5547                 cv = function(v){
5548                     if(!v){
5549                         return '';
5550                     }
5551                     if(v instanceof Date){
5552                         return v;
5553                     }
5554                     if(dateFormat){
5555                         if(dateFormat == "timestamp"){
5556                             return new Date(v*1000);
5557                         }
5558                         return Date.parseDate(v, dateFormat);
5559                     }
5560                     var parsed = Date.parse(v);
5561                     return parsed ? new Date(parsed) : null;
5562                 };
5563              break;
5564             
5565         }
5566         this.convert = cv;
5567     }
5568 };
5569
5570 Roo.data.Field.prototype = {
5571     dateFormat: null,
5572     defaultValue: "",
5573     mapping: null,
5574     sortType : null,
5575     sortDir : "ASC"
5576 };/*
5577  * Based on:
5578  * Ext JS Library 1.1.1
5579  * Copyright(c) 2006-2007, Ext JS, LLC.
5580  *
5581  * Originally Released Under LGPL - original licence link has changed is not relivant.
5582  *
5583  * Fork - LGPL
5584  * <script type="text/javascript">
5585  */
5586  
5587 // Base class for reading structured data from a data source.  This class is intended to be
5588 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5589
5590 /**
5591  * @class Roo.data.DataReader
5592  * Base class for reading structured data from a data source.  This class is intended to be
5593  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5594  */
5595
5596 Roo.data.DataReader = function(meta, recordType){
5597     
5598     this.meta = meta;
5599     
5600     this.recordType = recordType instanceof Array ? 
5601         Roo.data.Record.create(recordType) : recordType;
5602 };
5603
5604 Roo.data.DataReader.prototype = {
5605      /**
5606      * Create an empty record
5607      * @param {Object} data (optional) - overlay some values
5608      * @return {Roo.data.Record} record created.
5609      */
5610     newRow :  function(d) {
5611         var da =  {};
5612         this.recordType.prototype.fields.each(function(c) {
5613             switch( c.type) {
5614                 case 'int' : da[c.name] = 0; break;
5615                 case 'date' : da[c.name] = new Date(); break;
5616                 case 'float' : da[c.name] = 0.0; break;
5617                 case 'boolean' : da[c.name] = false; break;
5618                 default : da[c.name] = ""; break;
5619             }
5620             
5621         });
5622         return new this.recordType(Roo.apply(da, d));
5623     }
5624     
5625 };/*
5626  * Based on:
5627  * Ext JS Library 1.1.1
5628  * Copyright(c) 2006-2007, Ext JS, LLC.
5629  *
5630  * Originally Released Under LGPL - original licence link has changed is not relivant.
5631  *
5632  * Fork - LGPL
5633  * <script type="text/javascript">
5634  */
5635
5636 /**
5637  * @class Roo.data.DataProxy
5638  * @extends Roo.data.Observable
5639  * This class is an abstract base class for implementations which provide retrieval of
5640  * unformatted data objects.<br>
5641  * <p>
5642  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5643  * (of the appropriate type which knows how to parse the data object) to provide a block of
5644  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5645  * <p>
5646  * Custom implementations must implement the load method as described in
5647  * {@link Roo.data.HttpProxy#load}.
5648  */
5649 Roo.data.DataProxy = function(){
5650     this.addEvents({
5651         /**
5652          * @event beforeload
5653          * Fires before a network request is made to retrieve a data object.
5654          * @param {Object} This DataProxy object.
5655          * @param {Object} params The params parameter to the load function.
5656          */
5657         beforeload : true,
5658         /**
5659          * @event load
5660          * Fires before the load method's callback is called.
5661          * @param {Object} This DataProxy object.
5662          * @param {Object} o The data object.
5663          * @param {Object} arg The callback argument object passed to the load function.
5664          */
5665         load : true,
5666         /**
5667          * @event loadexception
5668          * Fires if an Exception occurs during data retrieval.
5669          * @param {Object} This DataProxy object.
5670          * @param {Object} o The data object.
5671          * @param {Object} arg The callback argument object passed to the load function.
5672          * @param {Object} e The Exception.
5673          */
5674         loadexception : true
5675     });
5676     Roo.data.DataProxy.superclass.constructor.call(this);
5677 };
5678
5679 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5680
5681     /**
5682      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5683      */
5684 /*
5685  * Based on:
5686  * Ext JS Library 1.1.1
5687  * Copyright(c) 2006-2007, Ext JS, LLC.
5688  *
5689  * Originally Released Under LGPL - original licence link has changed is not relivant.
5690  *
5691  * Fork - LGPL
5692  * <script type="text/javascript">
5693  */
5694 /**
5695  * @class Roo.data.MemoryProxy
5696  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5697  * to the Reader when its load method is called.
5698  * @constructor
5699  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5700  */
5701 Roo.data.MemoryProxy = function(data){
5702     if (data.data) {
5703         data = data.data;
5704     }
5705     Roo.data.MemoryProxy.superclass.constructor.call(this);
5706     this.data = data;
5707 };
5708
5709 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5710     /**
5711      * Load data from the requested source (in this case an in-memory
5712      * data object passed to the constructor), read the data object into
5713      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5714      * process that block using the passed callback.
5715      * @param {Object} params This parameter is not used by the MemoryProxy class.
5716      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5717      * object into a block of Roo.data.Records.
5718      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5719      * The function must be passed <ul>
5720      * <li>The Record block object</li>
5721      * <li>The "arg" argument from the load function</li>
5722      * <li>A boolean success indicator</li>
5723      * </ul>
5724      * @param {Object} scope The scope in which to call the callback
5725      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5726      */
5727     load : function(params, reader, callback, scope, arg){
5728         params = params || {};
5729         var result;
5730         try {
5731             result = reader.readRecords(this.data);
5732         }catch(e){
5733             this.fireEvent("loadexception", this, arg, null, e);
5734             callback.call(scope, null, arg, false);
5735             return;
5736         }
5737         callback.call(scope, result, arg, true);
5738     },
5739     
5740     // private
5741     update : function(params, records){
5742         
5743     }
5744 });/*
5745  * Based on:
5746  * Ext JS Library 1.1.1
5747  * Copyright(c) 2006-2007, Ext JS, LLC.
5748  *
5749  * Originally Released Under LGPL - original licence link has changed is not relivant.
5750  *
5751  * Fork - LGPL
5752  * <script type="text/javascript">
5753  */
5754 /**
5755  * @class Roo.data.HttpProxy
5756  * @extends Roo.data.DataProxy
5757  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5758  * configured to reference a certain URL.<br><br>
5759  * <p>
5760  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5761  * from which the running page was served.<br><br>
5762  * <p>
5763  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5764  * <p>
5765  * Be aware that to enable the browser to parse an XML document, the server must set
5766  * the Content-Type header in the HTTP response to "text/xml".
5767  * @constructor
5768  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5769  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5770  * will be used to make the request.
5771  */
5772 Roo.data.HttpProxy = function(conn){
5773     Roo.data.HttpProxy.superclass.constructor.call(this);
5774     // is conn a conn config or a real conn?
5775     this.conn = conn;
5776     this.useAjax = !conn || !conn.events;
5777   
5778 };
5779
5780 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5781     // thse are take from connection...
5782     
5783     /**
5784      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5785      */
5786     /**
5787      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5788      * extra parameters to each request made by this object. (defaults to undefined)
5789      */
5790     /**
5791      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5792      *  to each request made by this object. (defaults to undefined)
5793      */
5794     /**
5795      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5796      */
5797     /**
5798      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5799      */
5800      /**
5801      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5802      * @type Boolean
5803      */
5804   
5805
5806     /**
5807      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5808      * @type Boolean
5809      */
5810     /**
5811      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5812      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5813      * a finer-grained basis than the DataProxy events.
5814      */
5815     getConnection : function(){
5816         return this.useAjax ? Roo.Ajax : this.conn;
5817     },
5818
5819     /**
5820      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5821      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5822      * process that block using the passed callback.
5823      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5824      * for the request to the remote server.
5825      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5826      * object into a block of Roo.data.Records.
5827      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5828      * The function must be passed <ul>
5829      * <li>The Record block object</li>
5830      * <li>The "arg" argument from the load function</li>
5831      * <li>A boolean success indicator</li>
5832      * </ul>
5833      * @param {Object} scope The scope in which to call the callback
5834      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5835      */
5836     load : function(params, reader, callback, scope, arg){
5837         if(this.fireEvent("beforeload", this, params) !== false){
5838             var  o = {
5839                 params : params || {},
5840                 request: {
5841                     callback : callback,
5842                     scope : scope,
5843                     arg : arg
5844                 },
5845                 reader: reader,
5846                 callback : this.loadResponse,
5847                 scope: this
5848             };
5849             if(this.useAjax){
5850                 Roo.applyIf(o, this.conn);
5851                 if(this.activeRequest){
5852                     Roo.Ajax.abort(this.activeRequest);
5853                 }
5854                 this.activeRequest = Roo.Ajax.request(o);
5855             }else{
5856                 this.conn.request(o);
5857             }
5858         }else{
5859             callback.call(scope||this, null, arg, false);
5860         }
5861     },
5862
5863     // private
5864     loadResponse : function(o, success, response){
5865         delete this.activeRequest;
5866         if(!success){
5867             this.fireEvent("loadexception", this, o, response);
5868             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5869             return;
5870         }
5871         var result;
5872         try {
5873             result = o.reader.read(response);
5874         }catch(e){
5875             this.fireEvent("loadexception", this, o, response, e);
5876             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5877             return;
5878         }
5879         
5880         this.fireEvent("load", this, o, o.request.arg);
5881         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5882     },
5883
5884     // private
5885     update : function(dataSet){
5886
5887     },
5888
5889     // private
5890     updateResponse : function(dataSet){
5891
5892     }
5893 });/*
5894  * Based on:
5895  * Ext JS Library 1.1.1
5896  * Copyright(c) 2006-2007, Ext JS, LLC.
5897  *
5898  * Originally Released Under LGPL - original licence link has changed is not relivant.
5899  *
5900  * Fork - LGPL
5901  * <script type="text/javascript">
5902  */
5903
5904 /**
5905  * @class Roo.data.ScriptTagProxy
5906  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5907  * other than the originating domain of the running page.<br><br>
5908  * <p>
5909  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5910  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5911  * <p>
5912  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5913  * source code that is used as the source inside a &lt;script> tag.<br><br>
5914  * <p>
5915  * In order for the browser to process the returned data, the server must wrap the data object
5916  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5917  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5918  * depending on whether the callback name was passed:
5919  * <p>
5920  * <pre><code>
5921 boolean scriptTag = false;
5922 String cb = request.getParameter("callback");
5923 if (cb != null) {
5924     scriptTag = true;
5925     response.setContentType("text/javascript");
5926 } else {
5927     response.setContentType("application/x-json");
5928 }
5929 Writer out = response.getWriter();
5930 if (scriptTag) {
5931     out.write(cb + "(");
5932 }
5933 out.print(dataBlock.toJsonString());
5934 if (scriptTag) {
5935     out.write(");");
5936 }
5937 </pre></code>
5938  *
5939  * @constructor
5940  * @param {Object} config A configuration object.
5941  */
5942 Roo.data.ScriptTagProxy = function(config){
5943     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5944     Roo.apply(this, config);
5945     this.head = document.getElementsByTagName("head")[0];
5946 };
5947
5948 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5949
5950 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5951     /**
5952      * @cfg {String} url The URL from which to request the data object.
5953      */
5954     /**
5955      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5956      */
5957     timeout : 30000,
5958     /**
5959      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5960      * the server the name of the callback function set up by the load call to process the returned data object.
5961      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5962      * javascript output which calls this named function passing the data object as its only parameter.
5963      */
5964     callbackParam : "callback",
5965     /**
5966      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5967      * name to the request.
5968      */
5969     nocache : true,
5970
5971     /**
5972      * Load data from the configured URL, read the data object into
5973      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5974      * process that block using the passed callback.
5975      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5976      * for the request to the remote server.
5977      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5978      * object into a block of Roo.data.Records.
5979      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5980      * The function must be passed <ul>
5981      * <li>The Record block object</li>
5982      * <li>The "arg" argument from the load function</li>
5983      * <li>A boolean success indicator</li>
5984      * </ul>
5985      * @param {Object} scope The scope in which to call the callback
5986      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5987      */
5988     load : function(params, reader, callback, scope, arg){
5989         if(this.fireEvent("beforeload", this, params) !== false){
5990
5991             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5992
5993             var url = this.url;
5994             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5995             if(this.nocache){
5996                 url += "&_dc=" + (new Date().getTime());
5997             }
5998             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5999             var trans = {
6000                 id : transId,
6001                 cb : "stcCallback"+transId,
6002                 scriptId : "stcScript"+transId,
6003                 params : params,
6004                 arg : arg,
6005                 url : url,
6006                 callback : callback,
6007                 scope : scope,
6008                 reader : reader
6009             };
6010             var conn = this;
6011
6012             window[trans.cb] = function(o){
6013                 conn.handleResponse(o, trans);
6014             };
6015
6016             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6017
6018             if(this.autoAbort !== false){
6019                 this.abort();
6020             }
6021
6022             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6023
6024             var script = document.createElement("script");
6025             script.setAttribute("src", url);
6026             script.setAttribute("type", "text/javascript");
6027             script.setAttribute("id", trans.scriptId);
6028             this.head.appendChild(script);
6029
6030             this.trans = trans;
6031         }else{
6032             callback.call(scope||this, null, arg, false);
6033         }
6034     },
6035
6036     // private
6037     isLoading : function(){
6038         return this.trans ? true : false;
6039     },
6040
6041     /**
6042      * Abort the current server request.
6043      */
6044     abort : function(){
6045         if(this.isLoading()){
6046             this.destroyTrans(this.trans);
6047         }
6048     },
6049
6050     // private
6051     destroyTrans : function(trans, isLoaded){
6052         this.head.removeChild(document.getElementById(trans.scriptId));
6053         clearTimeout(trans.timeoutId);
6054         if(isLoaded){
6055             window[trans.cb] = undefined;
6056             try{
6057                 delete window[trans.cb];
6058             }catch(e){}
6059         }else{
6060             // if hasn't been loaded, wait for load to remove it to prevent script error
6061             window[trans.cb] = function(){
6062                 window[trans.cb] = undefined;
6063                 try{
6064                     delete window[trans.cb];
6065                 }catch(e){}
6066             };
6067         }
6068     },
6069
6070     // private
6071     handleResponse : function(o, trans){
6072         this.trans = false;
6073         this.destroyTrans(trans, true);
6074         var result;
6075         try {
6076             result = trans.reader.readRecords(o);
6077         }catch(e){
6078             this.fireEvent("loadexception", this, o, trans.arg, e);
6079             trans.callback.call(trans.scope||window, null, trans.arg, false);
6080             return;
6081         }
6082         this.fireEvent("load", this, o, trans.arg);
6083         trans.callback.call(trans.scope||window, result, trans.arg, true);
6084     },
6085
6086     // private
6087     handleFailure : function(trans){
6088         this.trans = false;
6089         this.destroyTrans(trans, false);
6090         this.fireEvent("loadexception", this, null, trans.arg);
6091         trans.callback.call(trans.scope||window, null, trans.arg, false);
6092     }
6093 });/*
6094  * Based on:
6095  * Ext JS Library 1.1.1
6096  * Copyright(c) 2006-2007, Ext JS, LLC.
6097  *
6098  * Originally Released Under LGPL - original licence link has changed is not relivant.
6099  *
6100  * Fork - LGPL
6101  * <script type="text/javascript">
6102  */
6103
6104 /**
6105  * @class Roo.data.JsonReader
6106  * @extends Roo.data.DataReader
6107  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6108  * based on mappings in a provided Roo.data.Record constructor.
6109  * 
6110  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6111  * in the reply previously. 
6112  * 
6113  * <p>
6114  * Example code:
6115  * <pre><code>
6116 var RecordDef = Roo.data.Record.create([
6117     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6118     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6119 ]);
6120 var myReader = new Roo.data.JsonReader({
6121     totalProperty: "results",    // The property which contains the total dataset size (optional)
6122     root: "rows",                // The property which contains an Array of row objects
6123     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6124 }, RecordDef);
6125 </code></pre>
6126  * <p>
6127  * This would consume a JSON file like this:
6128  * <pre><code>
6129 { 'results': 2, 'rows': [
6130     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6131     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6132 }
6133 </code></pre>
6134  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6135  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6136  * paged from the remote server.
6137  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6138  * @cfg {String} root name of the property which contains the Array of row objects.
6139  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6140  * @constructor
6141  * Create a new JsonReader
6142  * @param {Object} meta Metadata configuration options
6143  * @param {Object} recordType Either an Array of field definition objects,
6144  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6145  */
6146 Roo.data.JsonReader = function(meta, recordType){
6147     
6148     meta = meta || {};
6149     // set some defaults:
6150     Roo.applyIf(meta, {
6151         totalProperty: 'total',
6152         successProperty : 'success',
6153         root : 'data',
6154         id : 'id'
6155     });
6156     
6157     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6158 };
6159 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6160     
6161     /**
6162      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6163      * Used by Store query builder to append _requestMeta to params.
6164      * 
6165      */
6166     metaFromRemote : false,
6167     /**
6168      * This method is only used by a DataProxy which has retrieved data from a remote server.
6169      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6170      * @return {Object} data A data block which is used by an Roo.data.Store object as
6171      * a cache of Roo.data.Records.
6172      */
6173     read : function(response){
6174         var json = response.responseText;
6175        
6176         var o = /* eval:var:o */ eval("("+json+")");
6177         if(!o) {
6178             throw {message: "JsonReader.read: Json object not found"};
6179         }
6180         
6181         if(o.metaData){
6182             
6183             delete this.ef;
6184             this.metaFromRemote = true;
6185             this.meta = o.metaData;
6186             this.recordType = Roo.data.Record.create(o.metaData.fields);
6187             this.onMetaChange(this.meta, this.recordType, o);
6188         }
6189         return this.readRecords(o);
6190     },
6191
6192     // private function a store will implement
6193     onMetaChange : function(meta, recordType, o){
6194
6195     },
6196
6197     /**
6198          * @ignore
6199          */
6200     simpleAccess: function(obj, subsc) {
6201         return obj[subsc];
6202     },
6203
6204         /**
6205          * @ignore
6206          */
6207     getJsonAccessor: function(){
6208         var re = /[\[\.]/;
6209         return function(expr) {
6210             try {
6211                 return(re.test(expr))
6212                     ? new Function("obj", "return obj." + expr)
6213                     : function(obj){
6214                         return obj[expr];
6215                     };
6216             } catch(e){}
6217             return Roo.emptyFn;
6218         };
6219     }(),
6220
6221     /**
6222      * Create a data block containing Roo.data.Records from an XML document.
6223      * @param {Object} o An object which contains an Array of row objects in the property specified
6224      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6225      * which contains the total size of the dataset.
6226      * @return {Object} data A data block which is used by an Roo.data.Store object as
6227      * a cache of Roo.data.Records.
6228      */
6229     readRecords : function(o){
6230         /**
6231          * After any data loads, the raw JSON data is available for further custom processing.
6232          * @type Object
6233          */
6234         this.o = o;
6235         var s = this.meta, Record = this.recordType,
6236             f = Record.prototype.fields, fi = f.items, fl = f.length;
6237
6238 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6239         if (!this.ef) {
6240             if(s.totalProperty) {
6241                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6242                 }
6243                 if(s.successProperty) {
6244                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6245                 }
6246                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6247                 if (s.id) {
6248                         var g = this.getJsonAccessor(s.id);
6249                         this.getId = function(rec) {
6250                                 var r = g(rec);
6251                                 return (r === undefined || r === "") ? null : r;
6252                         };
6253                 } else {
6254                         this.getId = function(){return null;};
6255                 }
6256             this.ef = [];
6257             for(var jj = 0; jj < fl; jj++){
6258                 f = fi[jj];
6259                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6260                 this.ef[jj] = this.getJsonAccessor(map);
6261             }
6262         }
6263
6264         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6265         if(s.totalProperty){
6266             var vt = parseInt(this.getTotal(o), 10);
6267             if(!isNaN(vt)){
6268                 totalRecords = vt;
6269             }
6270         }
6271         if(s.successProperty){
6272             var vs = this.getSuccess(o);
6273             if(vs === false || vs === 'false'){
6274                 success = false;
6275             }
6276         }
6277         var records = [];
6278             for(var i = 0; i < c; i++){
6279                     var n = root[i];
6280                 var values = {};
6281                 var id = this.getId(n);
6282                 for(var j = 0; j < fl; j++){
6283                     f = fi[j];
6284                 var v = this.ef[j](n);
6285                 if (!f.convert) {
6286                     Roo.log('missing convert for ' + f.name);
6287                     Roo.log(f);
6288                     continue;
6289                 }
6290                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6291                 }
6292                 var record = new Record(values, id);
6293                 record.json = n;
6294                 records[i] = record;
6295             }
6296             return {
6297             raw : o,
6298                 success : success,
6299                 records : records,
6300                 totalRecords : totalRecords
6301             };
6302     }
6303 });/*
6304  * Based on:
6305  * Ext JS Library 1.1.1
6306  * Copyright(c) 2006-2007, Ext JS, LLC.
6307  *
6308  * Originally Released Under LGPL - original licence link has changed is not relivant.
6309  *
6310  * Fork - LGPL
6311  * <script type="text/javascript">
6312  */
6313
6314 /**
6315  * @class Roo.data.XmlReader
6316  * @extends Roo.data.DataReader
6317  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6318  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6319  * <p>
6320  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6321  * header in the HTTP response must be set to "text/xml".</em>
6322  * <p>
6323  * Example code:
6324  * <pre><code>
6325 var RecordDef = Roo.data.Record.create([
6326    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6327    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6328 ]);
6329 var myReader = new Roo.data.XmlReader({
6330    totalRecords: "results", // The element which contains the total dataset size (optional)
6331    record: "row",           // The repeated element which contains row information
6332    id: "id"                 // The element within the row that provides an ID for the record (optional)
6333 }, RecordDef);
6334 </code></pre>
6335  * <p>
6336  * This would consume an XML file like this:
6337  * <pre><code>
6338 &lt;?xml?>
6339 &lt;dataset>
6340  &lt;results>2&lt;/results>
6341  &lt;row>
6342    &lt;id>1&lt;/id>
6343    &lt;name>Bill&lt;/name>
6344    &lt;occupation>Gardener&lt;/occupation>
6345  &lt;/row>
6346  &lt;row>
6347    &lt;id>2&lt;/id>
6348    &lt;name>Ben&lt;/name>
6349    &lt;occupation>Horticulturalist&lt;/occupation>
6350  &lt;/row>
6351 &lt;/dataset>
6352 </code></pre>
6353  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6354  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6355  * paged from the remote server.
6356  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6357  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6358  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6359  * a record identifier value.
6360  * @constructor
6361  * Create a new XmlReader
6362  * @param {Object} meta Metadata configuration options
6363  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6364  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6365  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6366  */
6367 Roo.data.XmlReader = function(meta, recordType){
6368     meta = meta || {};
6369     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6370 };
6371 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6372     /**
6373      * This method is only used by a DataProxy which has retrieved data from a remote server.
6374          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6375          * to contain a method called 'responseXML' that returns an XML document object.
6376      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6377      * a cache of Roo.data.Records.
6378      */
6379     read : function(response){
6380         var doc = response.responseXML;
6381         if(!doc) {
6382             throw {message: "XmlReader.read: XML Document not available"};
6383         }
6384         return this.readRecords(doc);
6385     },
6386
6387     /**
6388      * Create a data block containing Roo.data.Records from an XML document.
6389          * @param {Object} doc A parsed XML document.
6390      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6391      * a cache of Roo.data.Records.
6392      */
6393     readRecords : function(doc){
6394         /**
6395          * After any data loads/reads, the raw XML Document is available for further custom processing.
6396          * @type XMLDocument
6397          */
6398         this.xmlData = doc;
6399         var root = doc.documentElement || doc;
6400         var q = Roo.DomQuery;
6401         var recordType = this.recordType, fields = recordType.prototype.fields;
6402         var sid = this.meta.id;
6403         var totalRecords = 0, success = true;
6404         if(this.meta.totalRecords){
6405             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6406         }
6407         
6408         if(this.meta.success){
6409             var sv = q.selectValue(this.meta.success, root, true);
6410             success = sv !== false && sv !== 'false';
6411         }
6412         var records = [];
6413         var ns = q.select(this.meta.record, root);
6414         for(var i = 0, len = ns.length; i < len; i++) {
6415                 var n = ns[i];
6416                 var values = {};
6417                 var id = sid ? q.selectValue(sid, n) : undefined;
6418                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6419                     var f = fields.items[j];
6420                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6421                     v = f.convert(v);
6422                     values[f.name] = v;
6423                 }
6424                 var record = new recordType(values, id);
6425                 record.node = n;
6426                 records[records.length] = record;
6427             }
6428
6429             return {
6430                 success : success,
6431                 records : records,
6432                 totalRecords : totalRecords || records.length
6433             };
6434     }
6435 });/*
6436  * Based on:
6437  * Ext JS Library 1.1.1
6438  * Copyright(c) 2006-2007, Ext JS, LLC.
6439  *
6440  * Originally Released Under LGPL - original licence link has changed is not relivant.
6441  *
6442  * Fork - LGPL
6443  * <script type="text/javascript">
6444  */
6445
6446 /**
6447  * @class Roo.data.ArrayReader
6448  * @extends Roo.data.DataReader
6449  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6450  * Each element of that Array represents a row of data fields. The
6451  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6452  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6453  * <p>
6454  * Example code:.
6455  * <pre><code>
6456 var RecordDef = Roo.data.Record.create([
6457     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6458     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6459 ]);
6460 var myReader = new Roo.data.ArrayReader({
6461     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6462 }, RecordDef);
6463 </code></pre>
6464  * <p>
6465  * This would consume an Array like this:
6466  * <pre><code>
6467 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6468   </code></pre>
6469  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6470  * @constructor
6471  * Create a new JsonReader
6472  * @param {Object} meta Metadata configuration options.
6473  * @param {Object} recordType Either an Array of field definition objects
6474  * as specified to {@link Roo.data.Record#create},
6475  * or an {@link Roo.data.Record} object
6476  * created using {@link Roo.data.Record#create}.
6477  */
6478 Roo.data.ArrayReader = function(meta, recordType){
6479     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6480 };
6481
6482 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6483     /**
6484      * Create a data block containing Roo.data.Records from an XML document.
6485      * @param {Object} o An Array of row objects which represents the dataset.
6486      * @return {Object} data A data block which is used by an Roo.data.Store object as
6487      * a cache of Roo.data.Records.
6488      */
6489     readRecords : function(o){
6490         var sid = this.meta ? this.meta.id : null;
6491         var recordType = this.recordType, fields = recordType.prototype.fields;
6492         var records = [];
6493         var root = o;
6494             for(var i = 0; i < root.length; i++){
6495                     var n = root[i];
6496                 var values = {};
6497                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6498                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6499                 var f = fields.items[j];
6500                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6501                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6502                 v = f.convert(v);
6503                 values[f.name] = v;
6504             }
6505                 var record = new recordType(values, id);
6506                 record.json = n;
6507                 records[records.length] = record;
6508             }
6509             return {
6510                 records : records,
6511                 totalRecords : records.length
6512             };
6513     }
6514 });/*
6515  * Based on:
6516  * Ext JS Library 1.1.1
6517  * Copyright(c) 2006-2007, Ext JS, LLC.
6518  *
6519  * Originally Released Under LGPL - original licence link has changed is not relivant.
6520  *
6521  * Fork - LGPL
6522  * <script type="text/javascript">
6523  */
6524
6525
6526 /**
6527  * @class Roo.data.Tree
6528  * @extends Roo.util.Observable
6529  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6530  * in the tree have most standard DOM functionality.
6531  * @constructor
6532  * @param {Node} root (optional) The root node
6533  */
6534 Roo.data.Tree = function(root){
6535    this.nodeHash = {};
6536    /**
6537     * The root node for this tree
6538     * @type Node
6539     */
6540    this.root = null;
6541    if(root){
6542        this.setRootNode(root);
6543    }
6544    this.addEvents({
6545        /**
6546         * @event append
6547         * Fires when a new child node is appended to a node in this tree.
6548         * @param {Tree} tree The owner tree
6549         * @param {Node} parent The parent node
6550         * @param {Node} node The newly appended node
6551         * @param {Number} index The index of the newly appended node
6552         */
6553        "append" : true,
6554        /**
6555         * @event remove
6556         * Fires when a child node is removed from a node in this tree.
6557         * @param {Tree} tree The owner tree
6558         * @param {Node} parent The parent node
6559         * @param {Node} node The child node removed
6560         */
6561        "remove" : true,
6562        /**
6563         * @event move
6564         * Fires when a node is moved to a new location in the tree
6565         * @param {Tree} tree The owner tree
6566         * @param {Node} node The node moved
6567         * @param {Node} oldParent The old parent of this node
6568         * @param {Node} newParent The new parent of this node
6569         * @param {Number} index The index it was moved to
6570         */
6571        "move" : true,
6572        /**
6573         * @event insert
6574         * Fires when a new child node is inserted in a node in this tree.
6575         * @param {Tree} tree The owner tree
6576         * @param {Node} parent The parent node
6577         * @param {Node} node The child node inserted
6578         * @param {Node} refNode The child node the node was inserted before
6579         */
6580        "insert" : true,
6581        /**
6582         * @event beforeappend
6583         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6584         * @param {Tree} tree The owner tree
6585         * @param {Node} parent The parent node
6586         * @param {Node} node The child node to be appended
6587         */
6588        "beforeappend" : true,
6589        /**
6590         * @event beforeremove
6591         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6592         * @param {Tree} tree The owner tree
6593         * @param {Node} parent The parent node
6594         * @param {Node} node The child node to be removed
6595         */
6596        "beforeremove" : true,
6597        /**
6598         * @event beforemove
6599         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6600         * @param {Tree} tree The owner tree
6601         * @param {Node} node The node being moved
6602         * @param {Node} oldParent The parent of the node
6603         * @param {Node} newParent The new parent the node is moving to
6604         * @param {Number} index The index it is being moved to
6605         */
6606        "beforemove" : true,
6607        /**
6608         * @event beforeinsert
6609         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6610         * @param {Tree} tree The owner tree
6611         * @param {Node} parent The parent node
6612         * @param {Node} node The child node to be inserted
6613         * @param {Node} refNode The child node the node is being inserted before
6614         */
6615        "beforeinsert" : true
6616    });
6617
6618     Roo.data.Tree.superclass.constructor.call(this);
6619 };
6620
6621 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6622     pathSeparator: "/",
6623
6624     proxyNodeEvent : function(){
6625         return this.fireEvent.apply(this, arguments);
6626     },
6627
6628     /**
6629      * Returns the root node for this tree.
6630      * @return {Node}
6631      */
6632     getRootNode : function(){
6633         return this.root;
6634     },
6635
6636     /**
6637      * Sets the root node for this tree.
6638      * @param {Node} node
6639      * @return {Node}
6640      */
6641     setRootNode : function(node){
6642         this.root = node;
6643         node.ownerTree = this;
6644         node.isRoot = true;
6645         this.registerNode(node);
6646         return node;
6647     },
6648
6649     /**
6650      * Gets a node in this tree by its id.
6651      * @param {String} id
6652      * @return {Node}
6653      */
6654     getNodeById : function(id){
6655         return this.nodeHash[id];
6656     },
6657
6658     registerNode : function(node){
6659         this.nodeHash[node.id] = node;
6660     },
6661
6662     unregisterNode : function(node){
6663         delete this.nodeHash[node.id];
6664     },
6665
6666     toString : function(){
6667         return "[Tree"+(this.id?" "+this.id:"")+"]";
6668     }
6669 });
6670
6671 /**
6672  * @class Roo.data.Node
6673  * @extends Roo.util.Observable
6674  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6675  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6676  * @constructor
6677  * @param {Object} attributes The attributes/config for the node
6678  */
6679 Roo.data.Node = function(attributes){
6680     /**
6681      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6682      * @type {Object}
6683      */
6684     this.attributes = attributes || {};
6685     this.leaf = this.attributes.leaf;
6686     /**
6687      * The node id. @type String
6688      */
6689     this.id = this.attributes.id;
6690     if(!this.id){
6691         this.id = Roo.id(null, "ynode-");
6692         this.attributes.id = this.id;
6693     }
6694      
6695     
6696     /**
6697      * All child nodes of this node. @type Array
6698      */
6699     this.childNodes = [];
6700     if(!this.childNodes.indexOf){ // indexOf is a must
6701         this.childNodes.indexOf = function(o){
6702             for(var i = 0, len = this.length; i < len; i++){
6703                 if(this[i] == o) {
6704                     return i;
6705                 }
6706             }
6707             return -1;
6708         };
6709     }
6710     /**
6711      * The parent node for this node. @type Node
6712      */
6713     this.parentNode = null;
6714     /**
6715      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6716      */
6717     this.firstChild = null;
6718     /**
6719      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6720      */
6721     this.lastChild = null;
6722     /**
6723      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6724      */
6725     this.previousSibling = null;
6726     /**
6727      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6728      */
6729     this.nextSibling = null;
6730
6731     this.addEvents({
6732        /**
6733         * @event append
6734         * Fires when a new child node is appended
6735         * @param {Tree} tree The owner tree
6736         * @param {Node} this This node
6737         * @param {Node} node The newly appended node
6738         * @param {Number} index The index of the newly appended node
6739         */
6740        "append" : true,
6741        /**
6742         * @event remove
6743         * Fires when a child node is removed
6744         * @param {Tree} tree The owner tree
6745         * @param {Node} this This node
6746         * @param {Node} node The removed node
6747         */
6748        "remove" : true,
6749        /**
6750         * @event move
6751         * Fires when this node is moved to a new location in the tree
6752         * @param {Tree} tree The owner tree
6753         * @param {Node} this This node
6754         * @param {Node} oldParent The old parent of this node
6755         * @param {Node} newParent The new parent of this node
6756         * @param {Number} index The index it was moved to
6757         */
6758        "move" : true,
6759        /**
6760         * @event insert
6761         * Fires when a new child node is inserted.
6762         * @param {Tree} tree The owner tree
6763         * @param {Node} this This node
6764         * @param {Node} node The child node inserted
6765         * @param {Node} refNode The child node the node was inserted before
6766         */
6767        "insert" : true,
6768        /**
6769         * @event beforeappend
6770         * Fires before a new child is appended, return false to cancel the append.
6771         * @param {Tree} tree The owner tree
6772         * @param {Node} this This node
6773         * @param {Node} node The child node to be appended
6774         */
6775        "beforeappend" : true,
6776        /**
6777         * @event beforeremove
6778         * Fires before a child is removed, return false to cancel the remove.
6779         * @param {Tree} tree The owner tree
6780         * @param {Node} this This node
6781         * @param {Node} node The child node to be removed
6782         */
6783        "beforeremove" : true,
6784        /**
6785         * @event beforemove
6786         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6787         * @param {Tree} tree The owner tree
6788         * @param {Node} this This node
6789         * @param {Node} oldParent The parent of this node
6790         * @param {Node} newParent The new parent this node is moving to
6791         * @param {Number} index The index it is being moved to
6792         */
6793        "beforemove" : true,
6794        /**
6795         * @event beforeinsert
6796         * Fires before a new child is inserted, return false to cancel the insert.
6797         * @param {Tree} tree The owner tree
6798         * @param {Node} this This node
6799         * @param {Node} node The child node to be inserted
6800         * @param {Node} refNode The child node the node is being inserted before
6801         */
6802        "beforeinsert" : true
6803    });
6804     this.listeners = this.attributes.listeners;
6805     Roo.data.Node.superclass.constructor.call(this);
6806 };
6807
6808 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6809     fireEvent : function(evtName){
6810         // first do standard event for this node
6811         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6812             return false;
6813         }
6814         // then bubble it up to the tree if the event wasn't cancelled
6815         var ot = this.getOwnerTree();
6816         if(ot){
6817             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6818                 return false;
6819             }
6820         }
6821         return true;
6822     },
6823
6824     /**
6825      * Returns true if this node is a leaf
6826      * @return {Boolean}
6827      */
6828     isLeaf : function(){
6829         return this.leaf === true;
6830     },
6831
6832     // private
6833     setFirstChild : function(node){
6834         this.firstChild = node;
6835     },
6836
6837     //private
6838     setLastChild : function(node){
6839         this.lastChild = node;
6840     },
6841
6842
6843     /**
6844      * Returns true if this node is the last child of its parent
6845      * @return {Boolean}
6846      */
6847     isLast : function(){
6848        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6849     },
6850
6851     /**
6852      * Returns true if this node is the first child of its parent
6853      * @return {Boolean}
6854      */
6855     isFirst : function(){
6856        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6857     },
6858
6859     hasChildNodes : function(){
6860         return !this.isLeaf() && this.childNodes.length > 0;
6861     },
6862
6863     /**
6864      * Insert node(s) as the last child node of this node.
6865      * @param {Node/Array} node The node or Array of nodes to append
6866      * @return {Node} The appended node if single append, or null if an array was passed
6867      */
6868     appendChild : function(node){
6869         var multi = false;
6870         if(node instanceof Array){
6871             multi = node;
6872         }else if(arguments.length > 1){
6873             multi = arguments;
6874         }
6875         // if passed an array or multiple args do them one by one
6876         if(multi){
6877             for(var i = 0, len = multi.length; i < len; i++) {
6878                 this.appendChild(multi[i]);
6879             }
6880         }else{
6881             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6882                 return false;
6883             }
6884             var index = this.childNodes.length;
6885             var oldParent = node.parentNode;
6886             // it's a move, make sure we move it cleanly
6887             if(oldParent){
6888                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6889                     return false;
6890                 }
6891                 oldParent.removeChild(node);
6892             }
6893             index = this.childNodes.length;
6894             if(index == 0){
6895                 this.setFirstChild(node);
6896             }
6897             this.childNodes.push(node);
6898             node.parentNode = this;
6899             var ps = this.childNodes[index-1];
6900             if(ps){
6901                 node.previousSibling = ps;
6902                 ps.nextSibling = node;
6903             }else{
6904                 node.previousSibling = null;
6905             }
6906             node.nextSibling = null;
6907             this.setLastChild(node);
6908             node.setOwnerTree(this.getOwnerTree());
6909             this.fireEvent("append", this.ownerTree, this, node, index);
6910             if(oldParent){
6911                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6912             }
6913             return node;
6914         }
6915     },
6916
6917     /**
6918      * Removes a child node from this node.
6919      * @param {Node} node The node to remove
6920      * @return {Node} The removed node
6921      */
6922     removeChild : function(node){
6923         var index = this.childNodes.indexOf(node);
6924         if(index == -1){
6925             return false;
6926         }
6927         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6928             return false;
6929         }
6930
6931         // remove it from childNodes collection
6932         this.childNodes.splice(index, 1);
6933
6934         // update siblings
6935         if(node.previousSibling){
6936             node.previousSibling.nextSibling = node.nextSibling;
6937         }
6938         if(node.nextSibling){
6939             node.nextSibling.previousSibling = node.previousSibling;
6940         }
6941
6942         // update child refs
6943         if(this.firstChild == node){
6944             this.setFirstChild(node.nextSibling);
6945         }
6946         if(this.lastChild == node){
6947             this.setLastChild(node.previousSibling);
6948         }
6949
6950         node.setOwnerTree(null);
6951         // clear any references from the node
6952         node.parentNode = null;
6953         node.previousSibling = null;
6954         node.nextSibling = null;
6955         this.fireEvent("remove", this.ownerTree, this, node);
6956         return node;
6957     },
6958
6959     /**
6960      * Inserts the first node before the second node in this nodes childNodes collection.
6961      * @param {Node} node The node to insert
6962      * @param {Node} refNode The node to insert before (if null the node is appended)
6963      * @return {Node} The inserted node
6964      */
6965     insertBefore : function(node, refNode){
6966         if(!refNode){ // like standard Dom, refNode can be null for append
6967             return this.appendChild(node);
6968         }
6969         // nothing to do
6970         if(node == refNode){
6971             return false;
6972         }
6973
6974         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6975             return false;
6976         }
6977         var index = this.childNodes.indexOf(refNode);
6978         var oldParent = node.parentNode;
6979         var refIndex = index;
6980
6981         // when moving internally, indexes will change after remove
6982         if(oldParent == this && this.childNodes.indexOf(node) < index){
6983             refIndex--;
6984         }
6985
6986         // it's a move, make sure we move it cleanly
6987         if(oldParent){
6988             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6989                 return false;
6990             }
6991             oldParent.removeChild(node);
6992         }
6993         if(refIndex == 0){
6994             this.setFirstChild(node);
6995         }
6996         this.childNodes.splice(refIndex, 0, node);
6997         node.parentNode = this;
6998         var ps = this.childNodes[refIndex-1];
6999         if(ps){
7000             node.previousSibling = ps;
7001             ps.nextSibling = node;
7002         }else{
7003             node.previousSibling = null;
7004         }
7005         node.nextSibling = refNode;
7006         refNode.previousSibling = node;
7007         node.setOwnerTree(this.getOwnerTree());
7008         this.fireEvent("insert", this.ownerTree, this, node, refNode);
7009         if(oldParent){
7010             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
7011         }
7012         return node;
7013     },
7014
7015     /**
7016      * Returns the child node at the specified index.
7017      * @param {Number} index
7018      * @return {Node}
7019      */
7020     item : function(index){
7021         return this.childNodes[index];
7022     },
7023
7024     /**
7025      * Replaces one child node in this node with another.
7026      * @param {Node} newChild The replacement node
7027      * @param {Node} oldChild The node to replace
7028      * @return {Node} The replaced node
7029      */
7030     replaceChild : function(newChild, oldChild){
7031         this.insertBefore(newChild, oldChild);
7032         this.removeChild(oldChild);
7033         return oldChild;
7034     },
7035
7036     /**
7037      * Returns the index of a child node
7038      * @param {Node} node
7039      * @return {Number} The index of the node or -1 if it was not found
7040      */
7041     indexOf : function(child){
7042         return this.childNodes.indexOf(child);
7043     },
7044
7045     /**
7046      * Returns the tree this node is in.
7047      * @return {Tree}
7048      */
7049     getOwnerTree : function(){
7050         // if it doesn't have one, look for one
7051         if(!this.ownerTree){
7052             var p = this;
7053             while(p){
7054                 if(p.ownerTree){
7055                     this.ownerTree = p.ownerTree;
7056                     break;
7057                 }
7058                 p = p.parentNode;
7059             }
7060         }
7061         return this.ownerTree;
7062     },
7063
7064     /**
7065      * Returns depth of this node (the root node has a depth of 0)
7066      * @return {Number}
7067      */
7068     getDepth : function(){
7069         var depth = 0;
7070         var p = this;
7071         while(p.parentNode){
7072             ++depth;
7073             p = p.parentNode;
7074         }
7075         return depth;
7076     },
7077
7078     // private
7079     setOwnerTree : function(tree){
7080         // if it's move, we need to update everyone
7081         if(tree != this.ownerTree){
7082             if(this.ownerTree){
7083                 this.ownerTree.unregisterNode(this);
7084             }
7085             this.ownerTree = tree;
7086             var cs = this.childNodes;
7087             for(var i = 0, len = cs.length; i < len; i++) {
7088                 cs[i].setOwnerTree(tree);
7089             }
7090             if(tree){
7091                 tree.registerNode(this);
7092             }
7093         }
7094     },
7095
7096     /**
7097      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7098      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7099      * @return {String} The path
7100      */
7101     getPath : function(attr){
7102         attr = attr || "id";
7103         var p = this.parentNode;
7104         var b = [this.attributes[attr]];
7105         while(p){
7106             b.unshift(p.attributes[attr]);
7107             p = p.parentNode;
7108         }
7109         var sep = this.getOwnerTree().pathSeparator;
7110         return sep + b.join(sep);
7111     },
7112
7113     /**
7114      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7115      * function call will be the scope provided or the current node. The arguments to the function
7116      * will be the args provided or the current node. If the function returns false at any point,
7117      * the bubble is stopped.
7118      * @param {Function} fn The function to call
7119      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7120      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7121      */
7122     bubble : function(fn, scope, args){
7123         var p = this;
7124         while(p){
7125             if(fn.call(scope || p, args || p) === false){
7126                 break;
7127             }
7128             p = p.parentNode;
7129         }
7130     },
7131
7132     /**
7133      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7134      * function call will be the scope provided or the current node. The arguments to the function
7135      * will be the args provided or the current node. If the function returns false at any point,
7136      * the cascade is stopped on that branch.
7137      * @param {Function} fn The function to call
7138      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7139      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7140      */
7141     cascade : function(fn, scope, args){
7142         if(fn.call(scope || this, args || this) !== false){
7143             var cs = this.childNodes;
7144             for(var i = 0, len = cs.length; i < len; i++) {
7145                 cs[i].cascade(fn, scope, args);
7146             }
7147         }
7148     },
7149
7150     /**
7151      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7152      * function call will be the scope provided or the current node. The arguments to the function
7153      * will be the args provided or the current node. If the function returns false at any point,
7154      * the iteration stops.
7155      * @param {Function} fn The function to call
7156      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7157      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7158      */
7159     eachChild : function(fn, scope, args){
7160         var cs = this.childNodes;
7161         for(var i = 0, len = cs.length; i < len; i++) {
7162                 if(fn.call(scope || this, args || cs[i]) === false){
7163                     break;
7164                 }
7165         }
7166     },
7167
7168     /**
7169      * Finds the first child that has the attribute with the specified value.
7170      * @param {String} attribute The attribute name
7171      * @param {Mixed} value The value to search for
7172      * @return {Node} The found child or null if none was found
7173      */
7174     findChild : function(attribute, value){
7175         var cs = this.childNodes;
7176         for(var i = 0, len = cs.length; i < len; i++) {
7177                 if(cs[i].attributes[attribute] == value){
7178                     return cs[i];
7179                 }
7180         }
7181         return null;
7182     },
7183
7184     /**
7185      * Finds the first child by a custom function. The child matches if the function passed
7186      * returns true.
7187      * @param {Function} fn
7188      * @param {Object} scope (optional)
7189      * @return {Node} The found child or null if none was found
7190      */
7191     findChildBy : function(fn, scope){
7192         var cs = this.childNodes;
7193         for(var i = 0, len = cs.length; i < len; i++) {
7194                 if(fn.call(scope||cs[i], cs[i]) === true){
7195                     return cs[i];
7196                 }
7197         }
7198         return null;
7199     },
7200
7201     /**
7202      * Sorts this nodes children using the supplied sort function
7203      * @param {Function} fn
7204      * @param {Object} scope (optional)
7205      */
7206     sort : function(fn, scope){
7207         var cs = this.childNodes;
7208         var len = cs.length;
7209         if(len > 0){
7210             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7211             cs.sort(sortFn);
7212             for(var i = 0; i < len; i++){
7213                 var n = cs[i];
7214                 n.previousSibling = cs[i-1];
7215                 n.nextSibling = cs[i+1];
7216                 if(i == 0){
7217                     this.setFirstChild(n);
7218                 }
7219                 if(i == len-1){
7220                     this.setLastChild(n);
7221                 }
7222             }
7223         }
7224     },
7225
7226     /**
7227      * Returns true if this node is an ancestor (at any point) of the passed node.
7228      * @param {Node} node
7229      * @return {Boolean}
7230      */
7231     contains : function(node){
7232         return node.isAncestor(this);
7233     },
7234
7235     /**
7236      * Returns true if the passed node is an ancestor (at any point) of this node.
7237      * @param {Node} node
7238      * @return {Boolean}
7239      */
7240     isAncestor : function(node){
7241         var p = this.parentNode;
7242         while(p){
7243             if(p == node){
7244                 return true;
7245             }
7246             p = p.parentNode;
7247         }
7248         return false;
7249     },
7250
7251     toString : function(){
7252         return "[Node"+(this.id?" "+this.id:"")+"]";
7253     }
7254 });/*
7255  * Based on:
7256  * Ext JS Library 1.1.1
7257  * Copyright(c) 2006-2007, Ext JS, LLC.
7258  *
7259  * Originally Released Under LGPL - original licence link has changed is not relivant.
7260  *
7261  * Fork - LGPL
7262  * <script type="text/javascript">
7263  */
7264  
7265
7266 /**
7267  * @class Roo.ComponentMgr
7268  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7269  * @singleton
7270  */
7271 Roo.ComponentMgr = function(){
7272     var all = new Roo.util.MixedCollection();
7273
7274     return {
7275         /**
7276          * Registers a component.
7277          * @param {Roo.Component} c The component
7278          */
7279         register : function(c){
7280             all.add(c);
7281         },
7282
7283         /**
7284          * Unregisters a component.
7285          * @param {Roo.Component} c The component
7286          */
7287         unregister : function(c){
7288             all.remove(c);
7289         },
7290
7291         /**
7292          * Returns a component by id
7293          * @param {String} id The component id
7294          */
7295         get : function(id){
7296             return all.get(id);
7297         },
7298
7299         /**
7300          * Registers a function that will be called when a specified component is added to ComponentMgr
7301          * @param {String} id The component id
7302          * @param {Funtction} fn The callback function
7303          * @param {Object} scope The scope of the callback
7304          */
7305         onAvailable : function(id, fn, scope){
7306             all.on("add", function(index, o){
7307                 if(o.id == id){
7308                     fn.call(scope || o, o);
7309                     all.un("add", fn, scope);
7310                 }
7311             });
7312         }
7313     };
7314 }();/*
7315  * Based on:
7316  * Ext JS Library 1.1.1
7317  * Copyright(c) 2006-2007, Ext JS, LLC.
7318  *
7319  * Originally Released Under LGPL - original licence link has changed is not relivant.
7320  *
7321  * Fork - LGPL
7322  * <script type="text/javascript">
7323  */
7324  
7325 /**
7326  * @class Roo.Component
7327  * @extends Roo.util.Observable
7328  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7329  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7330  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7331  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7332  * All visual components (widgets) that require rendering into a layout should subclass Component.
7333  * @constructor
7334  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7335  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7336  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7337  */
7338 Roo.Component = function(config){
7339     config = config || {};
7340     if(config.tagName || config.dom || typeof config == "string"){ // element object
7341         config = {el: config, id: config.id || config};
7342     }
7343     this.initialConfig = config;
7344
7345     Roo.apply(this, config);
7346     this.addEvents({
7347         /**
7348          * @event disable
7349          * Fires after the component is disabled.
7350              * @param {Roo.Component} this
7351              */
7352         disable : true,
7353         /**
7354          * @event enable
7355          * Fires after the component is enabled.
7356              * @param {Roo.Component} this
7357              */
7358         enable : true,
7359         /**
7360          * @event beforeshow
7361          * Fires before the component is shown.  Return false to stop the show.
7362              * @param {Roo.Component} this
7363              */
7364         beforeshow : true,
7365         /**
7366          * @event show
7367          * Fires after the component is shown.
7368              * @param {Roo.Component} this
7369              */
7370         show : true,
7371         /**
7372          * @event beforehide
7373          * Fires before the component is hidden. Return false to stop the hide.
7374              * @param {Roo.Component} this
7375              */
7376         beforehide : true,
7377         /**
7378          * @event hide
7379          * Fires after the component is hidden.
7380              * @param {Roo.Component} this
7381              */
7382         hide : true,
7383         /**
7384          * @event beforerender
7385          * Fires before the component is rendered. Return false to stop the render.
7386              * @param {Roo.Component} this
7387              */
7388         beforerender : true,
7389         /**
7390          * @event render
7391          * Fires after the component is rendered.
7392              * @param {Roo.Component} this
7393              */
7394         render : true,
7395         /**
7396          * @event beforedestroy
7397          * Fires before the component is destroyed. Return false to stop the destroy.
7398              * @param {Roo.Component} this
7399              */
7400         beforedestroy : true,
7401         /**
7402          * @event destroy
7403          * Fires after the component is destroyed.
7404              * @param {Roo.Component} this
7405              */
7406         destroy : true
7407     });
7408     if(!this.id){
7409         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7410     }
7411     Roo.ComponentMgr.register(this);
7412     Roo.Component.superclass.constructor.call(this);
7413     this.initComponent();
7414     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7415         this.render(this.renderTo);
7416         delete this.renderTo;
7417     }
7418 };
7419
7420 /** @private */
7421 Roo.Component.AUTO_ID = 1000;
7422
7423 Roo.extend(Roo.Component, Roo.util.Observable, {
7424     /**
7425      * @scope Roo.Component.prototype
7426      * @type {Boolean}
7427      * true if this component is hidden. Read-only.
7428      */
7429     hidden : false,
7430     /**
7431      * @type {Boolean}
7432      * true if this component is disabled. Read-only.
7433      */
7434     disabled : false,
7435     /**
7436      * @type {Boolean}
7437      * true if this component has been rendered. Read-only.
7438      */
7439     rendered : false,
7440     
7441     /** @cfg {String} disableClass
7442      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7443      */
7444     disabledClass : "x-item-disabled",
7445         /** @cfg {Boolean} allowDomMove
7446          * Whether the component can move the Dom node when rendering (defaults to true).
7447          */
7448     allowDomMove : true,
7449     /** @cfg {String} hideMode
7450      * How this component should hidden. Supported values are
7451      * "visibility" (css visibility), "offsets" (negative offset position) and
7452      * "display" (css display) - defaults to "display".
7453      */
7454     hideMode: 'display',
7455
7456     /** @private */
7457     ctype : "Roo.Component",
7458
7459     /**
7460      * @cfg {String} actionMode 
7461      * which property holds the element that used for  hide() / show() / disable() / enable()
7462      * default is 'el' 
7463      */
7464     actionMode : "el",
7465
7466     /** @private */
7467     getActionEl : function(){
7468         return this[this.actionMode];
7469     },
7470
7471     initComponent : Roo.emptyFn,
7472     /**
7473      * If this is a lazy rendering component, render it to its container element.
7474      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7475      */
7476     render : function(container, position){
7477         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7478             if(!container && this.el){
7479                 this.el = Roo.get(this.el);
7480                 container = this.el.dom.parentNode;
7481                 this.allowDomMove = false;
7482             }
7483             this.container = Roo.get(container);
7484             this.rendered = true;
7485             if(position !== undefined){
7486                 if(typeof position == 'number'){
7487                     position = this.container.dom.childNodes[position];
7488                 }else{
7489                     position = Roo.getDom(position);
7490                 }
7491             }
7492             this.onRender(this.container, position || null);
7493             if(this.cls){
7494                 this.el.addClass(this.cls);
7495                 delete this.cls;
7496             }
7497             if(this.style){
7498                 this.el.applyStyles(this.style);
7499                 delete this.style;
7500             }
7501             this.fireEvent("render", this);
7502             this.afterRender(this.container);
7503             if(this.hidden){
7504                 this.hide();
7505             }
7506             if(this.disabled){
7507                 this.disable();
7508             }
7509         }
7510         return this;
7511     },
7512
7513     /** @private */
7514     // default function is not really useful
7515     onRender : function(ct, position){
7516         if(this.el){
7517             this.el = Roo.get(this.el);
7518             if(this.allowDomMove !== false){
7519                 ct.dom.insertBefore(this.el.dom, position);
7520             }
7521         }
7522     },
7523
7524     /** @private */
7525     getAutoCreate : function(){
7526         var cfg = typeof this.autoCreate == "object" ?
7527                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7528         if(this.id && !cfg.id){
7529             cfg.id = this.id;
7530         }
7531         return cfg;
7532     },
7533
7534     /** @private */
7535     afterRender : Roo.emptyFn,
7536
7537     /**
7538      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7539      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7540      */
7541     destroy : function(){
7542         if(this.fireEvent("beforedestroy", this) !== false){
7543             this.purgeListeners();
7544             this.beforeDestroy();
7545             if(this.rendered){
7546                 this.el.removeAllListeners();
7547                 this.el.remove();
7548                 if(this.actionMode == "container"){
7549                     this.container.remove();
7550                 }
7551             }
7552             this.onDestroy();
7553             Roo.ComponentMgr.unregister(this);
7554             this.fireEvent("destroy", this);
7555         }
7556     },
7557
7558         /** @private */
7559     beforeDestroy : function(){
7560
7561     },
7562
7563         /** @private */
7564         onDestroy : function(){
7565
7566     },
7567
7568     /**
7569      * Returns the underlying {@link Roo.Element}.
7570      * @return {Roo.Element} The element
7571      */
7572     getEl : function(){
7573         return this.el;
7574     },
7575
7576     /**
7577      * Returns the id of this component.
7578      * @return {String}
7579      */
7580     getId : function(){
7581         return this.id;
7582     },
7583
7584     /**
7585      * Try to focus this component.
7586      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7587      * @return {Roo.Component} this
7588      */
7589     focus : function(selectText){
7590         if(this.rendered){
7591             this.el.focus();
7592             if(selectText === true){
7593                 this.el.dom.select();
7594             }
7595         }
7596         return this;
7597     },
7598
7599     /** @private */
7600     blur : function(){
7601         if(this.rendered){
7602             this.el.blur();
7603         }
7604         return this;
7605     },
7606
7607     /**
7608      * Disable this component.
7609      * @return {Roo.Component} this
7610      */
7611     disable : function(){
7612         if(this.rendered){
7613             this.onDisable();
7614         }
7615         this.disabled = true;
7616         this.fireEvent("disable", this);
7617         return this;
7618     },
7619
7620         // private
7621     onDisable : function(){
7622         this.getActionEl().addClass(this.disabledClass);
7623         this.el.dom.disabled = true;
7624     },
7625
7626     /**
7627      * Enable this component.
7628      * @return {Roo.Component} this
7629      */
7630     enable : function(){
7631         if(this.rendered){
7632             this.onEnable();
7633         }
7634         this.disabled = false;
7635         this.fireEvent("enable", this);
7636         return this;
7637     },
7638
7639         // private
7640     onEnable : function(){
7641         this.getActionEl().removeClass(this.disabledClass);
7642         this.el.dom.disabled = false;
7643     },
7644
7645     /**
7646      * Convenience function for setting disabled/enabled by boolean.
7647      * @param {Boolean} disabled
7648      */
7649     setDisabled : function(disabled){
7650         this[disabled ? "disable" : "enable"]();
7651     },
7652
7653     /**
7654      * Show this component.
7655      * @return {Roo.Component} this
7656      */
7657     show: function(){
7658         if(this.fireEvent("beforeshow", this) !== false){
7659             this.hidden = false;
7660             if(this.rendered){
7661                 this.onShow();
7662             }
7663             this.fireEvent("show", this);
7664         }
7665         return this;
7666     },
7667
7668     // private
7669     onShow : function(){
7670         var ae = this.getActionEl();
7671         if(this.hideMode == 'visibility'){
7672             ae.dom.style.visibility = "visible";
7673         }else if(this.hideMode == 'offsets'){
7674             ae.removeClass('x-hidden');
7675         }else{
7676             ae.dom.style.display = "";
7677         }
7678     },
7679
7680     /**
7681      * Hide this component.
7682      * @return {Roo.Component} this
7683      */
7684     hide: function(){
7685         if(this.fireEvent("beforehide", this) !== false){
7686             this.hidden = true;
7687             if(this.rendered){
7688                 this.onHide();
7689             }
7690             this.fireEvent("hide", this);
7691         }
7692         return this;
7693     },
7694
7695     // private
7696     onHide : function(){
7697         var ae = this.getActionEl();
7698         if(this.hideMode == 'visibility'){
7699             ae.dom.style.visibility = "hidden";
7700         }else if(this.hideMode == 'offsets'){
7701             ae.addClass('x-hidden');
7702         }else{
7703             ae.dom.style.display = "none";
7704         }
7705     },
7706
7707     /**
7708      * Convenience function to hide or show this component by boolean.
7709      * @param {Boolean} visible True to show, false to hide
7710      * @return {Roo.Component} this
7711      */
7712     setVisible: function(visible){
7713         if(visible) {
7714             this.show();
7715         }else{
7716             this.hide();
7717         }
7718         return this;
7719     },
7720
7721     /**
7722      * Returns true if this component is visible.
7723      */
7724     isVisible : function(){
7725         return this.getActionEl().isVisible();
7726     },
7727
7728     cloneConfig : function(overrides){
7729         overrides = overrides || {};
7730         var id = overrides.id || Roo.id();
7731         var cfg = Roo.applyIf(overrides, this.initialConfig);
7732         cfg.id = id; // prevent dup id
7733         return new this.constructor(cfg);
7734     }
7735 });/*
7736  * Based on:
7737  * Ext JS Library 1.1.1
7738  * Copyright(c) 2006-2007, Ext JS, LLC.
7739  *
7740  * Originally Released Under LGPL - original licence link has changed is not relivant.
7741  *
7742  * Fork - LGPL
7743  * <script type="text/javascript">
7744  */
7745  (function(){ 
7746 /**
7747  * @class Roo.Layer
7748  * @extends Roo.Element
7749  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7750  * automatic maintaining of shadow/shim positions.
7751  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7752  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7753  * you can pass a string with a CSS class name. False turns off the shadow.
7754  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7755  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7756  * @cfg {String} cls CSS class to add to the element
7757  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7758  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7759  * @constructor
7760  * @param {Object} config An object with config options.
7761  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7762  */
7763
7764 Roo.Layer = function(config, existingEl){
7765     config = config || {};
7766     var dh = Roo.DomHelper;
7767     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7768     if(existingEl){
7769         this.dom = Roo.getDom(existingEl);
7770     }
7771     if(!this.dom){
7772         var o = config.dh || {tag: "div", cls: "x-layer"};
7773         this.dom = dh.append(pel, o);
7774     }
7775     if(config.cls){
7776         this.addClass(config.cls);
7777     }
7778     this.constrain = config.constrain !== false;
7779     this.visibilityMode = Roo.Element.VISIBILITY;
7780     if(config.id){
7781         this.id = this.dom.id = config.id;
7782     }else{
7783         this.id = Roo.id(this.dom);
7784     }
7785     this.zindex = config.zindex || this.getZIndex();
7786     this.position("absolute", this.zindex);
7787     if(config.shadow){
7788         this.shadowOffset = config.shadowOffset || 4;
7789         this.shadow = new Roo.Shadow({
7790             offset : this.shadowOffset,
7791             mode : config.shadow
7792         });
7793     }else{
7794         this.shadowOffset = 0;
7795     }
7796     this.useShim = config.shim !== false && Roo.useShims;
7797     this.useDisplay = config.useDisplay;
7798     this.hide();
7799 };
7800
7801 var supr = Roo.Element.prototype;
7802
7803 // shims are shared among layer to keep from having 100 iframes
7804 var shims = [];
7805
7806 Roo.extend(Roo.Layer, Roo.Element, {
7807
7808     getZIndex : function(){
7809         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7810     },
7811
7812     getShim : function(){
7813         if(!this.useShim){
7814             return null;
7815         }
7816         if(this.shim){
7817             return this.shim;
7818         }
7819         var shim = shims.shift();
7820         if(!shim){
7821             shim = this.createShim();
7822             shim.enableDisplayMode('block');
7823             shim.dom.style.display = 'none';
7824             shim.dom.style.visibility = 'visible';
7825         }
7826         var pn = this.dom.parentNode;
7827         if(shim.dom.parentNode != pn){
7828             pn.insertBefore(shim.dom, this.dom);
7829         }
7830         shim.setStyle('z-index', this.getZIndex()-2);
7831         this.shim = shim;
7832         return shim;
7833     },
7834
7835     hideShim : function(){
7836         if(this.shim){
7837             this.shim.setDisplayed(false);
7838             shims.push(this.shim);
7839             delete this.shim;
7840         }
7841     },
7842
7843     disableShadow : function(){
7844         if(this.shadow){
7845             this.shadowDisabled = true;
7846             this.shadow.hide();
7847             this.lastShadowOffset = this.shadowOffset;
7848             this.shadowOffset = 0;
7849         }
7850     },
7851
7852     enableShadow : function(show){
7853         if(this.shadow){
7854             this.shadowDisabled = false;
7855             this.shadowOffset = this.lastShadowOffset;
7856             delete this.lastShadowOffset;
7857             if(show){
7858                 this.sync(true);
7859             }
7860         }
7861     },
7862
7863     // private
7864     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7865     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7866     sync : function(doShow){
7867         var sw = this.shadow;
7868         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7869             var sh = this.getShim();
7870
7871             var w = this.getWidth(),
7872                 h = this.getHeight();
7873
7874             var l = this.getLeft(true),
7875                 t = this.getTop(true);
7876
7877             if(sw && !this.shadowDisabled){
7878                 if(doShow && !sw.isVisible()){
7879                     sw.show(this);
7880                 }else{
7881                     sw.realign(l, t, w, h);
7882                 }
7883                 if(sh){
7884                     if(doShow){
7885                        sh.show();
7886                     }
7887                     // fit the shim behind the shadow, so it is shimmed too
7888                     var a = sw.adjusts, s = sh.dom.style;
7889                     s.left = (Math.min(l, l+a.l))+"px";
7890                     s.top = (Math.min(t, t+a.t))+"px";
7891                     s.width = (w+a.w)+"px";
7892                     s.height = (h+a.h)+"px";
7893                 }
7894             }else if(sh){
7895                 if(doShow){
7896                    sh.show();
7897                 }
7898                 sh.setSize(w, h);
7899                 sh.setLeftTop(l, t);
7900             }
7901             
7902         }
7903     },
7904
7905     // private
7906     destroy : function(){
7907         this.hideShim();
7908         if(this.shadow){
7909             this.shadow.hide();
7910         }
7911         this.removeAllListeners();
7912         var pn = this.dom.parentNode;
7913         if(pn){
7914             pn.removeChild(this.dom);
7915         }
7916         Roo.Element.uncache(this.id);
7917     },
7918
7919     remove : function(){
7920         this.destroy();
7921     },
7922
7923     // private
7924     beginUpdate : function(){
7925         this.updating = true;
7926     },
7927
7928     // private
7929     endUpdate : function(){
7930         this.updating = false;
7931         this.sync(true);
7932     },
7933
7934     // private
7935     hideUnders : function(negOffset){
7936         if(this.shadow){
7937             this.shadow.hide();
7938         }
7939         this.hideShim();
7940     },
7941
7942     // private
7943     constrainXY : function(){
7944         if(this.constrain){
7945             var vw = Roo.lib.Dom.getViewWidth(),
7946                 vh = Roo.lib.Dom.getViewHeight();
7947             var s = Roo.get(document).getScroll();
7948
7949             var xy = this.getXY();
7950             var x = xy[0], y = xy[1];   
7951             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7952             // only move it if it needs it
7953             var moved = false;
7954             // first validate right/bottom
7955             if((x + w) > vw+s.left){
7956                 x = vw - w - this.shadowOffset;
7957                 moved = true;
7958             }
7959             if((y + h) > vh+s.top){
7960                 y = vh - h - this.shadowOffset;
7961                 moved = true;
7962             }
7963             // then make sure top/left isn't negative
7964             if(x < s.left){
7965                 x = s.left;
7966                 moved = true;
7967             }
7968             if(y < s.top){
7969                 y = s.top;
7970                 moved = true;
7971             }
7972             if(moved){
7973                 if(this.avoidY){
7974                     var ay = this.avoidY;
7975                     if(y <= ay && (y+h) >= ay){
7976                         y = ay-h-5;   
7977                     }
7978                 }
7979                 xy = [x, y];
7980                 this.storeXY(xy);
7981                 supr.setXY.call(this, xy);
7982                 this.sync();
7983             }
7984         }
7985     },
7986
7987     isVisible : function(){
7988         return this.visible;    
7989     },
7990
7991     // private
7992     showAction : function(){
7993         this.visible = true; // track visibility to prevent getStyle calls
7994         if(this.useDisplay === true){
7995             this.setDisplayed("");
7996         }else if(this.lastXY){
7997             supr.setXY.call(this, this.lastXY);
7998         }else if(this.lastLT){
7999             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
8000         }
8001     },
8002
8003     // private
8004     hideAction : function(){
8005         this.visible = false;
8006         if(this.useDisplay === true){
8007             this.setDisplayed(false);
8008         }else{
8009             this.setLeftTop(-10000,-10000);
8010         }
8011     },
8012
8013     // overridden Element method
8014     setVisible : function(v, a, d, c, e){
8015         if(v){
8016             this.showAction();
8017         }
8018         if(a && v){
8019             var cb = function(){
8020                 this.sync(true);
8021                 if(c){
8022                     c();
8023                 }
8024             }.createDelegate(this);
8025             supr.setVisible.call(this, true, true, d, cb, e);
8026         }else{
8027             if(!v){
8028                 this.hideUnders(true);
8029             }
8030             var cb = c;
8031             if(a){
8032                 cb = function(){
8033                     this.hideAction();
8034                     if(c){
8035                         c();
8036                     }
8037                 }.createDelegate(this);
8038             }
8039             supr.setVisible.call(this, v, a, d, cb, e);
8040             if(v){
8041                 this.sync(true);
8042             }else if(!a){
8043                 this.hideAction();
8044             }
8045         }
8046     },
8047
8048     storeXY : function(xy){
8049         delete this.lastLT;
8050         this.lastXY = xy;
8051     },
8052
8053     storeLeftTop : function(left, top){
8054         delete this.lastXY;
8055         this.lastLT = [left, top];
8056     },
8057
8058     // private
8059     beforeFx : function(){
8060         this.beforeAction();
8061         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8062     },
8063
8064     // private
8065     afterFx : function(){
8066         Roo.Layer.superclass.afterFx.apply(this, arguments);
8067         this.sync(this.isVisible());
8068     },
8069
8070     // private
8071     beforeAction : function(){
8072         if(!this.updating && this.shadow){
8073             this.shadow.hide();
8074         }
8075     },
8076
8077     // overridden Element method
8078     setLeft : function(left){
8079         this.storeLeftTop(left, this.getTop(true));
8080         supr.setLeft.apply(this, arguments);
8081         this.sync();
8082     },
8083
8084     setTop : function(top){
8085         this.storeLeftTop(this.getLeft(true), top);
8086         supr.setTop.apply(this, arguments);
8087         this.sync();
8088     },
8089
8090     setLeftTop : function(left, top){
8091         this.storeLeftTop(left, top);
8092         supr.setLeftTop.apply(this, arguments);
8093         this.sync();
8094     },
8095
8096     setXY : function(xy, a, d, c, e){
8097         this.fixDisplay();
8098         this.beforeAction();
8099         this.storeXY(xy);
8100         var cb = this.createCB(c);
8101         supr.setXY.call(this, xy, a, d, cb, e);
8102         if(!a){
8103             cb();
8104         }
8105     },
8106
8107     // private
8108     createCB : function(c){
8109         var el = this;
8110         return function(){
8111             el.constrainXY();
8112             el.sync(true);
8113             if(c){
8114                 c();
8115             }
8116         };
8117     },
8118
8119     // overridden Element method
8120     setX : function(x, a, d, c, e){
8121         this.setXY([x, this.getY()], a, d, c, e);
8122     },
8123
8124     // overridden Element method
8125     setY : function(y, a, d, c, e){
8126         this.setXY([this.getX(), y], a, d, c, e);
8127     },
8128
8129     // overridden Element method
8130     setSize : function(w, h, a, d, c, e){
8131         this.beforeAction();
8132         var cb = this.createCB(c);
8133         supr.setSize.call(this, w, h, a, d, cb, e);
8134         if(!a){
8135             cb();
8136         }
8137     },
8138
8139     // overridden Element method
8140     setWidth : function(w, a, d, c, e){
8141         this.beforeAction();
8142         var cb = this.createCB(c);
8143         supr.setWidth.call(this, w, a, d, cb, e);
8144         if(!a){
8145             cb();
8146         }
8147     },
8148
8149     // overridden Element method
8150     setHeight : function(h, a, d, c, e){
8151         this.beforeAction();
8152         var cb = this.createCB(c);
8153         supr.setHeight.call(this, h, a, d, cb, e);
8154         if(!a){
8155             cb();
8156         }
8157     },
8158
8159     // overridden Element method
8160     setBounds : function(x, y, w, h, a, d, c, e){
8161         this.beforeAction();
8162         var cb = this.createCB(c);
8163         if(!a){
8164             this.storeXY([x, y]);
8165             supr.setXY.call(this, [x, y]);
8166             supr.setSize.call(this, w, h, a, d, cb, e);
8167             cb();
8168         }else{
8169             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8170         }
8171         return this;
8172     },
8173     
8174     /**
8175      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8176      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8177      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8178      * @param {Number} zindex The new z-index to set
8179      * @return {this} The Layer
8180      */
8181     setZIndex : function(zindex){
8182         this.zindex = zindex;
8183         this.setStyle("z-index", zindex + 2);
8184         if(this.shadow){
8185             this.shadow.setZIndex(zindex + 1);
8186         }
8187         if(this.shim){
8188             this.shim.setStyle("z-index", zindex);
8189         }
8190     }
8191 });
8192 })();/*
8193  * Based on:
8194  * Ext JS Library 1.1.1
8195  * Copyright(c) 2006-2007, Ext JS, LLC.
8196  *
8197  * Originally Released Under LGPL - original licence link has changed is not relivant.
8198  *
8199  * Fork - LGPL
8200  * <script type="text/javascript">
8201  */
8202
8203
8204 /**
8205  * @class Roo.Shadow
8206  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8207  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8208  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8209  * @constructor
8210  * Create a new Shadow
8211  * @param {Object} config The config object
8212  */
8213 Roo.Shadow = function(config){
8214     Roo.apply(this, config);
8215     if(typeof this.mode != "string"){
8216         this.mode = this.defaultMode;
8217     }
8218     var o = this.offset, a = {h: 0};
8219     var rad = Math.floor(this.offset/2);
8220     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8221         case "drop":
8222             a.w = 0;
8223             a.l = a.t = o;
8224             a.t -= 1;
8225             if(Roo.isIE){
8226                 a.l -= this.offset + rad;
8227                 a.t -= this.offset + rad;
8228                 a.w -= rad;
8229                 a.h -= rad;
8230                 a.t += 1;
8231             }
8232         break;
8233         case "sides":
8234             a.w = (o*2);
8235             a.l = -o;
8236             a.t = o-1;
8237             if(Roo.isIE){
8238                 a.l -= (this.offset - rad);
8239                 a.t -= this.offset + rad;
8240                 a.l += 1;
8241                 a.w -= (this.offset - rad)*2;
8242                 a.w -= rad + 1;
8243                 a.h -= 1;
8244             }
8245         break;
8246         case "frame":
8247             a.w = a.h = (o*2);
8248             a.l = a.t = -o;
8249             a.t += 1;
8250             a.h -= 2;
8251             if(Roo.isIE){
8252                 a.l -= (this.offset - rad);
8253                 a.t -= (this.offset - rad);
8254                 a.l += 1;
8255                 a.w -= (this.offset + rad + 1);
8256                 a.h -= (this.offset + rad);
8257                 a.h += 1;
8258             }
8259         break;
8260     };
8261
8262     this.adjusts = a;
8263 };
8264
8265 Roo.Shadow.prototype = {
8266     /**
8267      * @cfg {String} mode
8268      * The shadow display mode.  Supports the following options:<br />
8269      * sides: Shadow displays on both sides and bottom only<br />
8270      * frame: Shadow displays equally on all four sides<br />
8271      * drop: Traditional bottom-right drop shadow (default)
8272      */
8273     /**
8274      * @cfg {String} offset
8275      * The number of pixels to offset the shadow from the element (defaults to 4)
8276      */
8277     offset: 4,
8278
8279     // private
8280     defaultMode: "drop",
8281
8282     /**
8283      * Displays the shadow under the target element
8284      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8285      */
8286     show : function(target){
8287         target = Roo.get(target);
8288         if(!this.el){
8289             this.el = Roo.Shadow.Pool.pull();
8290             if(this.el.dom.nextSibling != target.dom){
8291                 this.el.insertBefore(target);
8292             }
8293         }
8294         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8295         if(Roo.isIE){
8296             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8297         }
8298         this.realign(
8299             target.getLeft(true),
8300             target.getTop(true),
8301             target.getWidth(),
8302             target.getHeight()
8303         );
8304         this.el.dom.style.display = "block";
8305     },
8306
8307     /**
8308      * Returns true if the shadow is visible, else false
8309      */
8310     isVisible : function(){
8311         return this.el ? true : false;  
8312     },
8313
8314     /**
8315      * Direct alignment when values are already available. Show must be called at least once before
8316      * calling this method to ensure it is initialized.
8317      * @param {Number} left The target element left position
8318      * @param {Number} top The target element top position
8319      * @param {Number} width The target element width
8320      * @param {Number} height The target element height
8321      */
8322     realign : function(l, t, w, h){
8323         if(!this.el){
8324             return;
8325         }
8326         var a = this.adjusts, d = this.el.dom, s = d.style;
8327         var iea = 0;
8328         s.left = (l+a.l)+"px";
8329         s.top = (t+a.t)+"px";
8330         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8331  
8332         if(s.width != sws || s.height != shs){
8333             s.width = sws;
8334             s.height = shs;
8335             if(!Roo.isIE){
8336                 var cn = d.childNodes;
8337                 var sww = Math.max(0, (sw-12))+"px";
8338                 cn[0].childNodes[1].style.width = sww;
8339                 cn[1].childNodes[1].style.width = sww;
8340                 cn[2].childNodes[1].style.width = sww;
8341                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8342             }
8343         }
8344     },
8345
8346     /**
8347      * Hides this shadow
8348      */
8349     hide : function(){
8350         if(this.el){
8351             this.el.dom.style.display = "none";
8352             Roo.Shadow.Pool.push(this.el);
8353             delete this.el;
8354         }
8355     },
8356
8357     /**
8358      * Adjust the z-index of this shadow
8359      * @param {Number} zindex The new z-index
8360      */
8361     setZIndex : function(z){
8362         this.zIndex = z;
8363         if(this.el){
8364             this.el.setStyle("z-index", z);
8365         }
8366     }
8367 };
8368
8369 // Private utility class that manages the internal Shadow cache
8370 Roo.Shadow.Pool = function(){
8371     var p = [];
8372     var markup = Roo.isIE ?
8373                  '<div class="x-ie-shadow"></div>' :
8374                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8375     return {
8376         pull : function(){
8377             var sh = p.shift();
8378             if(!sh){
8379                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8380                 sh.autoBoxAdjust = false;
8381             }
8382             return sh;
8383         },
8384
8385         push : function(sh){
8386             p.push(sh);
8387         }
8388     };
8389 }();/*
8390  * Based on:
8391  * Ext JS Library 1.1.1
8392  * Copyright(c) 2006-2007, Ext JS, LLC.
8393  *
8394  * Originally Released Under LGPL - original licence link has changed is not relivant.
8395  *
8396  * Fork - LGPL
8397  * <script type="text/javascript">
8398  */
8399
8400 /**
8401  * @class Roo.BoxComponent
8402  * @extends Roo.Component
8403  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8404  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8405  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8406  * layout containers.
8407  * @constructor
8408  * @param {Roo.Element/String/Object} config The configuration options.
8409  */
8410 Roo.BoxComponent = function(config){
8411     Roo.Component.call(this, config);
8412     this.addEvents({
8413         /**
8414          * @event resize
8415          * Fires after the component is resized.
8416              * @param {Roo.Component} this
8417              * @param {Number} adjWidth The box-adjusted width that was set
8418              * @param {Number} adjHeight The box-adjusted height that was set
8419              * @param {Number} rawWidth The width that was originally specified
8420              * @param {Number} rawHeight The height that was originally specified
8421              */
8422         resize : true,
8423         /**
8424          * @event move
8425          * Fires after the component is moved.
8426              * @param {Roo.Component} this
8427              * @param {Number} x The new x position
8428              * @param {Number} y The new y position
8429              */
8430         move : true
8431     });
8432 };
8433
8434 Roo.extend(Roo.BoxComponent, Roo.Component, {
8435     // private, set in afterRender to signify that the component has been rendered
8436     boxReady : false,
8437     // private, used to defer height settings to subclasses
8438     deferHeight: false,
8439     /** @cfg {Number} width
8440      * width (optional) size of component
8441      */
8442      /** @cfg {Number} height
8443      * height (optional) size of component
8444      */
8445      
8446     /**
8447      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8448      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8449      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8450      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8451      * @return {Roo.BoxComponent} this
8452      */
8453     setSize : function(w, h){
8454         // support for standard size objects
8455         if(typeof w == 'object'){
8456             h = w.height;
8457             w = w.width;
8458         }
8459         // not rendered
8460         if(!this.boxReady){
8461             this.width = w;
8462             this.height = h;
8463             return this;
8464         }
8465
8466         // prevent recalcs when not needed
8467         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8468             return this;
8469         }
8470         this.lastSize = {width: w, height: h};
8471
8472         var adj = this.adjustSize(w, h);
8473         var aw = adj.width, ah = adj.height;
8474         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8475             var rz = this.getResizeEl();
8476             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8477                 rz.setSize(aw, ah);
8478             }else if(!this.deferHeight && ah !== undefined){
8479                 rz.setHeight(ah);
8480             }else if(aw !== undefined){
8481                 rz.setWidth(aw);
8482             }
8483             this.onResize(aw, ah, w, h);
8484             this.fireEvent('resize', this, aw, ah, w, h);
8485         }
8486         return this;
8487     },
8488
8489     /**
8490      * Gets the current size of the component's underlying element.
8491      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8492      */
8493     getSize : function(){
8494         return this.el.getSize();
8495     },
8496
8497     /**
8498      * Gets the current XY position of the component's underlying element.
8499      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8500      * @return {Array} The XY position of the element (e.g., [100, 200])
8501      */
8502     getPosition : function(local){
8503         if(local === true){
8504             return [this.el.getLeft(true), this.el.getTop(true)];
8505         }
8506         return this.xy || this.el.getXY();
8507     },
8508
8509     /**
8510      * Gets the current box measurements of the component's underlying element.
8511      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8512      * @returns {Object} box An object in the format {x, y, width, height}
8513      */
8514     getBox : function(local){
8515         var s = this.el.getSize();
8516         if(local){
8517             s.x = this.el.getLeft(true);
8518             s.y = this.el.getTop(true);
8519         }else{
8520             var xy = this.xy || this.el.getXY();
8521             s.x = xy[0];
8522             s.y = xy[1];
8523         }
8524         return s;
8525     },
8526
8527     /**
8528      * Sets the current box measurements of the component's underlying element.
8529      * @param {Object} box An object in the format {x, y, width, height}
8530      * @returns {Roo.BoxComponent} this
8531      */
8532     updateBox : function(box){
8533         this.setSize(box.width, box.height);
8534         this.setPagePosition(box.x, box.y);
8535         return this;
8536     },
8537
8538     // protected
8539     getResizeEl : function(){
8540         return this.resizeEl || this.el;
8541     },
8542
8543     // protected
8544     getPositionEl : function(){
8545         return this.positionEl || this.el;
8546     },
8547
8548     /**
8549      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8550      * This method fires the move event.
8551      * @param {Number} left The new left
8552      * @param {Number} top The new top
8553      * @returns {Roo.BoxComponent} this
8554      */
8555     setPosition : function(x, y){
8556         this.x = x;
8557         this.y = y;
8558         if(!this.boxReady){
8559             return this;
8560         }
8561         var adj = this.adjustPosition(x, y);
8562         var ax = adj.x, ay = adj.y;
8563
8564         var el = this.getPositionEl();
8565         if(ax !== undefined || ay !== undefined){
8566             if(ax !== undefined && ay !== undefined){
8567                 el.setLeftTop(ax, ay);
8568             }else if(ax !== undefined){
8569                 el.setLeft(ax);
8570             }else if(ay !== undefined){
8571                 el.setTop(ay);
8572             }
8573             this.onPosition(ax, ay);
8574             this.fireEvent('move', this, ax, ay);
8575         }
8576         return this;
8577     },
8578
8579     /**
8580      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8581      * This method fires the move event.
8582      * @param {Number} x The new x position
8583      * @param {Number} y The new y position
8584      * @returns {Roo.BoxComponent} this
8585      */
8586     setPagePosition : function(x, y){
8587         this.pageX = x;
8588         this.pageY = y;
8589         if(!this.boxReady){
8590             return;
8591         }
8592         if(x === undefined || y === undefined){ // cannot translate undefined points
8593             return;
8594         }
8595         var p = this.el.translatePoints(x, y);
8596         this.setPosition(p.left, p.top);
8597         return this;
8598     },
8599
8600     // private
8601     onRender : function(ct, position){
8602         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8603         if(this.resizeEl){
8604             this.resizeEl = Roo.get(this.resizeEl);
8605         }
8606         if(this.positionEl){
8607             this.positionEl = Roo.get(this.positionEl);
8608         }
8609     },
8610
8611     // private
8612     afterRender : function(){
8613         Roo.BoxComponent.superclass.afterRender.call(this);
8614         this.boxReady = true;
8615         this.setSize(this.width, this.height);
8616         if(this.x || this.y){
8617             this.setPosition(this.x, this.y);
8618         }
8619         if(this.pageX || this.pageY){
8620             this.setPagePosition(this.pageX, this.pageY);
8621         }
8622     },
8623
8624     /**
8625      * Force the component's size to recalculate based on the underlying element's current height and width.
8626      * @returns {Roo.BoxComponent} this
8627      */
8628     syncSize : function(){
8629         delete this.lastSize;
8630         this.setSize(this.el.getWidth(), this.el.getHeight());
8631         return this;
8632     },
8633
8634     /**
8635      * Called after the component is resized, this method is empty by default but can be implemented by any
8636      * subclass that needs to perform custom logic after a resize occurs.
8637      * @param {Number} adjWidth The box-adjusted width that was set
8638      * @param {Number} adjHeight The box-adjusted height that was set
8639      * @param {Number} rawWidth The width that was originally specified
8640      * @param {Number} rawHeight The height that was originally specified
8641      */
8642     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8643
8644     },
8645
8646     /**
8647      * Called after the component is moved, this method is empty by default but can be implemented by any
8648      * subclass that needs to perform custom logic after a move occurs.
8649      * @param {Number} x The new x position
8650      * @param {Number} y The new y position
8651      */
8652     onPosition : function(x, y){
8653
8654     },
8655
8656     // private
8657     adjustSize : function(w, h){
8658         if(this.autoWidth){
8659             w = 'auto';
8660         }
8661         if(this.autoHeight){
8662             h = 'auto';
8663         }
8664         return {width : w, height: h};
8665     },
8666
8667     // private
8668     adjustPosition : function(x, y){
8669         return {x : x, y: y};
8670     }
8671 });/*
8672  * Based on:
8673  * Ext JS Library 1.1.1
8674  * Copyright(c) 2006-2007, Ext JS, LLC.
8675  *
8676  * Originally Released Under LGPL - original licence link has changed is not relivant.
8677  *
8678  * Fork - LGPL
8679  * <script type="text/javascript">
8680  */
8681
8682
8683 /**
8684  * @class Roo.SplitBar
8685  * @extends Roo.util.Observable
8686  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8687  * <br><br>
8688  * Usage:
8689  * <pre><code>
8690 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8691                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8692 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8693 split.minSize = 100;
8694 split.maxSize = 600;
8695 split.animate = true;
8696 split.on('moved', splitterMoved);
8697 </code></pre>
8698  * @constructor
8699  * Create a new SplitBar
8700  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8701  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8702  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8703  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8704                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8705                         position of the SplitBar).
8706  */
8707 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8708     
8709     /** @private */
8710     this.el = Roo.get(dragElement, true);
8711     this.el.dom.unselectable = "on";
8712     /** @private */
8713     this.resizingEl = Roo.get(resizingElement, true);
8714
8715     /**
8716      * @private
8717      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8718      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8719      * @type Number
8720      */
8721     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8722     
8723     /**
8724      * The minimum size of the resizing element. (Defaults to 0)
8725      * @type Number
8726      */
8727     this.minSize = 0;
8728     
8729     /**
8730      * The maximum size of the resizing element. (Defaults to 2000)
8731      * @type Number
8732      */
8733     this.maxSize = 2000;
8734     
8735     /**
8736      * Whether to animate the transition to the new size
8737      * @type Boolean
8738      */
8739     this.animate = false;
8740     
8741     /**
8742      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8743      * @type Boolean
8744      */
8745     this.useShim = false;
8746     
8747     /** @private */
8748     this.shim = null;
8749     
8750     if(!existingProxy){
8751         /** @private */
8752         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8753     }else{
8754         this.proxy = Roo.get(existingProxy).dom;
8755     }
8756     /** @private */
8757     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8758     
8759     /** @private */
8760     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8761     
8762     /** @private */
8763     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8764     
8765     /** @private */
8766     this.dragSpecs = {};
8767     
8768     /**
8769      * @private The adapter to use to positon and resize elements
8770      */
8771     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8772     this.adapter.init(this);
8773     
8774     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8775         /** @private */
8776         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8777         this.el.addClass("x-splitbar-h");
8778     }else{
8779         /** @private */
8780         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8781         this.el.addClass("x-splitbar-v");
8782     }
8783     
8784     this.addEvents({
8785         /**
8786          * @event resize
8787          * Fires when the splitter is moved (alias for {@link #event-moved})
8788          * @param {Roo.SplitBar} this
8789          * @param {Number} newSize the new width or height
8790          */
8791         "resize" : true,
8792         /**
8793          * @event moved
8794          * Fires when the splitter is moved
8795          * @param {Roo.SplitBar} this
8796          * @param {Number} newSize the new width or height
8797          */
8798         "moved" : true,
8799         /**
8800          * @event beforeresize
8801          * Fires before the splitter is dragged
8802          * @param {Roo.SplitBar} this
8803          */
8804         "beforeresize" : true,
8805
8806         "beforeapply" : true
8807     });
8808
8809     Roo.util.Observable.call(this);
8810 };
8811
8812 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8813     onStartProxyDrag : function(x, y){
8814         this.fireEvent("beforeresize", this);
8815         if(!this.overlay){
8816             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8817             o.unselectable();
8818             o.enableDisplayMode("block");
8819             // all splitbars share the same overlay
8820             Roo.SplitBar.prototype.overlay = o;
8821         }
8822         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8823         this.overlay.show();
8824         Roo.get(this.proxy).setDisplayed("block");
8825         var size = this.adapter.getElementSize(this);
8826         this.activeMinSize = this.getMinimumSize();;
8827         this.activeMaxSize = this.getMaximumSize();;
8828         var c1 = size - this.activeMinSize;
8829         var c2 = Math.max(this.activeMaxSize - size, 0);
8830         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8831             this.dd.resetConstraints();
8832             this.dd.setXConstraint(
8833                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8834                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8835             );
8836             this.dd.setYConstraint(0, 0);
8837         }else{
8838             this.dd.resetConstraints();
8839             this.dd.setXConstraint(0, 0);
8840             this.dd.setYConstraint(
8841                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8842                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8843             );
8844          }
8845         this.dragSpecs.startSize = size;
8846         this.dragSpecs.startPoint = [x, y];
8847         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8848     },
8849     
8850     /** 
8851      * @private Called after the drag operation by the DDProxy
8852      */
8853     onEndProxyDrag : function(e){
8854         Roo.get(this.proxy).setDisplayed(false);
8855         var endPoint = Roo.lib.Event.getXY(e);
8856         if(this.overlay){
8857             this.overlay.hide();
8858         }
8859         var newSize;
8860         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8861             newSize = this.dragSpecs.startSize + 
8862                 (this.placement == Roo.SplitBar.LEFT ?
8863                     endPoint[0] - this.dragSpecs.startPoint[0] :
8864                     this.dragSpecs.startPoint[0] - endPoint[0]
8865                 );
8866         }else{
8867             newSize = this.dragSpecs.startSize + 
8868                 (this.placement == Roo.SplitBar.TOP ?
8869                     endPoint[1] - this.dragSpecs.startPoint[1] :
8870                     this.dragSpecs.startPoint[1] - endPoint[1]
8871                 );
8872         }
8873         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8874         if(newSize != this.dragSpecs.startSize){
8875             if(this.fireEvent('beforeapply', this, newSize) !== false){
8876                 this.adapter.setElementSize(this, newSize);
8877                 this.fireEvent("moved", this, newSize);
8878                 this.fireEvent("resize", this, newSize);
8879             }
8880         }
8881     },
8882     
8883     /**
8884      * Get the adapter this SplitBar uses
8885      * @return The adapter object
8886      */
8887     getAdapter : function(){
8888         return this.adapter;
8889     },
8890     
8891     /**
8892      * Set the adapter this SplitBar uses
8893      * @param {Object} adapter A SplitBar adapter object
8894      */
8895     setAdapter : function(adapter){
8896         this.adapter = adapter;
8897         this.adapter.init(this);
8898     },
8899     
8900     /**
8901      * Gets the minimum size for the resizing element
8902      * @return {Number} The minimum size
8903      */
8904     getMinimumSize : function(){
8905         return this.minSize;
8906     },
8907     
8908     /**
8909      * Sets the minimum size for the resizing element
8910      * @param {Number} minSize The minimum size
8911      */
8912     setMinimumSize : function(minSize){
8913         this.minSize = minSize;
8914     },
8915     
8916     /**
8917      * Gets the maximum size for the resizing element
8918      * @return {Number} The maximum size
8919      */
8920     getMaximumSize : function(){
8921         return this.maxSize;
8922     },
8923     
8924     /**
8925      * Sets the maximum size for the resizing element
8926      * @param {Number} maxSize The maximum size
8927      */
8928     setMaximumSize : function(maxSize){
8929         this.maxSize = maxSize;
8930     },
8931     
8932     /**
8933      * Sets the initialize size for the resizing element
8934      * @param {Number} size The initial size
8935      */
8936     setCurrentSize : function(size){
8937         var oldAnimate = this.animate;
8938         this.animate = false;
8939         this.adapter.setElementSize(this, size);
8940         this.animate = oldAnimate;
8941     },
8942     
8943     /**
8944      * Destroy this splitbar. 
8945      * @param {Boolean} removeEl True to remove the element
8946      */
8947     destroy : function(removeEl){
8948         if(this.shim){
8949             this.shim.remove();
8950         }
8951         this.dd.unreg();
8952         this.proxy.parentNode.removeChild(this.proxy);
8953         if(removeEl){
8954             this.el.remove();
8955         }
8956     }
8957 });
8958
8959 /**
8960  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8961  */
8962 Roo.SplitBar.createProxy = function(dir){
8963     var proxy = new Roo.Element(document.createElement("div"));
8964     proxy.unselectable();
8965     var cls = 'x-splitbar-proxy';
8966     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8967     document.body.appendChild(proxy.dom);
8968     return proxy.dom;
8969 };
8970
8971 /** 
8972  * @class Roo.SplitBar.BasicLayoutAdapter
8973  * Default Adapter. It assumes the splitter and resizing element are not positioned
8974  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8975  */
8976 Roo.SplitBar.BasicLayoutAdapter = function(){
8977 };
8978
8979 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8980     // do nothing for now
8981     init : function(s){
8982     
8983     },
8984     /**
8985      * Called before drag operations to get the current size of the resizing element. 
8986      * @param {Roo.SplitBar} s The SplitBar using this adapter
8987      */
8988      getElementSize : function(s){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             return s.resizingEl.getWidth();
8991         }else{
8992             return s.resizingEl.getHeight();
8993         }
8994     },
8995     
8996     /**
8997      * Called after drag operations to set the size of the resizing element.
8998      * @param {Roo.SplitBar} s The SplitBar using this adapter
8999      * @param {Number} newSize The new size to set
9000      * @param {Function} onComplete A function to be invoked when resizing is complete
9001      */
9002     setElementSize : function(s, newSize, onComplete){
9003         if(s.orientation == Roo.SplitBar.HORIZONTAL){
9004             if(!s.animate){
9005                 s.resizingEl.setWidth(newSize);
9006                 if(onComplete){
9007                     onComplete(s, newSize);
9008                 }
9009             }else{
9010                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
9011             }
9012         }else{
9013             
9014             if(!s.animate){
9015                 s.resizingEl.setHeight(newSize);
9016                 if(onComplete){
9017                     onComplete(s, newSize);
9018                 }
9019             }else{
9020                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9021             }
9022         }
9023     }
9024 };
9025
9026 /** 
9027  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9028  * @extends Roo.SplitBar.BasicLayoutAdapter
9029  * Adapter that  moves the splitter element to align with the resized sizing element. 
9030  * Used with an absolute positioned SplitBar.
9031  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9032  * document.body, make sure you assign an id to the body element.
9033  */
9034 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9035     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9036     this.container = Roo.get(container);
9037 };
9038
9039 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9040     init : function(s){
9041         this.basic.init(s);
9042     },
9043     
9044     getElementSize : function(s){
9045         return this.basic.getElementSize(s);
9046     },
9047     
9048     setElementSize : function(s, newSize, onComplete){
9049         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9050     },
9051     
9052     moveSplitter : function(s){
9053         var yes = Roo.SplitBar;
9054         switch(s.placement){
9055             case yes.LEFT:
9056                 s.el.setX(s.resizingEl.getRight());
9057                 break;
9058             case yes.RIGHT:
9059                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9060                 break;
9061             case yes.TOP:
9062                 s.el.setY(s.resizingEl.getBottom());
9063                 break;
9064             case yes.BOTTOM:
9065                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9066                 break;
9067         }
9068     }
9069 };
9070
9071 /**
9072  * Orientation constant - Create a vertical SplitBar
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.VERTICAL = 1;
9077
9078 /**
9079  * Orientation constant - Create a horizontal SplitBar
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.HORIZONTAL = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is to the left of the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.LEFT = 1;
9091
9092 /**
9093  * Placement constant - The resizing element is to the right of the splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.RIGHT = 2;
9098
9099 /**
9100  * Placement constant - The resizing element is positioned above the splitter element
9101  * @static
9102  * @type Number
9103  */
9104 Roo.SplitBar.TOP = 3;
9105
9106 /**
9107  * Placement constant - The resizing element is positioned under splitter element
9108  * @static
9109  * @type Number
9110  */
9111 Roo.SplitBar.BOTTOM = 4;
9112 /*
9113  * Based on:
9114  * Ext JS Library 1.1.1
9115  * Copyright(c) 2006-2007, Ext JS, LLC.
9116  *
9117  * Originally Released Under LGPL - original licence link has changed is not relivant.
9118  *
9119  * Fork - LGPL
9120  * <script type="text/javascript">
9121  */
9122
9123 /**
9124  * @class Roo.View
9125  * @extends Roo.util.Observable
9126  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9127  * This class also supports single and multi selection modes. <br>
9128  * Create a data model bound view:
9129  <pre><code>
9130  var store = new Roo.data.Store(...);
9131
9132  var view = new Roo.View({
9133     el : "my-element",
9134     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9135  
9136     singleSelect: true,
9137     selectedClass: "ydataview-selected",
9138     store: store
9139  });
9140
9141  // listen for node click?
9142  view.on("click", function(vw, index, node, e){
9143  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9144  });
9145
9146  // load XML data
9147  dataModel.load("foobar.xml");
9148  </code></pre>
9149  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9150  * <br><br>
9151  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9152  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9153  * 
9154  * Note: old style constructor is still suported (container, template, config)
9155  * 
9156  * @constructor
9157  * Create a new View
9158  * @param {Object} config The config object
9159  * 
9160  */
9161 Roo.View = function(config, depreciated_tpl, depreciated_config){
9162     
9163     if (typeof(depreciated_tpl) == 'undefined') {
9164         // new way.. - universal constructor.
9165         Roo.apply(this, config);
9166         this.el  = Roo.get(this.el);
9167     } else {
9168         // old format..
9169         this.el  = Roo.get(config);
9170         this.tpl = depreciated_tpl;
9171         Roo.apply(this, depreciated_config);
9172     }
9173      
9174     
9175     if(typeof(this.tpl) == "string"){
9176         this.tpl = new Roo.Template(this.tpl);
9177     } else {
9178         // support xtype ctors..
9179         this.tpl = new Roo.factory(this.tpl, Roo);
9180     }
9181     
9182     
9183     this.tpl.compile();
9184    
9185
9186      
9187     /** @private */
9188     this.addEvents({
9189         /**
9190          * @event beforeclick
9191          * Fires before a click is processed. Returns false to cancel the default action.
9192          * @param {Roo.View} this
9193          * @param {Number} index The index of the target node
9194          * @param {HTMLElement} node The target node
9195          * @param {Roo.EventObject} e The raw event object
9196          */
9197             "beforeclick" : true,
9198         /**
9199          * @event click
9200          * Fires when a template node is clicked.
9201          * @param {Roo.View} this
9202          * @param {Number} index The index of the target node
9203          * @param {HTMLElement} node The target node
9204          * @param {Roo.EventObject} e The raw event object
9205          */
9206             "click" : true,
9207         /**
9208          * @event dblclick
9209          * Fires when a template node is double clicked.
9210          * @param {Roo.View} this
9211          * @param {Number} index The index of the target node
9212          * @param {HTMLElement} node The target node
9213          * @param {Roo.EventObject} e The raw event object
9214          */
9215             "dblclick" : true,
9216         /**
9217          * @event contextmenu
9218          * Fires when a template node is right clicked.
9219          * @param {Roo.View} this
9220          * @param {Number} index The index of the target node
9221          * @param {HTMLElement} node The target node
9222          * @param {Roo.EventObject} e The raw event object
9223          */
9224             "contextmenu" : true,
9225         /**
9226          * @event selectionchange
9227          * Fires when the selected nodes change.
9228          * @param {Roo.View} this
9229          * @param {Array} selections Array of the selected nodes
9230          */
9231             "selectionchange" : true,
9232     
9233         /**
9234          * @event beforeselect
9235          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9236          * @param {Roo.View} this
9237          * @param {HTMLElement} node The node to be selected
9238          * @param {Array} selections Array of currently selected nodes
9239          */
9240             "beforeselect" : true,
9241         /**
9242          * @event preparedata
9243          * Fires on every row to render, to allow you to change the data.
9244          * @param {Roo.View} this
9245          * @param {Object} data to be rendered (change this)
9246          */
9247           "preparedata" : true
9248         });
9249
9250     this.el.on({
9251         "click": this.onClick,
9252         "dblclick": this.onDblClick,
9253         "contextmenu": this.onContextMenu,
9254         scope:this
9255     });
9256
9257     this.selections = [];
9258     this.nodes = [];
9259     this.cmp = new Roo.CompositeElementLite([]);
9260     if(this.store){
9261         this.store = Roo.factory(this.store, Roo.data);
9262         this.setStore(this.store, true);
9263     }
9264     Roo.View.superclass.constructor.call(this);
9265 };
9266
9267 Roo.extend(Roo.View, Roo.util.Observable, {
9268     
9269      /**
9270      * @cfg {Roo.data.Store} store Data store to load data from.
9271      */
9272     store : false,
9273     
9274     /**
9275      * @cfg {String|Roo.Element} el The container element.
9276      */
9277     el : '',
9278     
9279     /**
9280      * @cfg {String|Roo.Template} tpl The template used by this View 
9281      */
9282     tpl : false,
9283     /**
9284      * @cfg {String} dataName the named area of the template to use as the data area
9285      *                          Works with domtemplates roo-name="name"
9286      */
9287     dataName: false,
9288     /**
9289      * @cfg {String} selectedClass The css class to add to selected nodes
9290      */
9291     selectedClass : "x-view-selected",
9292      /**
9293      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9294      */
9295     emptyText : "",
9296     
9297     /**
9298      * @cfg {String} text to display on mask (default Loading)
9299      */
9300     mask : false,
9301     /**
9302      * @cfg {Boolean} multiSelect Allow multiple selection
9303      */
9304     multiSelect : false,
9305     /**
9306      * @cfg {Boolean} singleSelect Allow single selection
9307      */
9308     singleSelect:  false,
9309     
9310     /**
9311      * @cfg {Boolean} toggleSelect - selecting 
9312      */
9313     toggleSelect : false,
9314     
9315     /**
9316      * Returns the element this view is bound to.
9317      * @return {Roo.Element}
9318      */
9319     getEl : function(){
9320         return this.el;
9321     },
9322
9323     /**
9324      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9325      */
9326     refresh : function(){
9327         var t = this.tpl;
9328         
9329         // if we are using something like 'domtemplate', then
9330         // the what gets used is:
9331         // t.applySubtemplate(NAME, data, wrapping data..)
9332         // the outer template then get' applied with
9333         //     the store 'extra data'
9334         // and the body get's added to the
9335         //      roo-name="data" node?
9336         //      <span class='roo-tpl-{name}'></span> ?????
9337         
9338         
9339         
9340         this.clearSelections();
9341         this.el.update("");
9342         var html = [];
9343         var records = this.store.getRange();
9344         if(records.length < 1) {
9345             
9346             // is this valid??  = should it render a template??
9347             
9348             this.el.update(this.emptyText);
9349             return;
9350         }
9351         var el = this.el;
9352         if (this.dataName) {
9353             this.el.update(t.apply(this.store.meta)); //????
9354             el = this.el.child('.roo-tpl-' + this.dataName);
9355         }
9356         
9357         for(var i = 0, len = records.length; i < len; i++){
9358             var data = this.prepareData(records[i].data, i, records[i]);
9359             this.fireEvent("preparedata", this, data, i, records[i]);
9360             html[html.length] = Roo.util.Format.trim(
9361                 this.dataName ?
9362                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9363                     t.apply(data)
9364             );
9365         }
9366         
9367         
9368         
9369         el.update(html.join(""));
9370         this.nodes = el.dom.childNodes;
9371         this.updateIndexes(0);
9372     },
9373
9374     /**
9375      * Function to override to reformat the data that is sent to
9376      * the template for each node.
9377      * DEPRICATED - use the preparedata event handler.
9378      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9379      * a JSON object for an UpdateManager bound view).
9380      */
9381     prepareData : function(data, index, record)
9382     {
9383         this.fireEvent("preparedata", this, data, index, record);
9384         return data;
9385     },
9386
9387     onUpdate : function(ds, record){
9388         this.clearSelections();
9389         var index = this.store.indexOf(record);
9390         var n = this.nodes[index];
9391         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9392         n.parentNode.removeChild(n);
9393         this.updateIndexes(index, index);
9394     },
9395
9396     
9397     
9398 // --------- FIXME     
9399     onAdd : function(ds, records, index)
9400     {
9401         this.clearSelections();
9402         if(this.nodes.length == 0){
9403             this.refresh();
9404             return;
9405         }
9406         var n = this.nodes[index];
9407         for(var i = 0, len = records.length; i < len; i++){
9408             var d = this.prepareData(records[i].data, i, records[i]);
9409             if(n){
9410                 this.tpl.insertBefore(n, d);
9411             }else{
9412                 
9413                 this.tpl.append(this.el, d);
9414             }
9415         }
9416         this.updateIndexes(index);
9417     },
9418
9419     onRemove : function(ds, record, index){
9420         this.clearSelections();
9421         var el = this.dataName  ?
9422             this.el.child('.roo-tpl-' + this.dataName) :
9423             this.el; 
9424         el.dom.removeChild(this.nodes[index]);
9425         this.updateIndexes(index);
9426     },
9427
9428     /**
9429      * Refresh an individual node.
9430      * @param {Number} index
9431      */
9432     refreshNode : function(index){
9433         this.onUpdate(this.store, this.store.getAt(index));
9434     },
9435
9436     updateIndexes : function(startIndex, endIndex){
9437         var ns = this.nodes;
9438         startIndex = startIndex || 0;
9439         endIndex = endIndex || ns.length - 1;
9440         for(var i = startIndex; i <= endIndex; i++){
9441             ns[i].nodeIndex = i;
9442         }
9443     },
9444
9445     /**
9446      * Changes the data store this view uses and refresh the view.
9447      * @param {Store} store
9448      */
9449     setStore : function(store, initial){
9450         if(!initial && this.store){
9451             this.store.un("datachanged", this.refresh);
9452             this.store.un("add", this.onAdd);
9453             this.store.un("remove", this.onRemove);
9454             this.store.un("update", this.onUpdate);
9455             this.store.un("clear", this.refresh);
9456             this.store.un("beforeload", this.onBeforeLoad);
9457             this.store.un("load", this.onLoad);
9458             this.store.un("loadexception", this.onLoad);
9459         }
9460         if(store){
9461           
9462             store.on("datachanged", this.refresh, this);
9463             store.on("add", this.onAdd, this);
9464             store.on("remove", this.onRemove, this);
9465             store.on("update", this.onUpdate, this);
9466             store.on("clear", this.refresh, this);
9467             store.on("beforeload", this.onBeforeLoad, this);
9468             store.on("load", this.onLoad, this);
9469             store.on("loadexception", this.onLoad, this);
9470         }
9471         
9472         if(store){
9473             this.refresh();
9474         }
9475     },
9476     /**
9477      * onbeforeLoad - masks the loading area.
9478      *
9479      */
9480     onBeforeLoad : function()
9481     {
9482         this.el.update("");
9483         this.el.mask(this.mask ? this.mask : "Loading" ); 
9484     },
9485     onLoad : function ()
9486     {
9487         this.el.unmask();
9488     },
9489     
9490
9491     /**
9492      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9493      * @param {HTMLElement} node
9494      * @return {HTMLElement} The template node
9495      */
9496     findItemFromChild : function(node){
9497         var el = this.dataName  ?
9498             this.el.child('.roo-tpl-' + this.dataName,true) :
9499             this.el.dom; 
9500         
9501         if(!node || node.parentNode == el){
9502                     return node;
9503             }
9504             var p = node.parentNode;
9505             while(p && p != el){
9506             if(p.parentNode == el){
9507                 return p;
9508             }
9509             p = p.parentNode;
9510         }
9511             return null;
9512     },
9513
9514     /** @ignore */
9515     onClick : function(e){
9516         var item = this.findItemFromChild(e.getTarget());
9517         if(item){
9518             var index = this.indexOf(item);
9519             if(this.onItemClick(item, index, e) !== false){
9520                 this.fireEvent("click", this, index, item, e);
9521             }
9522         }else{
9523             this.clearSelections();
9524         }
9525     },
9526
9527     /** @ignore */
9528     onContextMenu : function(e){
9529         var item = this.findItemFromChild(e.getTarget());
9530         if(item){
9531             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9532         }
9533     },
9534
9535     /** @ignore */
9536     onDblClick : function(e){
9537         var item = this.findItemFromChild(e.getTarget());
9538         if(item){
9539             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9540         }
9541     },
9542
9543     onItemClick : function(item, index, e)
9544     {
9545         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9546             return false;
9547         }
9548         if (this.toggleSelect) {
9549             var m = this.isSelected(item) ? 'unselect' : 'select';
9550             Roo.log(m);
9551             var _t = this;
9552             _t[m](item, true, false);
9553             return true;
9554         }
9555         if(this.multiSelect || this.singleSelect){
9556             if(this.multiSelect && e.shiftKey && this.lastSelection){
9557                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9558             }else{
9559                 this.select(item, this.multiSelect && e.ctrlKey);
9560                 this.lastSelection = item;
9561             }
9562             e.preventDefault();
9563         }
9564         return true;
9565     },
9566
9567     /**
9568      * Get the number of selected nodes.
9569      * @return {Number}
9570      */
9571     getSelectionCount : function(){
9572         return this.selections.length;
9573     },
9574
9575     /**
9576      * Get the currently selected nodes.
9577      * @return {Array} An array of HTMLElements
9578      */
9579     getSelectedNodes : function(){
9580         return this.selections;
9581     },
9582
9583     /**
9584      * Get the indexes of the selected nodes.
9585      * @return {Array}
9586      */
9587     getSelectedIndexes : function(){
9588         var indexes = [], s = this.selections;
9589         for(var i = 0, len = s.length; i < len; i++){
9590             indexes.push(s[i].nodeIndex);
9591         }
9592         return indexes;
9593     },
9594
9595     /**
9596      * Clear all selections
9597      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9598      */
9599     clearSelections : function(suppressEvent){
9600         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9601             this.cmp.elements = this.selections;
9602             this.cmp.removeClass(this.selectedClass);
9603             this.selections = [];
9604             if(!suppressEvent){
9605                 this.fireEvent("selectionchange", this, this.selections);
9606             }
9607         }
9608     },
9609
9610     /**
9611      * Returns true if the passed node is selected
9612      * @param {HTMLElement/Number} node The node or node index
9613      * @return {Boolean}
9614      */
9615     isSelected : function(node){
9616         var s = this.selections;
9617         if(s.length < 1){
9618             return false;
9619         }
9620         node = this.getNode(node);
9621         return s.indexOf(node) !== -1;
9622     },
9623
9624     /**
9625      * Selects nodes.
9626      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9627      * @param {Boolean} keepExisting (optional) true to keep existing selections
9628      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9629      */
9630     select : function(nodeInfo, keepExisting, suppressEvent){
9631         if(nodeInfo instanceof Array){
9632             if(!keepExisting){
9633                 this.clearSelections(true);
9634             }
9635             for(var i = 0, len = nodeInfo.length; i < len; i++){
9636                 this.select(nodeInfo[i], true, true);
9637             }
9638             return;
9639         } 
9640         var node = this.getNode(nodeInfo);
9641         if(!node || this.isSelected(node)){
9642             return; // already selected.
9643         }
9644         if(!keepExisting){
9645             this.clearSelections(true);
9646         }
9647         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9648             Roo.fly(node).addClass(this.selectedClass);
9649             this.selections.push(node);
9650             if(!suppressEvent){
9651                 this.fireEvent("selectionchange", this, this.selections);
9652             }
9653         }
9654         
9655         
9656     },
9657       /**
9658      * Unselects nodes.
9659      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9660      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9661      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9662      */
9663     unselect : function(nodeInfo, keepExisting, suppressEvent)
9664     {
9665         if(nodeInfo instanceof Array){
9666             Roo.each(this.selections, function(s) {
9667                 this.unselect(s, nodeInfo);
9668             }, this);
9669             return;
9670         }
9671         var node = this.getNode(nodeInfo);
9672         if(!node || !this.isSelected(node)){
9673             Roo.log("not selected");
9674             return; // not selected.
9675         }
9676         // fireevent???
9677         var ns = [];
9678         Roo.each(this.selections, function(s) {
9679             if (s == node ) {
9680                 Roo.fly(node).removeClass(this.selectedClass);
9681
9682                 return;
9683             }
9684             ns.push(s);
9685         },this);
9686         
9687         this.selections= ns;
9688         this.fireEvent("selectionchange", this, this.selections);
9689     },
9690
9691     /**
9692      * Gets a template node.
9693      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9694      * @return {HTMLElement} The node or null if it wasn't found
9695      */
9696     getNode : function(nodeInfo){
9697         if(typeof nodeInfo == "string"){
9698             return document.getElementById(nodeInfo);
9699         }else if(typeof nodeInfo == "number"){
9700             return this.nodes[nodeInfo];
9701         }
9702         return nodeInfo;
9703     },
9704
9705     /**
9706      * Gets a range template nodes.
9707      * @param {Number} startIndex
9708      * @param {Number} endIndex
9709      * @return {Array} An array of nodes
9710      */
9711     getNodes : function(start, end){
9712         var ns = this.nodes;
9713         start = start || 0;
9714         end = typeof end == "undefined" ? ns.length - 1 : end;
9715         var nodes = [];
9716         if(start <= end){
9717             for(var i = start; i <= end; i++){
9718                 nodes.push(ns[i]);
9719             }
9720         } else{
9721             for(var i = start; i >= end; i--){
9722                 nodes.push(ns[i]);
9723             }
9724         }
9725         return nodes;
9726     },
9727
9728     /**
9729      * Finds the index of the passed node
9730      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9731      * @return {Number} The index of the node or -1
9732      */
9733     indexOf : function(node){
9734         node = this.getNode(node);
9735         if(typeof node.nodeIndex == "number"){
9736             return node.nodeIndex;
9737         }
9738         var ns = this.nodes;
9739         for(var i = 0, len = ns.length; i < len; i++){
9740             if(ns[i] == node){
9741                 return i;
9742             }
9743         }
9744         return -1;
9745     }
9746 });
9747 /*
9748  * Based on:
9749  * Ext JS Library 1.1.1
9750  * Copyright(c) 2006-2007, Ext JS, LLC.
9751  *
9752  * Originally Released Under LGPL - original licence link has changed is not relivant.
9753  *
9754  * Fork - LGPL
9755  * <script type="text/javascript">
9756  */
9757
9758 /**
9759  * @class Roo.JsonView
9760  * @extends Roo.View
9761  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9762 <pre><code>
9763 var view = new Roo.JsonView({
9764     container: "my-element",
9765     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9766     multiSelect: true, 
9767     jsonRoot: "data" 
9768 });
9769
9770 // listen for node click?
9771 view.on("click", function(vw, index, node, e){
9772     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9773 });
9774
9775 // direct load of JSON data
9776 view.load("foobar.php");
9777
9778 // Example from my blog list
9779 var tpl = new Roo.Template(
9780     '&lt;div class="entry"&gt;' +
9781     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9782     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9783     "&lt;/div&gt;&lt;hr /&gt;"
9784 );
9785
9786 var moreView = new Roo.JsonView({
9787     container :  "entry-list", 
9788     template : tpl,
9789     jsonRoot: "posts"
9790 });
9791 moreView.on("beforerender", this.sortEntries, this);
9792 moreView.load({
9793     url: "/blog/get-posts.php",
9794     params: "allposts=true",
9795     text: "Loading Blog Entries..."
9796 });
9797 </code></pre>
9798
9799 * Note: old code is supported with arguments : (container, template, config)
9800
9801
9802  * @constructor
9803  * Create a new JsonView
9804  * 
9805  * @param {Object} config The config object
9806  * 
9807  */
9808 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9809     
9810     
9811     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9812
9813     var um = this.el.getUpdateManager();
9814     um.setRenderer(this);
9815     um.on("update", this.onLoad, this);
9816     um.on("failure", this.onLoadException, this);
9817
9818     /**
9819      * @event beforerender
9820      * Fires before rendering of the downloaded JSON data.
9821      * @param {Roo.JsonView} this
9822      * @param {Object} data The JSON data loaded
9823      */
9824     /**
9825      * @event load
9826      * Fires when data is loaded.
9827      * @param {Roo.JsonView} this
9828      * @param {Object} data The JSON data loaded
9829      * @param {Object} response The raw Connect response object
9830      */
9831     /**
9832      * @event loadexception
9833      * Fires when loading fails.
9834      * @param {Roo.JsonView} this
9835      * @param {Object} response The raw Connect response object
9836      */
9837     this.addEvents({
9838         'beforerender' : true,
9839         'load' : true,
9840         'loadexception' : true
9841     });
9842 };
9843 Roo.extend(Roo.JsonView, Roo.View, {
9844     /**
9845      * @type {String} The root property in the loaded JSON object that contains the data
9846      */
9847     jsonRoot : "",
9848
9849     /**
9850      * Refreshes the view.
9851      */
9852     refresh : function(){
9853         this.clearSelections();
9854         this.el.update("");
9855         var html = [];
9856         var o = this.jsonData;
9857         if(o && o.length > 0){
9858             for(var i = 0, len = o.length; i < len; i++){
9859                 var data = this.prepareData(o[i], i, o);
9860                 html[html.length] = this.tpl.apply(data);
9861             }
9862         }else{
9863             html.push(this.emptyText);
9864         }
9865         this.el.update(html.join(""));
9866         this.nodes = this.el.dom.childNodes;
9867         this.updateIndexes(0);
9868     },
9869
9870     /**
9871      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9872      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9873      <pre><code>
9874      view.load({
9875          url: "your-url.php",
9876          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9877          callback: yourFunction,
9878          scope: yourObject, //(optional scope)
9879          discardUrl: false,
9880          nocache: false,
9881          text: "Loading...",
9882          timeout: 30,
9883          scripts: false
9884      });
9885      </code></pre>
9886      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9887      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9888      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9889      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9890      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9891      */
9892     load : function(){
9893         var um = this.el.getUpdateManager();
9894         um.update.apply(um, arguments);
9895     },
9896
9897     render : function(el, response){
9898         this.clearSelections();
9899         this.el.update("");
9900         var o;
9901         try{
9902             o = Roo.util.JSON.decode(response.responseText);
9903             if(this.jsonRoot){
9904                 
9905                 o = o[this.jsonRoot];
9906             }
9907         } catch(e){
9908         }
9909         /**
9910          * The current JSON data or null
9911          */
9912         this.jsonData = o;
9913         this.beforeRender();
9914         this.refresh();
9915     },
9916
9917 /**
9918  * Get the number of records in the current JSON dataset
9919  * @return {Number}
9920  */
9921     getCount : function(){
9922         return this.jsonData ? this.jsonData.length : 0;
9923     },
9924
9925 /**
9926  * Returns the JSON object for the specified node(s)
9927  * @param {HTMLElement/Array} node The node or an array of nodes
9928  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9929  * you get the JSON object for the node
9930  */
9931     getNodeData : function(node){
9932         if(node instanceof Array){
9933             var data = [];
9934             for(var i = 0, len = node.length; i < len; i++){
9935                 data.push(this.getNodeData(node[i]));
9936             }
9937             return data;
9938         }
9939         return this.jsonData[this.indexOf(node)] || null;
9940     },
9941
9942     beforeRender : function(){
9943         this.snapshot = this.jsonData;
9944         if(this.sortInfo){
9945             this.sort.apply(this, this.sortInfo);
9946         }
9947         this.fireEvent("beforerender", this, this.jsonData);
9948     },
9949
9950     onLoad : function(el, o){
9951         this.fireEvent("load", this, this.jsonData, o);
9952     },
9953
9954     onLoadException : function(el, o){
9955         this.fireEvent("loadexception", this, o);
9956     },
9957
9958 /**
9959  * Filter the data by a specific property.
9960  * @param {String} property A property on your JSON objects
9961  * @param {String/RegExp} value Either string that the property values
9962  * should start with, or a RegExp to test against the property
9963  */
9964     filter : function(property, value){
9965         if(this.jsonData){
9966             var data = [];
9967             var ss = this.snapshot;
9968             if(typeof value == "string"){
9969                 var vlen = value.length;
9970                 if(vlen == 0){
9971                     this.clearFilter();
9972                     return;
9973                 }
9974                 value = value.toLowerCase();
9975                 for(var i = 0, len = ss.length; i < len; i++){
9976                     var o = ss[i];
9977                     if(o[property].substr(0, vlen).toLowerCase() == value){
9978                         data.push(o);
9979                     }
9980                 }
9981             } else if(value.exec){ // regex?
9982                 for(var i = 0, len = ss.length; i < len; i++){
9983                     var o = ss[i];
9984                     if(value.test(o[property])){
9985                         data.push(o);
9986                     }
9987                 }
9988             } else{
9989                 return;
9990             }
9991             this.jsonData = data;
9992             this.refresh();
9993         }
9994     },
9995
9996 /**
9997  * Filter by a function. The passed function will be called with each
9998  * object in the current dataset. If the function returns true the value is kept,
9999  * otherwise it is filtered.
10000  * @param {Function} fn
10001  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10002  */
10003     filterBy : function(fn, scope){
10004         if(this.jsonData){
10005             var data = [];
10006             var ss = this.snapshot;
10007             for(var i = 0, len = ss.length; i < len; i++){
10008                 var o = ss[i];
10009                 if(fn.call(scope || this, o)){
10010                     data.push(o);
10011                 }
10012             }
10013             this.jsonData = data;
10014             this.refresh();
10015         }
10016     },
10017
10018 /**
10019  * Clears the current filter.
10020  */
10021     clearFilter : function(){
10022         if(this.snapshot && this.jsonData != this.snapshot){
10023             this.jsonData = this.snapshot;
10024             this.refresh();
10025         }
10026     },
10027
10028
10029 /**
10030  * Sorts the data for this view and refreshes it.
10031  * @param {String} property A property on your JSON objects to sort on
10032  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10033  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10034  */
10035     sort : function(property, dir, sortType){
10036         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10037         if(this.jsonData){
10038             var p = property;
10039             var dsc = dir && dir.toLowerCase() == "desc";
10040             var f = function(o1, o2){
10041                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10042                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10043                 ;
10044                 if(v1 < v2){
10045                     return dsc ? +1 : -1;
10046                 } else if(v1 > v2){
10047                     return dsc ? -1 : +1;
10048                 } else{
10049                     return 0;
10050                 }
10051             };
10052             this.jsonData.sort(f);
10053             this.refresh();
10054             if(this.jsonData != this.snapshot){
10055                 this.snapshot.sort(f);
10056             }
10057         }
10058     }
10059 });/*
10060  * Based on:
10061  * Ext JS Library 1.1.1
10062  * Copyright(c) 2006-2007, Ext JS, LLC.
10063  *
10064  * Originally Released Under LGPL - original licence link has changed is not relivant.
10065  *
10066  * Fork - LGPL
10067  * <script type="text/javascript">
10068  */
10069  
10070
10071 /**
10072  * @class Roo.ColorPalette
10073  * @extends Roo.Component
10074  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10075  * Here's an example of typical usage:
10076  * <pre><code>
10077 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10078 cp.render('my-div');
10079
10080 cp.on('select', function(palette, selColor){
10081     // do something with selColor
10082 });
10083 </code></pre>
10084  * @constructor
10085  * Create a new ColorPalette
10086  * @param {Object} config The config object
10087  */
10088 Roo.ColorPalette = function(config){
10089     Roo.ColorPalette.superclass.constructor.call(this, config);
10090     this.addEvents({
10091         /**
10092              * @event select
10093              * Fires when a color is selected
10094              * @param {ColorPalette} this
10095              * @param {String} color The 6-digit color hex code (without the # symbol)
10096              */
10097         select: true
10098     });
10099
10100     if(this.handler){
10101         this.on("select", this.handler, this.scope, true);
10102     }
10103 };
10104 Roo.extend(Roo.ColorPalette, Roo.Component, {
10105     /**
10106      * @cfg {String} itemCls
10107      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10108      */
10109     itemCls : "x-color-palette",
10110     /**
10111      * @cfg {String} value
10112      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10113      * the hex codes are case-sensitive.
10114      */
10115     value : null,
10116     clickEvent:'click',
10117     // private
10118     ctype: "Roo.ColorPalette",
10119
10120     /**
10121      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10122      */
10123     allowReselect : false,
10124
10125     /**
10126      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10127      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10128      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10129      * of colors with the width setting until the box is symmetrical.</p>
10130      * <p>You can override individual colors if needed:</p>
10131      * <pre><code>
10132 var cp = new Roo.ColorPalette();
10133 cp.colors[0] = "FF0000";  // change the first box to red
10134 </code></pre>
10135
10136 Or you can provide a custom array of your own for complete control:
10137 <pre><code>
10138 var cp = new Roo.ColorPalette();
10139 cp.colors = ["000000", "993300", "333300"];
10140 </code></pre>
10141      * @type Array
10142      */
10143     colors : [
10144         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10145         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10146         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10147         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10148         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10149     ],
10150
10151     // private
10152     onRender : function(container, position){
10153         var t = new Roo.MasterTemplate(
10154             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10155         );
10156         var c = this.colors;
10157         for(var i = 0, len = c.length; i < len; i++){
10158             t.add([c[i]]);
10159         }
10160         var el = document.createElement("div");
10161         el.className = this.itemCls;
10162         t.overwrite(el);
10163         container.dom.insertBefore(el, position);
10164         this.el = Roo.get(el);
10165         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10166         if(this.clickEvent != 'click'){
10167             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10168         }
10169     },
10170
10171     // private
10172     afterRender : function(){
10173         Roo.ColorPalette.superclass.afterRender.call(this);
10174         if(this.value){
10175             var s = this.value;
10176             this.value = null;
10177             this.select(s);
10178         }
10179     },
10180
10181     // private
10182     handleClick : function(e, t){
10183         e.preventDefault();
10184         if(!this.disabled){
10185             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10186             this.select(c.toUpperCase());
10187         }
10188     },
10189
10190     /**
10191      * Selects the specified color in the palette (fires the select event)
10192      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10193      */
10194     select : function(color){
10195         color = color.replace("#", "");
10196         if(color != this.value || this.allowReselect){
10197             var el = this.el;
10198             if(this.value){
10199                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10200             }
10201             el.child("a.color-"+color).addClass("x-color-palette-sel");
10202             this.value = color;
10203             this.fireEvent("select", this, color);
10204         }
10205     }
10206 });/*
10207  * Based on:
10208  * Ext JS Library 1.1.1
10209  * Copyright(c) 2006-2007, Ext JS, LLC.
10210  *
10211  * Originally Released Under LGPL - original licence link has changed is not relivant.
10212  *
10213  * Fork - LGPL
10214  * <script type="text/javascript">
10215  */
10216  
10217 /**
10218  * @class Roo.DatePicker
10219  * @extends Roo.Component
10220  * Simple date picker class.
10221  * @constructor
10222  * Create a new DatePicker
10223  * @param {Object} config The config object
10224  */
10225 Roo.DatePicker = function(config){
10226     Roo.DatePicker.superclass.constructor.call(this, config);
10227
10228     this.value = config && config.value ?
10229                  config.value.clearTime() : new Date().clearTime();
10230
10231     this.addEvents({
10232         /**
10233              * @event select
10234              * Fires when a date is selected
10235              * @param {DatePicker} this
10236              * @param {Date} date The selected date
10237              */
10238         'select': true,
10239         /**
10240              * @event monthchange
10241              * Fires when the displayed month changes 
10242              * @param {DatePicker} this
10243              * @param {Date} date The selected month
10244              */
10245         'monthchange': true
10246     });
10247
10248     if(this.handler){
10249         this.on("select", this.handler,  this.scope || this);
10250     }
10251     // build the disabledDatesRE
10252     if(!this.disabledDatesRE && this.disabledDates){
10253         var dd = this.disabledDates;
10254         var re = "(?:";
10255         for(var i = 0; i < dd.length; i++){
10256             re += dd[i];
10257             if(i != dd.length-1) re += "|";
10258         }
10259         this.disabledDatesRE = new RegExp(re + ")");
10260     }
10261 };
10262
10263 Roo.extend(Roo.DatePicker, Roo.Component, {
10264     /**
10265      * @cfg {String} todayText
10266      * The text to display on the button that selects the current date (defaults to "Today")
10267      */
10268     todayText : "Today",
10269     /**
10270      * @cfg {String} okText
10271      * The text to display on the ok button
10272      */
10273     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10274     /**
10275      * @cfg {String} cancelText
10276      * The text to display on the cancel button
10277      */
10278     cancelText : "Cancel",
10279     /**
10280      * @cfg {String} todayTip
10281      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10282      */
10283     todayTip : "{0} (Spacebar)",
10284     /**
10285      * @cfg {Date} minDate
10286      * Minimum allowable date (JavaScript date object, defaults to null)
10287      */
10288     minDate : null,
10289     /**
10290      * @cfg {Date} maxDate
10291      * Maximum allowable date (JavaScript date object, defaults to null)
10292      */
10293     maxDate : null,
10294     /**
10295      * @cfg {String} minText
10296      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10297      */
10298     minText : "This date is before the minimum date",
10299     /**
10300      * @cfg {String} maxText
10301      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10302      */
10303     maxText : "This date is after the maximum date",
10304     /**
10305      * @cfg {String} format
10306      * The default date format string which can be overriden for localization support.  The format must be
10307      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10308      */
10309     format : "m/d/y",
10310     /**
10311      * @cfg {Array} disabledDays
10312      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10313      */
10314     disabledDays : null,
10315     /**
10316      * @cfg {String} disabledDaysText
10317      * The tooltip to display when the date falls on a disabled day (defaults to "")
10318      */
10319     disabledDaysText : "",
10320     /**
10321      * @cfg {RegExp} disabledDatesRE
10322      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10323      */
10324     disabledDatesRE : null,
10325     /**
10326      * @cfg {String} disabledDatesText
10327      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10328      */
10329     disabledDatesText : "",
10330     /**
10331      * @cfg {Boolean} constrainToViewport
10332      * True to constrain the date picker to the viewport (defaults to true)
10333      */
10334     constrainToViewport : true,
10335     /**
10336      * @cfg {Array} monthNames
10337      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10338      */
10339     monthNames : Date.monthNames,
10340     /**
10341      * @cfg {Array} dayNames
10342      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10343      */
10344     dayNames : Date.dayNames,
10345     /**
10346      * @cfg {String} nextText
10347      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10348      */
10349     nextText: 'Next Month (Control+Right)',
10350     /**
10351      * @cfg {String} prevText
10352      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10353      */
10354     prevText: 'Previous Month (Control+Left)',
10355     /**
10356      * @cfg {String} monthYearText
10357      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10358      */
10359     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10360     /**
10361      * @cfg {Number} startDay
10362      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10363      */
10364     startDay : 0,
10365     /**
10366      * @cfg {Bool} showClear
10367      * Show a clear button (usefull for date form elements that can be blank.)
10368      */
10369     
10370     showClear: false,
10371     
10372     /**
10373      * Sets the value of the date field
10374      * @param {Date} value The date to set
10375      */
10376     setValue : function(value){
10377         var old = this.value;
10378         
10379         if (typeof(value) == 'string') {
10380          
10381             value = Date.parseDate(value, this.format);
10382         }
10383         if (!value) {
10384             value = new Date();
10385         }
10386         
10387         this.value = value.clearTime(true);
10388         if(this.el){
10389             this.update(this.value);
10390         }
10391     },
10392
10393     /**
10394      * Gets the current selected value of the date field
10395      * @return {Date} The selected date
10396      */
10397     getValue : function(){
10398         return this.value;
10399     },
10400
10401     // private
10402     focus : function(){
10403         if(this.el){
10404             this.update(this.activeDate);
10405         }
10406     },
10407
10408     // privateval
10409     onRender : function(container, position){
10410         
10411         var m = [
10412              '<table cellspacing="0">',
10413                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
10414                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10415         var dn = this.dayNames;
10416         for(var i = 0; i < 7; i++){
10417             var d = this.startDay+i;
10418             if(d > 6){
10419                 d = d-7;
10420             }
10421             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10422         }
10423         m[m.length] = "</tr></thead><tbody><tr>";
10424         for(var i = 0; i < 42; i++) {
10425             if(i % 7 == 0 && i != 0){
10426                 m[m.length] = "</tr><tr>";
10427             }
10428             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10429         }
10430         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10431             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10432
10433         var el = document.createElement("div");
10434         el.className = "x-date-picker";
10435         el.innerHTML = m.join("");
10436
10437         container.dom.insertBefore(el, position);
10438
10439         this.el = Roo.get(el);
10440         this.eventEl = Roo.get(el.firstChild);
10441
10442         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10443             handler: this.showPrevMonth,
10444             scope: this,
10445             preventDefault:true,
10446             stopDefault:true
10447         });
10448
10449         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10450             handler: this.showNextMonth,
10451             scope: this,
10452             preventDefault:true,
10453             stopDefault:true
10454         });
10455
10456         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10457
10458         this.monthPicker = this.el.down('div.x-date-mp');
10459         this.monthPicker.enableDisplayMode('block');
10460         
10461         var kn = new Roo.KeyNav(this.eventEl, {
10462             "left" : function(e){
10463                 e.ctrlKey ?
10464                     this.showPrevMonth() :
10465                     this.update(this.activeDate.add("d", -1));
10466             },
10467
10468             "right" : function(e){
10469                 e.ctrlKey ?
10470                     this.showNextMonth() :
10471                     this.update(this.activeDate.add("d", 1));
10472             },
10473
10474             "up" : function(e){
10475                 e.ctrlKey ?
10476                     this.showNextYear() :
10477                     this.update(this.activeDate.add("d", -7));
10478             },
10479
10480             "down" : function(e){
10481                 e.ctrlKey ?
10482                     this.showPrevYear() :
10483                     this.update(this.activeDate.add("d", 7));
10484             },
10485
10486             "pageUp" : function(e){
10487                 this.showNextMonth();
10488             },
10489
10490             "pageDown" : function(e){
10491                 this.showPrevMonth();
10492             },
10493
10494             "enter" : function(e){
10495                 e.stopPropagation();
10496                 return true;
10497             },
10498
10499             scope : this
10500         });
10501
10502         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10503
10504         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10505
10506         this.el.unselectable();
10507         
10508         this.cells = this.el.select("table.x-date-inner tbody td");
10509         this.textNodes = this.el.query("table.x-date-inner tbody span");
10510
10511         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10512             text: "&#160;",
10513             tooltip: this.monthYearText
10514         });
10515
10516         this.mbtn.on('click', this.showMonthPicker, this);
10517         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10518
10519
10520         var today = (new Date()).dateFormat(this.format);
10521         
10522         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10523         if (this.showClear) {
10524             baseTb.add( new Roo.Toolbar.Fill());
10525         }
10526         baseTb.add({
10527             text: String.format(this.todayText, today),
10528             tooltip: String.format(this.todayTip, today),
10529             handler: this.selectToday,
10530             scope: this
10531         });
10532         
10533         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10534             
10535         //});
10536         if (this.showClear) {
10537             
10538             baseTb.add( new Roo.Toolbar.Fill());
10539             baseTb.add({
10540                 text: '&#160;',
10541                 cls: 'x-btn-icon x-btn-clear',
10542                 handler: function() {
10543                     //this.value = '';
10544                     this.fireEvent("select", this, '');
10545                 },
10546                 scope: this
10547             });
10548         }
10549         
10550         
10551         if(Roo.isIE){
10552             this.el.repaint();
10553         }
10554         this.update(this.value);
10555     },
10556
10557     createMonthPicker : function(){
10558         if(!this.monthPicker.dom.firstChild){
10559             var buf = ['<table border="0" cellspacing="0">'];
10560             for(var i = 0; i < 6; i++){
10561                 buf.push(
10562                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10563                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10564                     i == 0 ?
10565                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
10566                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10567                 );
10568             }
10569             buf.push(
10570                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10571                     this.okText,
10572                     '</button><button type="button" class="x-date-mp-cancel">',
10573                     this.cancelText,
10574                     '</button></td></tr>',
10575                 '</table>'
10576             );
10577             this.monthPicker.update(buf.join(''));
10578             this.monthPicker.on('click', this.onMonthClick, this);
10579             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10580
10581             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10582             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10583
10584             this.mpMonths.each(function(m, a, i){
10585                 i += 1;
10586                 if((i%2) == 0){
10587                     m.dom.xmonth = 5 + Math.round(i * .5);
10588                 }else{
10589                     m.dom.xmonth = Math.round((i-1) * .5);
10590                 }
10591             });
10592         }
10593     },
10594
10595     showMonthPicker : function(){
10596         this.createMonthPicker();
10597         var size = this.el.getSize();
10598         this.monthPicker.setSize(size);
10599         this.monthPicker.child('table').setSize(size);
10600
10601         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10602         this.updateMPMonth(this.mpSelMonth);
10603         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10604         this.updateMPYear(this.mpSelYear);
10605
10606         this.monthPicker.slideIn('t', {duration:.2});
10607     },
10608
10609     updateMPYear : function(y){
10610         this.mpyear = y;
10611         var ys = this.mpYears.elements;
10612         for(var i = 1; i <= 10; i++){
10613             var td = ys[i-1], y2;
10614             if((i%2) == 0){
10615                 y2 = y + Math.round(i * .5);
10616                 td.firstChild.innerHTML = y2;
10617                 td.xyear = y2;
10618             }else{
10619                 y2 = y - (5-Math.round(i * .5));
10620                 td.firstChild.innerHTML = y2;
10621                 td.xyear = y2;
10622             }
10623             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10624         }
10625     },
10626
10627     updateMPMonth : function(sm){
10628         this.mpMonths.each(function(m, a, i){
10629             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10630         });
10631     },
10632
10633     selectMPMonth: function(m){
10634         
10635     },
10636
10637     onMonthClick : function(e, t){
10638         e.stopEvent();
10639         var el = new Roo.Element(t), pn;
10640         if(el.is('button.x-date-mp-cancel')){
10641             this.hideMonthPicker();
10642         }
10643         else if(el.is('button.x-date-mp-ok')){
10644             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10645             this.hideMonthPicker();
10646         }
10647         else if(pn = el.up('td.x-date-mp-month', 2)){
10648             this.mpMonths.removeClass('x-date-mp-sel');
10649             pn.addClass('x-date-mp-sel');
10650             this.mpSelMonth = pn.dom.xmonth;
10651         }
10652         else if(pn = el.up('td.x-date-mp-year', 2)){
10653             this.mpYears.removeClass('x-date-mp-sel');
10654             pn.addClass('x-date-mp-sel');
10655             this.mpSelYear = pn.dom.xyear;
10656         }
10657         else if(el.is('a.x-date-mp-prev')){
10658             this.updateMPYear(this.mpyear-10);
10659         }
10660         else if(el.is('a.x-date-mp-next')){
10661             this.updateMPYear(this.mpyear+10);
10662         }
10663     },
10664
10665     onMonthDblClick : function(e, t){
10666         e.stopEvent();
10667         var el = new Roo.Element(t), pn;
10668         if(pn = el.up('td.x-date-mp-month', 2)){
10669             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10670             this.hideMonthPicker();
10671         }
10672         else if(pn = el.up('td.x-date-mp-year', 2)){
10673             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10674             this.hideMonthPicker();
10675         }
10676     },
10677
10678     hideMonthPicker : function(disableAnim){
10679         if(this.monthPicker){
10680             if(disableAnim === true){
10681                 this.monthPicker.hide();
10682             }else{
10683                 this.monthPicker.slideOut('t', {duration:.2});
10684             }
10685         }
10686     },
10687
10688     // private
10689     showPrevMonth : function(e){
10690         this.update(this.activeDate.add("mo", -1));
10691     },
10692
10693     // private
10694     showNextMonth : function(e){
10695         this.update(this.activeDate.add("mo", 1));
10696     },
10697
10698     // private
10699     showPrevYear : function(){
10700         this.update(this.activeDate.add("y", -1));
10701     },
10702
10703     // private
10704     showNextYear : function(){
10705         this.update(this.activeDate.add("y", 1));
10706     },
10707
10708     // private
10709     handleMouseWheel : function(e){
10710         var delta = e.getWheelDelta();
10711         if(delta > 0){
10712             this.showPrevMonth();
10713             e.stopEvent();
10714         } else if(delta < 0){
10715             this.showNextMonth();
10716             e.stopEvent();
10717         }
10718     },
10719
10720     // private
10721     handleDateClick : function(e, t){
10722         e.stopEvent();
10723         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10724             this.setValue(new Date(t.dateValue));
10725             this.fireEvent("select", this, this.value);
10726         }
10727     },
10728
10729     // private
10730     selectToday : function(){
10731         this.setValue(new Date().clearTime());
10732         this.fireEvent("select", this, this.value);
10733     },
10734
10735     // private
10736     update : function(date)
10737     {
10738         var vd = this.activeDate;
10739         this.activeDate = date;
10740         if(vd && this.el){
10741             var t = date.getTime();
10742             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10743                 this.cells.removeClass("x-date-selected");
10744                 this.cells.each(function(c){
10745                    if(c.dom.firstChild.dateValue == t){
10746                        c.addClass("x-date-selected");
10747                        setTimeout(function(){
10748                             try{c.dom.firstChild.focus();}catch(e){}
10749                        }, 50);
10750                        return false;
10751                    }
10752                 });
10753                 return;
10754             }
10755         }
10756         
10757         var days = date.getDaysInMonth();
10758         var firstOfMonth = date.getFirstDateOfMonth();
10759         var startingPos = firstOfMonth.getDay()-this.startDay;
10760
10761         if(startingPos <= this.startDay){
10762             startingPos += 7;
10763         }
10764
10765         var pm = date.add("mo", -1);
10766         var prevStart = pm.getDaysInMonth()-startingPos;
10767
10768         var cells = this.cells.elements;
10769         var textEls = this.textNodes;
10770         days += startingPos;
10771
10772         // convert everything to numbers so it's fast
10773         var day = 86400000;
10774         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10775         var today = new Date().clearTime().getTime();
10776         var sel = date.clearTime().getTime();
10777         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10778         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10779         var ddMatch = this.disabledDatesRE;
10780         var ddText = this.disabledDatesText;
10781         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10782         var ddaysText = this.disabledDaysText;
10783         var format = this.format;
10784
10785         var setCellClass = function(cal, cell){
10786             cell.title = "";
10787             var t = d.getTime();
10788             cell.firstChild.dateValue = t;
10789             if(t == today){
10790                 cell.className += " x-date-today";
10791                 cell.title = cal.todayText;
10792             }
10793             if(t == sel){
10794                 cell.className += " x-date-selected";
10795                 setTimeout(function(){
10796                     try{cell.firstChild.focus();}catch(e){}
10797                 }, 50);
10798             }
10799             // disabling
10800             if(t < min) {
10801                 cell.className = " x-date-disabled";
10802                 cell.title = cal.minText;
10803                 return;
10804             }
10805             if(t > max) {
10806                 cell.className = " x-date-disabled";
10807                 cell.title = cal.maxText;
10808                 return;
10809             }
10810             if(ddays){
10811                 if(ddays.indexOf(d.getDay()) != -1){
10812                     cell.title = ddaysText;
10813                     cell.className = " x-date-disabled";
10814                 }
10815             }
10816             if(ddMatch && format){
10817                 var fvalue = d.dateFormat(format);
10818                 if(ddMatch.test(fvalue)){
10819                     cell.title = ddText.replace("%0", fvalue);
10820                     cell.className = " x-date-disabled";
10821                 }
10822             }
10823         };
10824
10825         var i = 0;
10826         for(; i < startingPos; i++) {
10827             textEls[i].innerHTML = (++prevStart);
10828             d.setDate(d.getDate()+1);
10829             cells[i].className = "x-date-prevday";
10830             setCellClass(this, cells[i]);
10831         }
10832         for(; i < days; i++){
10833             intDay = i - startingPos + 1;
10834             textEls[i].innerHTML = (intDay);
10835             d.setDate(d.getDate()+1);
10836             cells[i].className = "x-date-active";
10837             setCellClass(this, cells[i]);
10838         }
10839         var extraDays = 0;
10840         for(; i < 42; i++) {
10841              textEls[i].innerHTML = (++extraDays);
10842              d.setDate(d.getDate()+1);
10843              cells[i].className = "x-date-nextday";
10844              setCellClass(this, cells[i]);
10845         }
10846
10847         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10848         this.fireEvent('monthchange', this, date);
10849         
10850         if(!this.internalRender){
10851             var main = this.el.dom.firstChild;
10852             var w = main.offsetWidth;
10853             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10854             Roo.fly(main).setWidth(w);
10855             this.internalRender = true;
10856             // opera does not respect the auto grow header center column
10857             // then, after it gets a width opera refuses to recalculate
10858             // without a second pass
10859             if(Roo.isOpera && !this.secondPass){
10860                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10861                 this.secondPass = true;
10862                 this.update.defer(10, this, [date]);
10863             }
10864         }
10865         
10866         
10867     }
10868 });        /*
10869  * Based on:
10870  * Ext JS Library 1.1.1
10871  * Copyright(c) 2006-2007, Ext JS, LLC.
10872  *
10873  * Originally Released Under LGPL - original licence link has changed is not relivant.
10874  *
10875  * Fork - LGPL
10876  * <script type="text/javascript">
10877  */
10878 /**
10879  * @class Roo.TabPanel
10880  * @extends Roo.util.Observable
10881  * A lightweight tab container.
10882  * <br><br>
10883  * Usage:
10884  * <pre><code>
10885 // basic tabs 1, built from existing content
10886 var tabs = new Roo.TabPanel("tabs1");
10887 tabs.addTab("script", "View Script");
10888 tabs.addTab("markup", "View Markup");
10889 tabs.activate("script");
10890
10891 // more advanced tabs, built from javascript
10892 var jtabs = new Roo.TabPanel("jtabs");
10893 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10894
10895 // set up the UpdateManager
10896 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10897 var updater = tab2.getUpdateManager();
10898 updater.setDefaultUrl("ajax1.htm");
10899 tab2.on('activate', updater.refresh, updater, true);
10900
10901 // Use setUrl for Ajax loading
10902 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10903 tab3.setUrl("ajax2.htm", null, true);
10904
10905 // Disabled tab
10906 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10907 tab4.disable();
10908
10909 jtabs.activate("jtabs-1");
10910  * </code></pre>
10911  * @constructor
10912  * Create a new TabPanel.
10913  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10914  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10915  */
10916 Roo.TabPanel = function(container, config){
10917     /**
10918     * The container element for this TabPanel.
10919     * @type Roo.Element
10920     */
10921     this.el = Roo.get(container, true);
10922     if(config){
10923         if(typeof config == "boolean"){
10924             this.tabPosition = config ? "bottom" : "top";
10925         }else{
10926             Roo.apply(this, config);
10927         }
10928     }
10929     if(this.tabPosition == "bottom"){
10930         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10931         this.el.addClass("x-tabs-bottom");
10932     }
10933     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10934     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10935     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10936     if(Roo.isIE){
10937         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10938     }
10939     if(this.tabPosition != "bottom"){
10940         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10941          * @type Roo.Element
10942          */
10943         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10944         this.el.addClass("x-tabs-top");
10945     }
10946     this.items = [];
10947
10948     this.bodyEl.setStyle("position", "relative");
10949
10950     this.active = null;
10951     this.activateDelegate = this.activate.createDelegate(this);
10952
10953     this.addEvents({
10954         /**
10955          * @event tabchange
10956          * Fires when the active tab changes
10957          * @param {Roo.TabPanel} this
10958          * @param {Roo.TabPanelItem} activePanel The new active tab
10959          */
10960         "tabchange": true,
10961         /**
10962          * @event beforetabchange
10963          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10964          * @param {Roo.TabPanel} this
10965          * @param {Object} e Set cancel to true on this object to cancel the tab change
10966          * @param {Roo.TabPanelItem} tab The tab being changed to
10967          */
10968         "beforetabchange" : true
10969     });
10970
10971     Roo.EventManager.onWindowResize(this.onResize, this);
10972     this.cpad = this.el.getPadding("lr");
10973     this.hiddenCount = 0;
10974
10975
10976     // toolbar on the tabbar support...
10977     if (this.toolbar) {
10978         var tcfg = this.toolbar;
10979         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10980         this.toolbar = new Roo.Toolbar(tcfg);
10981         if (Roo.isSafari) {
10982             var tbl = tcfg.container.child('table', true);
10983             tbl.setAttribute('width', '100%');
10984         }
10985         
10986     }
10987    
10988
10989
10990     Roo.TabPanel.superclass.constructor.call(this);
10991 };
10992
10993 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10994     /*
10995      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10996      */
10997     tabPosition : "top",
10998     /*
10999      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11000      */
11001     currentTabWidth : 0,
11002     /*
11003      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11004      */
11005     minTabWidth : 40,
11006     /*
11007      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11008      */
11009     maxTabWidth : 250,
11010     /*
11011      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11012      */
11013     preferredTabWidth : 175,
11014     /*
11015      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11016      */
11017     resizeTabs : false,
11018     /*
11019      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11020      */
11021     monitorResize : true,
11022     /*
11023      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11024      */
11025     toolbar : false,
11026
11027     /**
11028      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11029      * @param {String} id The id of the div to use <b>or create</b>
11030      * @param {String} text The text for the tab
11031      * @param {String} content (optional) Content to put in the TabPanelItem body
11032      * @param {Boolean} closable (optional) True to create a close icon on the tab
11033      * @return {Roo.TabPanelItem} The created TabPanelItem
11034      */
11035     addTab : function(id, text, content, closable){
11036         var item = new Roo.TabPanelItem(this, id, text, closable);
11037         this.addTabItem(item);
11038         if(content){
11039             item.setContent(content);
11040         }
11041         return item;
11042     },
11043
11044     /**
11045      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11046      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11047      * @return {Roo.TabPanelItem}
11048      */
11049     getTab : function(id){
11050         return this.items[id];
11051     },
11052
11053     /**
11054      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11055      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11056      */
11057     hideTab : function(id){
11058         var t = this.items[id];
11059         if(!t.isHidden()){
11060            t.setHidden(true);
11061            this.hiddenCount++;
11062            this.autoSizeTabs();
11063         }
11064     },
11065
11066     /**
11067      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11068      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11069      */
11070     unhideTab : function(id){
11071         var t = this.items[id];
11072         if(t.isHidden()){
11073            t.setHidden(false);
11074            this.hiddenCount--;
11075            this.autoSizeTabs();
11076         }
11077     },
11078
11079     /**
11080      * Adds an existing {@link Roo.TabPanelItem}.
11081      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11082      */
11083     addTabItem : function(item){
11084         this.items[item.id] = item;
11085         this.items.push(item);
11086         if(this.resizeTabs){
11087            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11088            this.autoSizeTabs();
11089         }else{
11090             item.autoSize();
11091         }
11092     },
11093
11094     /**
11095      * Removes a {@link Roo.TabPanelItem}.
11096      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11097      */
11098     removeTab : function(id){
11099         var items = this.items;
11100         var tab = items[id];
11101         if(!tab) { return; }
11102         var index = items.indexOf(tab);
11103         if(this.active == tab && items.length > 1){
11104             var newTab = this.getNextAvailable(index);
11105             if(newTab) {
11106                 newTab.activate();
11107             }
11108         }
11109         this.stripEl.dom.removeChild(tab.pnode.dom);
11110         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11111             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11112         }
11113         items.splice(index, 1);
11114         delete this.items[tab.id];
11115         tab.fireEvent("close", tab);
11116         tab.purgeListeners();
11117         this.autoSizeTabs();
11118     },
11119
11120     getNextAvailable : function(start){
11121         var items = this.items;
11122         var index = start;
11123         // look for a next tab that will slide over to
11124         // replace the one being removed
11125         while(index < items.length){
11126             var item = items[++index];
11127             if(item && !item.isHidden()){
11128                 return item;
11129             }
11130         }
11131         // if one isn't found select the previous tab (on the left)
11132         index = start;
11133         while(index >= 0){
11134             var item = items[--index];
11135             if(item && !item.isHidden()){
11136                 return item;
11137             }
11138         }
11139         return null;
11140     },
11141
11142     /**
11143      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11144      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11145      */
11146     disableTab : function(id){
11147         var tab = this.items[id];
11148         if(tab && this.active != tab){
11149             tab.disable();
11150         }
11151     },
11152
11153     /**
11154      * Enables a {@link Roo.TabPanelItem} that is disabled.
11155      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11156      */
11157     enableTab : function(id){
11158         var tab = this.items[id];
11159         tab.enable();
11160     },
11161
11162     /**
11163      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11164      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11165      * @return {Roo.TabPanelItem} The TabPanelItem.
11166      */
11167     activate : function(id){
11168         var tab = this.items[id];
11169         if(!tab){
11170             return null;
11171         }
11172         if(tab == this.active || tab.disabled){
11173             return tab;
11174         }
11175         var e = {};
11176         this.fireEvent("beforetabchange", this, e, tab);
11177         if(e.cancel !== true && !tab.disabled){
11178             if(this.active){
11179                 this.active.hide();
11180             }
11181             this.active = this.items[id];
11182             this.active.show();
11183             this.fireEvent("tabchange", this, this.active);
11184         }
11185         return tab;
11186     },
11187
11188     /**
11189      * Gets the active {@link Roo.TabPanelItem}.
11190      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11191      */
11192     getActiveTab : function(){
11193         return this.active;
11194     },
11195
11196     /**
11197      * Updates the tab body element to fit the height of the container element
11198      * for overflow scrolling
11199      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11200      */
11201     syncHeight : function(targetHeight){
11202         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11203         var bm = this.bodyEl.getMargins();
11204         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11205         this.bodyEl.setHeight(newHeight);
11206         return newHeight;
11207     },
11208
11209     onResize : function(){
11210         if(this.monitorResize){
11211             this.autoSizeTabs();
11212         }
11213     },
11214
11215     /**
11216      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11217      */
11218     beginUpdate : function(){
11219         this.updating = true;
11220     },
11221
11222     /**
11223      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11224      */
11225     endUpdate : function(){
11226         this.updating = false;
11227         this.autoSizeTabs();
11228     },
11229
11230     /**
11231      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11232      */
11233     autoSizeTabs : function(){
11234         var count = this.items.length;
11235         var vcount = count - this.hiddenCount;
11236         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11237         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11238         var availWidth = Math.floor(w / vcount);
11239         var b = this.stripBody;
11240         if(b.getWidth() > w){
11241             var tabs = this.items;
11242             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11243             if(availWidth < this.minTabWidth){
11244                 /*if(!this.sleft){    // incomplete scrolling code
11245                     this.createScrollButtons();
11246                 }
11247                 this.showScroll();
11248                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11249             }
11250         }else{
11251             if(this.currentTabWidth < this.preferredTabWidth){
11252                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11253             }
11254         }
11255     },
11256
11257     /**
11258      * Returns the number of tabs in this TabPanel.
11259      * @return {Number}
11260      */
11261      getCount : function(){
11262          return this.items.length;
11263      },
11264
11265     /**
11266      * Resizes all the tabs to the passed width
11267      * @param {Number} The new width
11268      */
11269     setTabWidth : function(width){
11270         this.currentTabWidth = width;
11271         for(var i = 0, len = this.items.length; i < len; i++) {
11272                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11273         }
11274     },
11275
11276     /**
11277      * Destroys this TabPanel
11278      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11279      */
11280     destroy : function(removeEl){
11281         Roo.EventManager.removeResizeListener(this.onResize, this);
11282         for(var i = 0, len = this.items.length; i < len; i++){
11283             this.items[i].purgeListeners();
11284         }
11285         if(removeEl === true){
11286             this.el.update("");
11287             this.el.remove();
11288         }
11289     }
11290 });
11291
11292 /**
11293  * @class Roo.TabPanelItem
11294  * @extends Roo.util.Observable
11295  * Represents an individual item (tab plus body) in a TabPanel.
11296  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11297  * @param {String} id The id of this TabPanelItem
11298  * @param {String} text The text for the tab of this TabPanelItem
11299  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11300  */
11301 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11302     /**
11303      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11304      * @type Roo.TabPanel
11305      */
11306     this.tabPanel = tabPanel;
11307     /**
11308      * The id for this TabPanelItem
11309      * @type String
11310      */
11311     this.id = id;
11312     /** @private */
11313     this.disabled = false;
11314     /** @private */
11315     this.text = text;
11316     /** @private */
11317     this.loaded = false;
11318     this.closable = closable;
11319
11320     /**
11321      * The body element for this TabPanelItem.
11322      * @type Roo.Element
11323      */
11324     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11325     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11326     this.bodyEl.setStyle("display", "block");
11327     this.bodyEl.setStyle("zoom", "1");
11328     this.hideAction();
11329
11330     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11331     /** @private */
11332     this.el = Roo.get(els.el, true);
11333     this.inner = Roo.get(els.inner, true);
11334     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11335     this.pnode = Roo.get(els.el.parentNode, true);
11336     this.el.on("mousedown", this.onTabMouseDown, this);
11337     this.el.on("click", this.onTabClick, this);
11338     /** @private */
11339     if(closable){
11340         var c = Roo.get(els.close, true);
11341         c.dom.title = this.closeText;
11342         c.addClassOnOver("close-over");
11343         c.on("click", this.closeClick, this);
11344      }
11345
11346     this.addEvents({
11347          /**
11348          * @event activate
11349          * Fires when this tab becomes the active tab.
11350          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11351          * @param {Roo.TabPanelItem} this
11352          */
11353         "activate": true,
11354         /**
11355          * @event beforeclose
11356          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11357          * @param {Roo.TabPanelItem} this
11358          * @param {Object} e Set cancel to true on this object to cancel the close.
11359          */
11360         "beforeclose": true,
11361         /**
11362          * @event close
11363          * Fires when this tab is closed.
11364          * @param {Roo.TabPanelItem} this
11365          */
11366          "close": true,
11367         /**
11368          * @event deactivate
11369          * Fires when this tab is no longer the active tab.
11370          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11371          * @param {Roo.TabPanelItem} this
11372          */
11373          "deactivate" : true
11374     });
11375     this.hidden = false;
11376
11377     Roo.TabPanelItem.superclass.constructor.call(this);
11378 };
11379
11380 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11381     purgeListeners : function(){
11382        Roo.util.Observable.prototype.purgeListeners.call(this);
11383        this.el.removeAllListeners();
11384     },
11385     /**
11386      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11387      */
11388     show : function(){
11389         this.pnode.addClass("on");
11390         this.showAction();
11391         if(Roo.isOpera){
11392             this.tabPanel.stripWrap.repaint();
11393         }
11394         this.fireEvent("activate", this.tabPanel, this);
11395     },
11396
11397     /**
11398      * Returns true if this tab is the active tab.
11399      * @return {Boolean}
11400      */
11401     isActive : function(){
11402         return this.tabPanel.getActiveTab() == this;
11403     },
11404
11405     /**
11406      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11407      */
11408     hide : function(){
11409         this.pnode.removeClass("on");
11410         this.hideAction();
11411         this.fireEvent("deactivate", this.tabPanel, this);
11412     },
11413
11414     hideAction : function(){
11415         this.bodyEl.hide();
11416         this.bodyEl.setStyle("position", "absolute");
11417         this.bodyEl.setLeft("-20000px");
11418         this.bodyEl.setTop("-20000px");
11419     },
11420
11421     showAction : function(){
11422         this.bodyEl.setStyle("position", "relative");
11423         this.bodyEl.setTop("");
11424         this.bodyEl.setLeft("");
11425         this.bodyEl.show();
11426     },
11427
11428     /**
11429      * Set the tooltip for the tab.
11430      * @param {String} tooltip The tab's tooltip
11431      */
11432     setTooltip : function(text){
11433         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11434             this.textEl.dom.qtip = text;
11435             this.textEl.dom.removeAttribute('title');
11436         }else{
11437             this.textEl.dom.title = text;
11438         }
11439     },
11440
11441     onTabClick : function(e){
11442         e.preventDefault();
11443         this.tabPanel.activate(this.id);
11444     },
11445
11446     onTabMouseDown : function(e){
11447         e.preventDefault();
11448         this.tabPanel.activate(this.id);
11449     },
11450
11451     getWidth : function(){
11452         return this.inner.getWidth();
11453     },
11454
11455     setWidth : function(width){
11456         var iwidth = width - this.pnode.getPadding("lr");
11457         this.inner.setWidth(iwidth);
11458         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11459         this.pnode.setWidth(width);
11460     },
11461
11462     /**
11463      * Show or hide the tab
11464      * @param {Boolean} hidden True to hide or false to show.
11465      */
11466     setHidden : function(hidden){
11467         this.hidden = hidden;
11468         this.pnode.setStyle("display", hidden ? "none" : "");
11469     },
11470
11471     /**
11472      * Returns true if this tab is "hidden"
11473      * @return {Boolean}
11474      */
11475     isHidden : function(){
11476         return this.hidden;
11477     },
11478
11479     /**
11480      * Returns the text for this tab
11481      * @return {String}
11482      */
11483     getText : function(){
11484         return this.text;
11485     },
11486
11487     autoSize : function(){
11488         //this.el.beginMeasure();
11489         this.textEl.setWidth(1);
11490         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11491         //this.el.endMeasure();
11492     },
11493
11494     /**
11495      * Sets the text for the tab (Note: this also sets the tooltip text)
11496      * @param {String} text The tab's text and tooltip
11497      */
11498     setText : function(text){
11499         this.text = text;
11500         this.textEl.update(text);
11501         this.setTooltip(text);
11502         if(!this.tabPanel.resizeTabs){
11503             this.autoSize();
11504         }
11505     },
11506     /**
11507      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11508      */
11509     activate : function(){
11510         this.tabPanel.activate(this.id);
11511     },
11512
11513     /**
11514      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11515      */
11516     disable : function(){
11517         if(this.tabPanel.active != this){
11518             this.disabled = true;
11519             this.pnode.addClass("disabled");
11520         }
11521     },
11522
11523     /**
11524      * Enables this TabPanelItem if it was previously disabled.
11525      */
11526     enable : function(){
11527         this.disabled = false;
11528         this.pnode.removeClass("disabled");
11529     },
11530
11531     /**
11532      * Sets the content for this TabPanelItem.
11533      * @param {String} content The content
11534      * @param {Boolean} loadScripts true to look for and load scripts
11535      */
11536     setContent : function(content, loadScripts){
11537         this.bodyEl.update(content, loadScripts);
11538     },
11539
11540     /**
11541      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11542      * @return {Roo.UpdateManager} The UpdateManager
11543      */
11544     getUpdateManager : function(){
11545         return this.bodyEl.getUpdateManager();
11546     },
11547
11548     /**
11549      * Set a URL to be used to load the content for this TabPanelItem.
11550      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11551      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11552      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11553      * @return {Roo.UpdateManager} The UpdateManager
11554      */
11555     setUrl : function(url, params, loadOnce){
11556         if(this.refreshDelegate){
11557             this.un('activate', this.refreshDelegate);
11558         }
11559         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11560         this.on("activate", this.refreshDelegate);
11561         return this.bodyEl.getUpdateManager();
11562     },
11563
11564     /** @private */
11565     _handleRefresh : function(url, params, loadOnce){
11566         if(!loadOnce || !this.loaded){
11567             var updater = this.bodyEl.getUpdateManager();
11568             updater.update(url, params, this._setLoaded.createDelegate(this));
11569         }
11570     },
11571
11572     /**
11573      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11574      *   Will fail silently if the setUrl method has not been called.
11575      *   This does not activate the panel, just updates its content.
11576      */
11577     refresh : function(){
11578         if(this.refreshDelegate){
11579            this.loaded = false;
11580            this.refreshDelegate();
11581         }
11582     },
11583
11584     /** @private */
11585     _setLoaded : function(){
11586         this.loaded = true;
11587     },
11588
11589     /** @private */
11590     closeClick : function(e){
11591         var o = {};
11592         e.stopEvent();
11593         this.fireEvent("beforeclose", this, o);
11594         if(o.cancel !== true){
11595             this.tabPanel.removeTab(this.id);
11596         }
11597     },
11598     /**
11599      * The text displayed in the tooltip for the close icon.
11600      * @type String
11601      */
11602     closeText : "Close this tab"
11603 });
11604
11605 /** @private */
11606 Roo.TabPanel.prototype.createStrip = function(container){
11607     var strip = document.createElement("div");
11608     strip.className = "x-tabs-wrap";
11609     container.appendChild(strip);
11610     return strip;
11611 };
11612 /** @private */
11613 Roo.TabPanel.prototype.createStripList = function(strip){
11614     // div wrapper for retard IE
11615     // returns the "tr" element.
11616     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11617         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11618         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11619     return strip.firstChild.firstChild.firstChild.firstChild;
11620 };
11621 /** @private */
11622 Roo.TabPanel.prototype.createBody = function(container){
11623     var body = document.createElement("div");
11624     Roo.id(body, "tab-body");
11625     Roo.fly(body).addClass("x-tabs-body");
11626     container.appendChild(body);
11627     return body;
11628 };
11629 /** @private */
11630 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11631     var body = Roo.getDom(id);
11632     if(!body){
11633         body = document.createElement("div");
11634         body.id = id;
11635     }
11636     Roo.fly(body).addClass("x-tabs-item-body");
11637     bodyEl.insertBefore(body, bodyEl.firstChild);
11638     return body;
11639 };
11640 /** @private */
11641 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11642     var td = document.createElement("td");
11643     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11644     //stripEl.appendChild(td);
11645     if(closable){
11646         td.className = "x-tabs-closable";
11647         if(!this.closeTpl){
11648             this.closeTpl = new Roo.Template(
11649                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11650                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11651                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11652             );
11653         }
11654         var el = this.closeTpl.overwrite(td, {"text": text});
11655         var close = el.getElementsByTagName("div")[0];
11656         var inner = el.getElementsByTagName("em")[0];
11657         return {"el": el, "close": close, "inner": inner};
11658     } else {
11659         if(!this.tabTpl){
11660             this.tabTpl = new Roo.Template(
11661                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11662                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11663             );
11664         }
11665         var el = this.tabTpl.overwrite(td, {"text": text});
11666         var inner = el.getElementsByTagName("em")[0];
11667         return {"el": el, "inner": inner};
11668     }
11669 };/*
11670  * Based on:
11671  * Ext JS Library 1.1.1
11672  * Copyright(c) 2006-2007, Ext JS, LLC.
11673  *
11674  * Originally Released Under LGPL - original licence link has changed is not relivant.
11675  *
11676  * Fork - LGPL
11677  * <script type="text/javascript">
11678  */
11679
11680 /**
11681  * @class Roo.Button
11682  * @extends Roo.util.Observable
11683  * Simple Button class
11684  * @cfg {String} text The button text
11685  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11686  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11687  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11688  * @cfg {Object} scope The scope of the handler
11689  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11690  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11691  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11692  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11693  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11694  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11695    applies if enableToggle = true)
11696  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11697  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11698   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11699  * @constructor
11700  * Create a new button
11701  * @param {Object} config The config object
11702  */
11703 Roo.Button = function(renderTo, config)
11704 {
11705     if (!config) {
11706         config = renderTo;
11707         renderTo = config.renderTo || false;
11708     }
11709     
11710     Roo.apply(this, config);
11711     this.addEvents({
11712         /**
11713              * @event click
11714              * Fires when this button is clicked
11715              * @param {Button} this
11716              * @param {EventObject} e The click event
11717              */
11718             "click" : true,
11719         /**
11720              * @event toggle
11721              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11722              * @param {Button} this
11723              * @param {Boolean} pressed
11724              */
11725             "toggle" : true,
11726         /**
11727              * @event mouseover
11728              * Fires when the mouse hovers over the button
11729              * @param {Button} this
11730              * @param {Event} e The event object
11731              */
11732         'mouseover' : true,
11733         /**
11734              * @event mouseout
11735              * Fires when the mouse exits the button
11736              * @param {Button} this
11737              * @param {Event} e The event object
11738              */
11739         'mouseout': true,
11740          /**
11741              * @event render
11742              * Fires when the button is rendered
11743              * @param {Button} this
11744              */
11745         'render': true
11746     });
11747     if(this.menu){
11748         this.menu = Roo.menu.MenuMgr.get(this.menu);
11749     }
11750     // register listeners first!!  - so render can be captured..
11751     Roo.util.Observable.call(this);
11752     if(renderTo){
11753         this.render(renderTo);
11754     }
11755     
11756   
11757 };
11758
11759 Roo.extend(Roo.Button, Roo.util.Observable, {
11760     /**
11761      * 
11762      */
11763     
11764     /**
11765      * Read-only. True if this button is hidden
11766      * @type Boolean
11767      */
11768     hidden : false,
11769     /**
11770      * Read-only. True if this button is disabled
11771      * @type Boolean
11772      */
11773     disabled : false,
11774     /**
11775      * Read-only. True if this button is pressed (only if enableToggle = true)
11776      * @type Boolean
11777      */
11778     pressed : false,
11779
11780     /**
11781      * @cfg {Number} tabIndex 
11782      * The DOM tabIndex for this button (defaults to undefined)
11783      */
11784     tabIndex : undefined,
11785
11786     /**
11787      * @cfg {Boolean} enableToggle
11788      * True to enable pressed/not pressed toggling (defaults to false)
11789      */
11790     enableToggle: false,
11791     /**
11792      * @cfg {Mixed} menu
11793      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11794      */
11795     menu : undefined,
11796     /**
11797      * @cfg {String} menuAlign
11798      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11799      */
11800     menuAlign : "tl-bl?",
11801
11802     /**
11803      * @cfg {String} iconCls
11804      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11805      */
11806     iconCls : undefined,
11807     /**
11808      * @cfg {String} type
11809      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11810      */
11811     type : 'button',
11812
11813     // private
11814     menuClassTarget: 'tr',
11815
11816     /**
11817      * @cfg {String} clickEvent
11818      * The type of event to map to the button's event handler (defaults to 'click')
11819      */
11820     clickEvent : 'click',
11821
11822     /**
11823      * @cfg {Boolean} handleMouseEvents
11824      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11825      */
11826     handleMouseEvents : true,
11827
11828     /**
11829      * @cfg {String} tooltipType
11830      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11831      */
11832     tooltipType : 'qtip',
11833
11834     /**
11835      * @cfg {String} cls
11836      * A CSS class to apply to the button's main element.
11837      */
11838     
11839     /**
11840      * @cfg {Roo.Template} template (Optional)
11841      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11842      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11843      * require code modifications if required elements (e.g. a button) aren't present.
11844      */
11845
11846     // private
11847     render : function(renderTo){
11848         var btn;
11849         if(this.hideParent){
11850             this.parentEl = Roo.get(renderTo);
11851         }
11852         if(!this.dhconfig){
11853             if(!this.template){
11854                 if(!Roo.Button.buttonTemplate){
11855                     // hideous table template
11856                     Roo.Button.buttonTemplate = new Roo.Template(
11857                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11858                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11859                         "</tr></tbody></table>");
11860                 }
11861                 this.template = Roo.Button.buttonTemplate;
11862             }
11863             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11864             var btnEl = btn.child("button:first");
11865             btnEl.on('focus', this.onFocus, this);
11866             btnEl.on('blur', this.onBlur, this);
11867             if(this.cls){
11868                 btn.addClass(this.cls);
11869             }
11870             if(this.icon){
11871                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11872             }
11873             if(this.iconCls){
11874                 btnEl.addClass(this.iconCls);
11875                 if(!this.cls){
11876                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11877                 }
11878             }
11879             if(this.tabIndex !== undefined){
11880                 btnEl.dom.tabIndex = this.tabIndex;
11881             }
11882             if(this.tooltip){
11883                 if(typeof this.tooltip == 'object'){
11884                     Roo.QuickTips.tips(Roo.apply({
11885                           target: btnEl.id
11886                     }, this.tooltip));
11887                 } else {
11888                     btnEl.dom[this.tooltipType] = this.tooltip;
11889                 }
11890             }
11891         }else{
11892             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11893         }
11894         this.el = btn;
11895         if(this.id){
11896             this.el.dom.id = this.el.id = this.id;
11897         }
11898         if(this.menu){
11899             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11900             this.menu.on("show", this.onMenuShow, this);
11901             this.menu.on("hide", this.onMenuHide, this);
11902         }
11903         btn.addClass("x-btn");
11904         if(Roo.isIE && !Roo.isIE7){
11905             this.autoWidth.defer(1, this);
11906         }else{
11907             this.autoWidth();
11908         }
11909         if(this.handleMouseEvents){
11910             btn.on("mouseover", this.onMouseOver, this);
11911             btn.on("mouseout", this.onMouseOut, this);
11912             btn.on("mousedown", this.onMouseDown, this);
11913         }
11914         btn.on(this.clickEvent, this.onClick, this);
11915         //btn.on("mouseup", this.onMouseUp, this);
11916         if(this.hidden){
11917             this.hide();
11918         }
11919         if(this.disabled){
11920             this.disable();
11921         }
11922         Roo.ButtonToggleMgr.register(this);
11923         if(this.pressed){
11924             this.el.addClass("x-btn-pressed");
11925         }
11926         if(this.repeat){
11927             var repeater = new Roo.util.ClickRepeater(btn,
11928                 typeof this.repeat == "object" ? this.repeat : {}
11929             );
11930             repeater.on("click", this.onClick,  this);
11931         }
11932         
11933         this.fireEvent('render', this);
11934         
11935     },
11936     /**
11937      * Returns the button's underlying element
11938      * @return {Roo.Element} The element
11939      */
11940     getEl : function(){
11941         return this.el;  
11942     },
11943     
11944     /**
11945      * Destroys this Button and removes any listeners.
11946      */
11947     destroy : function(){
11948         Roo.ButtonToggleMgr.unregister(this);
11949         this.el.removeAllListeners();
11950         this.purgeListeners();
11951         this.el.remove();
11952     },
11953
11954     // private
11955     autoWidth : function(){
11956         if(this.el){
11957             this.el.setWidth("auto");
11958             if(Roo.isIE7 && Roo.isStrict){
11959                 var ib = this.el.child('button');
11960                 if(ib && ib.getWidth() > 20){
11961                     ib.clip();
11962                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11963                 }
11964             }
11965             if(this.minWidth){
11966                 if(this.hidden){
11967                     this.el.beginMeasure();
11968                 }
11969                 if(this.el.getWidth() < this.minWidth){
11970                     this.el.setWidth(this.minWidth);
11971                 }
11972                 if(this.hidden){
11973                     this.el.endMeasure();
11974                 }
11975             }
11976         }
11977     },
11978
11979     /**
11980      * Assigns this button's click handler
11981      * @param {Function} handler The function to call when the button is clicked
11982      * @param {Object} scope (optional) Scope for the function passed in
11983      */
11984     setHandler : function(handler, scope){
11985         this.handler = handler;
11986         this.scope = scope;  
11987     },
11988     
11989     /**
11990      * Sets this button's text
11991      * @param {String} text The button text
11992      */
11993     setText : function(text){
11994         this.text = text;
11995         if(this.el){
11996             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11997         }
11998         this.autoWidth();
11999     },
12000     
12001     /**
12002      * Gets the text for this button
12003      * @return {String} The button text
12004      */
12005     getText : function(){
12006         return this.text;  
12007     },
12008     
12009     /**
12010      * Show this button
12011      */
12012     show: function(){
12013         this.hidden = false;
12014         if(this.el){
12015             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12016         }
12017     },
12018     
12019     /**
12020      * Hide this button
12021      */
12022     hide: function(){
12023         this.hidden = true;
12024         if(this.el){
12025             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12026         }
12027     },
12028     
12029     /**
12030      * Convenience function for boolean show/hide
12031      * @param {Boolean} visible True to show, false to hide
12032      */
12033     setVisible: function(visible){
12034         if(visible) {
12035             this.show();
12036         }else{
12037             this.hide();
12038         }
12039     },
12040     
12041     /**
12042      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12043      * @param {Boolean} state (optional) Force a particular state
12044      */
12045     toggle : function(state){
12046         state = state === undefined ? !this.pressed : state;
12047         if(state != this.pressed){
12048             if(state){
12049                 this.el.addClass("x-btn-pressed");
12050                 this.pressed = true;
12051                 this.fireEvent("toggle", this, true);
12052             }else{
12053                 this.el.removeClass("x-btn-pressed");
12054                 this.pressed = false;
12055                 this.fireEvent("toggle", this, false);
12056             }
12057             if(this.toggleHandler){
12058                 this.toggleHandler.call(this.scope || this, this, state);
12059             }
12060         }
12061     },
12062     
12063     /**
12064      * Focus the button
12065      */
12066     focus : function(){
12067         this.el.child('button:first').focus();
12068     },
12069     
12070     /**
12071      * Disable this button
12072      */
12073     disable : function(){
12074         if(this.el){
12075             this.el.addClass("x-btn-disabled");
12076         }
12077         this.disabled = true;
12078     },
12079     
12080     /**
12081      * Enable this button
12082      */
12083     enable : function(){
12084         if(this.el){
12085             this.el.removeClass("x-btn-disabled");
12086         }
12087         this.disabled = false;
12088     },
12089
12090     /**
12091      * Convenience function for boolean enable/disable
12092      * @param {Boolean} enabled True to enable, false to disable
12093      */
12094     setDisabled : function(v){
12095         this[v !== true ? "enable" : "disable"]();
12096     },
12097
12098     // private
12099     onClick : function(e){
12100         if(e){
12101             e.preventDefault();
12102         }
12103         if(e.button != 0){
12104             return;
12105         }
12106         if(!this.disabled){
12107             if(this.enableToggle){
12108                 this.toggle();
12109             }
12110             if(this.menu && !this.menu.isVisible()){
12111                 this.menu.show(this.el, this.menuAlign);
12112             }
12113             this.fireEvent("click", this, e);
12114             if(this.handler){
12115                 this.el.removeClass("x-btn-over");
12116                 this.handler.call(this.scope || this, this, e);
12117             }
12118         }
12119     },
12120     // private
12121     onMouseOver : function(e){
12122         if(!this.disabled){
12123             this.el.addClass("x-btn-over");
12124             this.fireEvent('mouseover', this, e);
12125         }
12126     },
12127     // private
12128     onMouseOut : function(e){
12129         if(!e.within(this.el,  true)){
12130             this.el.removeClass("x-btn-over");
12131             this.fireEvent('mouseout', this, e);
12132         }
12133     },
12134     // private
12135     onFocus : function(e){
12136         if(!this.disabled){
12137             this.el.addClass("x-btn-focus");
12138         }
12139     },
12140     // private
12141     onBlur : function(e){
12142         this.el.removeClass("x-btn-focus");
12143     },
12144     // private
12145     onMouseDown : function(e){
12146         if(!this.disabled && e.button == 0){
12147             this.el.addClass("x-btn-click");
12148             Roo.get(document).on('mouseup', this.onMouseUp, this);
12149         }
12150     },
12151     // private
12152     onMouseUp : function(e){
12153         if(e.button == 0){
12154             this.el.removeClass("x-btn-click");
12155             Roo.get(document).un('mouseup', this.onMouseUp, this);
12156         }
12157     },
12158     // private
12159     onMenuShow : function(e){
12160         this.el.addClass("x-btn-menu-active");
12161     },
12162     // private
12163     onMenuHide : function(e){
12164         this.el.removeClass("x-btn-menu-active");
12165     }   
12166 });
12167
12168 // Private utility class used by Button
12169 Roo.ButtonToggleMgr = function(){
12170    var groups = {};
12171    
12172    function toggleGroup(btn, state){
12173        if(state){
12174            var g = groups[btn.toggleGroup];
12175            for(var i = 0, l = g.length; i < l; i++){
12176                if(g[i] != btn){
12177                    g[i].toggle(false);
12178                }
12179            }
12180        }
12181    }
12182    
12183    return {
12184        register : function(btn){
12185            if(!btn.toggleGroup){
12186                return;
12187            }
12188            var g = groups[btn.toggleGroup];
12189            if(!g){
12190                g = groups[btn.toggleGroup] = [];
12191            }
12192            g.push(btn);
12193            btn.on("toggle", toggleGroup);
12194        },
12195        
12196        unregister : function(btn){
12197            if(!btn.toggleGroup){
12198                return;
12199            }
12200            var g = groups[btn.toggleGroup];
12201            if(g){
12202                g.remove(btn);
12203                btn.un("toggle", toggleGroup);
12204            }
12205        }
12206    };
12207 }();/*
12208  * Based on:
12209  * Ext JS Library 1.1.1
12210  * Copyright(c) 2006-2007, Ext JS, LLC.
12211  *
12212  * Originally Released Under LGPL - original licence link has changed is not relivant.
12213  *
12214  * Fork - LGPL
12215  * <script type="text/javascript">
12216  */
12217  
12218 /**
12219  * @class Roo.SplitButton
12220  * @extends Roo.Button
12221  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12222  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12223  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12224  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12225  * @cfg {String} arrowTooltip The title attribute of the arrow
12226  * @constructor
12227  * Create a new menu button
12228  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12229  * @param {Object} config The config object
12230  */
12231 Roo.SplitButton = function(renderTo, config){
12232     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12233     /**
12234      * @event arrowclick
12235      * Fires when this button's arrow is clicked
12236      * @param {SplitButton} this
12237      * @param {EventObject} e The click event
12238      */
12239     this.addEvents({"arrowclick":true});
12240 };
12241
12242 Roo.extend(Roo.SplitButton, Roo.Button, {
12243     render : function(renderTo){
12244         // this is one sweet looking template!
12245         var tpl = new Roo.Template(
12246             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12247             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12248             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
12249             "</tbody></table></td><td>",
12250             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12251             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
12252             "</tbody></table></td></tr></table>"
12253         );
12254         var btn = tpl.append(renderTo, [this.text, this.type], true);
12255         var btnEl = btn.child("button");
12256         if(this.cls){
12257             btn.addClass(this.cls);
12258         }
12259         if(this.icon){
12260             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12261         }
12262         if(this.iconCls){
12263             btnEl.addClass(this.iconCls);
12264             if(!this.cls){
12265                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12266             }
12267         }
12268         this.el = btn;
12269         if(this.handleMouseEvents){
12270             btn.on("mouseover", this.onMouseOver, this);
12271             btn.on("mouseout", this.onMouseOut, this);
12272             btn.on("mousedown", this.onMouseDown, this);
12273             btn.on("mouseup", this.onMouseUp, this);
12274         }
12275         btn.on(this.clickEvent, this.onClick, this);
12276         if(this.tooltip){
12277             if(typeof this.tooltip == 'object'){
12278                 Roo.QuickTips.tips(Roo.apply({
12279                       target: btnEl.id
12280                 }, this.tooltip));
12281             } else {
12282                 btnEl.dom[this.tooltipType] = this.tooltip;
12283             }
12284         }
12285         if(this.arrowTooltip){
12286             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12287         }
12288         if(this.hidden){
12289             this.hide();
12290         }
12291         if(this.disabled){
12292             this.disable();
12293         }
12294         if(this.pressed){
12295             this.el.addClass("x-btn-pressed");
12296         }
12297         if(Roo.isIE && !Roo.isIE7){
12298             this.autoWidth.defer(1, this);
12299         }else{
12300             this.autoWidth();
12301         }
12302         if(this.menu){
12303             this.menu.on("show", this.onMenuShow, this);
12304             this.menu.on("hide", this.onMenuHide, this);
12305         }
12306         this.fireEvent('render', this);
12307     },
12308
12309     // private
12310     autoWidth : function(){
12311         if(this.el){
12312             var tbl = this.el.child("table:first");
12313             var tbl2 = this.el.child("table:last");
12314             this.el.setWidth("auto");
12315             tbl.setWidth("auto");
12316             if(Roo.isIE7 && Roo.isStrict){
12317                 var ib = this.el.child('button:first');
12318                 if(ib && ib.getWidth() > 20){
12319                     ib.clip();
12320                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12321                 }
12322             }
12323             if(this.minWidth){
12324                 if(this.hidden){
12325                     this.el.beginMeasure();
12326                 }
12327                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12328                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12329                 }
12330                 if(this.hidden){
12331                     this.el.endMeasure();
12332                 }
12333             }
12334             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12335         } 
12336     },
12337     /**
12338      * Sets this button's click handler
12339      * @param {Function} handler The function to call when the button is clicked
12340      * @param {Object} scope (optional) Scope for the function passed above
12341      */
12342     setHandler : function(handler, scope){
12343         this.handler = handler;
12344         this.scope = scope;  
12345     },
12346     
12347     /**
12348      * Sets this button's arrow click handler
12349      * @param {Function} handler The function to call when the arrow is clicked
12350      * @param {Object} scope (optional) Scope for the function passed above
12351      */
12352     setArrowHandler : function(handler, scope){
12353         this.arrowHandler = handler;
12354         this.scope = scope;  
12355     },
12356     
12357     /**
12358      * Focus the button
12359      */
12360     focus : function(){
12361         if(this.el){
12362             this.el.child("button:first").focus();
12363         }
12364     },
12365
12366     // private
12367     onClick : function(e){
12368         e.preventDefault();
12369         if(!this.disabled){
12370             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12371                 if(this.menu && !this.menu.isVisible()){
12372                     this.menu.show(this.el, this.menuAlign);
12373                 }
12374                 this.fireEvent("arrowclick", this, e);
12375                 if(this.arrowHandler){
12376                     this.arrowHandler.call(this.scope || this, this, e);
12377                 }
12378             }else{
12379                 this.fireEvent("click", this, e);
12380                 if(this.handler){
12381                     this.handler.call(this.scope || this, this, e);
12382                 }
12383             }
12384         }
12385     },
12386     // private
12387     onMouseDown : function(e){
12388         if(!this.disabled){
12389             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12390         }
12391     },
12392     // private
12393     onMouseUp : function(e){
12394         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12395     }   
12396 });
12397
12398
12399 // backwards compat
12400 Roo.MenuButton = Roo.SplitButton;/*
12401  * Based on:
12402  * Ext JS Library 1.1.1
12403  * Copyright(c) 2006-2007, Ext JS, LLC.
12404  *
12405  * Originally Released Under LGPL - original licence link has changed is not relivant.
12406  *
12407  * Fork - LGPL
12408  * <script type="text/javascript">
12409  */
12410
12411 /**
12412  * @class Roo.Toolbar
12413  * Basic Toolbar class.
12414  * @constructor
12415  * Creates a new Toolbar
12416  * @param {Object} container The config object
12417  */ 
12418 Roo.Toolbar = function(container, buttons, config)
12419 {
12420     /// old consturctor format still supported..
12421     if(container instanceof Array){ // omit the container for later rendering
12422         buttons = container;
12423         config = buttons;
12424         container = null;
12425     }
12426     if (typeof(container) == 'object' && container.xtype) {
12427         config = container;
12428         container = config.container;
12429         buttons = config.buttons || []; // not really - use items!!
12430     }
12431     var xitems = [];
12432     if (config && config.items) {
12433         xitems = config.items;
12434         delete config.items;
12435     }
12436     Roo.apply(this, config);
12437     this.buttons = buttons;
12438     
12439     if(container){
12440         this.render(container);
12441     }
12442     this.xitems = xitems;
12443     Roo.each(xitems, function(b) {
12444         this.add(b);
12445     }, this);
12446     
12447 };
12448
12449 Roo.Toolbar.prototype = {
12450     /**
12451      * @cfg {Array} items
12452      * array of button configs or elements to add (will be converted to a MixedCollection)
12453      */
12454     
12455     /**
12456      * @cfg {String/HTMLElement/Element} container
12457      * The id or element that will contain the toolbar
12458      */
12459     // private
12460     render : function(ct){
12461         this.el = Roo.get(ct);
12462         if(this.cls){
12463             this.el.addClass(this.cls);
12464         }
12465         // using a table allows for vertical alignment
12466         // 100% width is needed by Safari...
12467         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12468         this.tr = this.el.child("tr", true);
12469         var autoId = 0;
12470         this.items = new Roo.util.MixedCollection(false, function(o){
12471             return o.id || ("item" + (++autoId));
12472         });
12473         if(this.buttons){
12474             this.add.apply(this, this.buttons);
12475             delete this.buttons;
12476         }
12477     },
12478
12479     /**
12480      * Adds element(s) to the toolbar -- this function takes a variable number of 
12481      * arguments of mixed type and adds them to the toolbar.
12482      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12483      * <ul>
12484      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12485      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12486      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12487      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12488      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12489      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12490      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12491      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12492      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12493      * </ul>
12494      * @param {Mixed} arg2
12495      * @param {Mixed} etc.
12496      */
12497     add : function(){
12498         var a = arguments, l = a.length;
12499         for(var i = 0; i < l; i++){
12500             this._add(a[i]);
12501         }
12502     },
12503     // private..
12504     _add : function(el) {
12505         
12506         if (el.xtype) {
12507             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12508         }
12509         
12510         if (el.applyTo){ // some kind of form field
12511             return this.addField(el);
12512         } 
12513         if (el.render){ // some kind of Toolbar.Item
12514             return this.addItem(el);
12515         }
12516         if (typeof el == "string"){ // string
12517             if(el == "separator" || el == "-"){
12518                 return this.addSeparator();
12519             }
12520             if (el == " "){
12521                 return this.addSpacer();
12522             }
12523             if(el == "->"){
12524                 return this.addFill();
12525             }
12526             return this.addText(el);
12527             
12528         }
12529         if(el.tagName){ // element
12530             return this.addElement(el);
12531         }
12532         if(typeof el == "object"){ // must be button config?
12533             return this.addButton(el);
12534         }
12535         // and now what?!?!
12536         return false;
12537         
12538     },
12539     
12540     /**
12541      * Add an Xtype element
12542      * @param {Object} xtype Xtype Object
12543      * @return {Object} created Object
12544      */
12545     addxtype : function(e){
12546         return this.add(e);  
12547     },
12548     
12549     /**
12550      * Returns the Element for this toolbar.
12551      * @return {Roo.Element}
12552      */
12553     getEl : function(){
12554         return this.el;  
12555     },
12556     
12557     /**
12558      * Adds a separator
12559      * @return {Roo.Toolbar.Item} The separator item
12560      */
12561     addSeparator : function(){
12562         return this.addItem(new Roo.Toolbar.Separator());
12563     },
12564
12565     /**
12566      * Adds a spacer element
12567      * @return {Roo.Toolbar.Spacer} The spacer item
12568      */
12569     addSpacer : function(){
12570         return this.addItem(new Roo.Toolbar.Spacer());
12571     },
12572
12573     /**
12574      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12575      * @return {Roo.Toolbar.Fill} The fill item
12576      */
12577     addFill : function(){
12578         return this.addItem(new Roo.Toolbar.Fill());
12579     },
12580
12581     /**
12582      * Adds any standard HTML element to the toolbar
12583      * @param {String/HTMLElement/Element} el The element or id of the element to add
12584      * @return {Roo.Toolbar.Item} The element's item
12585      */
12586     addElement : function(el){
12587         return this.addItem(new Roo.Toolbar.Item(el));
12588     },
12589     /**
12590      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12591      * @type Roo.util.MixedCollection  
12592      */
12593     items : false,
12594      
12595     /**
12596      * Adds any Toolbar.Item or subclass
12597      * @param {Roo.Toolbar.Item} item
12598      * @return {Roo.Toolbar.Item} The item
12599      */
12600     addItem : function(item){
12601         var td = this.nextBlock();
12602         item.render(td);
12603         this.items.add(item);
12604         return item;
12605     },
12606     
12607     /**
12608      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12609      * @param {Object/Array} config A button config or array of configs
12610      * @return {Roo.Toolbar.Button/Array}
12611      */
12612     addButton : function(config){
12613         if(config instanceof Array){
12614             var buttons = [];
12615             for(var i = 0, len = config.length; i < len; i++) {
12616                 buttons.push(this.addButton(config[i]));
12617             }
12618             return buttons;
12619         }
12620         var b = config;
12621         if(!(config instanceof Roo.Toolbar.Button)){
12622             b = config.split ?
12623                 new Roo.Toolbar.SplitButton(config) :
12624                 new Roo.Toolbar.Button(config);
12625         }
12626         var td = this.nextBlock();
12627         b.render(td);
12628         this.items.add(b);
12629         return b;
12630     },
12631     
12632     /**
12633      * Adds text to the toolbar
12634      * @param {String} text The text to add
12635      * @return {Roo.Toolbar.Item} The element's item
12636      */
12637     addText : function(text){
12638         return this.addItem(new Roo.Toolbar.TextItem(text));
12639     },
12640     
12641     /**
12642      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12643      * @param {Number} index The index where the item is to be inserted
12644      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12645      * @return {Roo.Toolbar.Button/Item}
12646      */
12647     insertButton : function(index, item){
12648         if(item instanceof Array){
12649             var buttons = [];
12650             for(var i = 0, len = item.length; i < len; i++) {
12651                buttons.push(this.insertButton(index + i, item[i]));
12652             }
12653             return buttons;
12654         }
12655         if (!(item instanceof Roo.Toolbar.Button)){
12656            item = new Roo.Toolbar.Button(item);
12657         }
12658         var td = document.createElement("td");
12659         this.tr.insertBefore(td, this.tr.childNodes[index]);
12660         item.render(td);
12661         this.items.insert(index, item);
12662         return item;
12663     },
12664     
12665     /**
12666      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12667      * @param {Object} config
12668      * @return {Roo.Toolbar.Item} The element's item
12669      */
12670     addDom : function(config, returnEl){
12671         var td = this.nextBlock();
12672         Roo.DomHelper.overwrite(td, config);
12673         var ti = new Roo.Toolbar.Item(td.firstChild);
12674         ti.render(td);
12675         this.items.add(ti);
12676         return ti;
12677     },
12678
12679     /**
12680      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12681      * @type Roo.util.MixedCollection  
12682      */
12683     fields : false,
12684     
12685     /**
12686      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12687      * Note: the field should not have been rendered yet. For a field that has already been
12688      * rendered, use {@link #addElement}.
12689      * @param {Roo.form.Field} field
12690      * @return {Roo.ToolbarItem}
12691      */
12692      
12693       
12694     addField : function(field) {
12695         if (!this.fields) {
12696             var autoId = 0;
12697             this.fields = new Roo.util.MixedCollection(false, function(o){
12698                 return o.id || ("item" + (++autoId));
12699             });
12700
12701         }
12702         
12703         var td = this.nextBlock();
12704         field.render(td);
12705         var ti = new Roo.Toolbar.Item(td.firstChild);
12706         ti.render(td);
12707         this.items.add(ti);
12708         this.fields.add(field);
12709         return ti;
12710     },
12711     /**
12712      * Hide the toolbar
12713      * @method hide
12714      */
12715      
12716       
12717     hide : function()
12718     {
12719         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12720         this.el.child('div').hide();
12721     },
12722     /**
12723      * Show the toolbar
12724      * @method show
12725      */
12726     show : function()
12727     {
12728         this.el.child('div').show();
12729     },
12730       
12731     // private
12732     nextBlock : function(){
12733         var td = document.createElement("td");
12734         this.tr.appendChild(td);
12735         return td;
12736     },
12737
12738     // private
12739     destroy : function(){
12740         if(this.items){ // rendered?
12741             Roo.destroy.apply(Roo, this.items.items);
12742         }
12743         if(this.fields){ // rendered?
12744             Roo.destroy.apply(Roo, this.fields.items);
12745         }
12746         Roo.Element.uncache(this.el, this.tr);
12747     }
12748 };
12749
12750 /**
12751  * @class Roo.Toolbar.Item
12752  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12753  * @constructor
12754  * Creates a new Item
12755  * @param {HTMLElement} el 
12756  */
12757 Roo.Toolbar.Item = function(el){
12758     this.el = Roo.getDom(el);
12759     this.id = Roo.id(this.el);
12760     this.hidden = false;
12761 };
12762
12763 Roo.Toolbar.Item.prototype = {
12764     
12765     /**
12766      * Get this item's HTML Element
12767      * @return {HTMLElement}
12768      */
12769     getEl : function(){
12770        return this.el;  
12771     },
12772
12773     // private
12774     render : function(td){
12775         this.td = td;
12776         td.appendChild(this.el);
12777     },
12778     
12779     /**
12780      * Removes and destroys this item.
12781      */
12782     destroy : function(){
12783         this.td.parentNode.removeChild(this.td);
12784     },
12785     
12786     /**
12787      * Shows this item.
12788      */
12789     show: function(){
12790         this.hidden = false;
12791         this.td.style.display = "";
12792     },
12793     
12794     /**
12795      * Hides this item.
12796      */
12797     hide: function(){
12798         this.hidden = true;
12799         this.td.style.display = "none";
12800     },
12801     
12802     /**
12803      * Convenience function for boolean show/hide.
12804      * @param {Boolean} visible true to show/false to hide
12805      */
12806     setVisible: function(visible){
12807         if(visible) {
12808             this.show();
12809         }else{
12810             this.hide();
12811         }
12812     },
12813     
12814     /**
12815      * Try to focus this item.
12816      */
12817     focus : function(){
12818         Roo.fly(this.el).focus();
12819     },
12820     
12821     /**
12822      * Disables this item.
12823      */
12824     disable : function(){
12825         Roo.fly(this.td).addClass("x-item-disabled");
12826         this.disabled = true;
12827         this.el.disabled = true;
12828     },
12829     
12830     /**
12831      * Enables this item.
12832      */
12833     enable : function(){
12834         Roo.fly(this.td).removeClass("x-item-disabled");
12835         this.disabled = false;
12836         this.el.disabled = false;
12837     }
12838 };
12839
12840
12841 /**
12842  * @class Roo.Toolbar.Separator
12843  * @extends Roo.Toolbar.Item
12844  * A simple toolbar separator class
12845  * @constructor
12846  * Creates a new Separator
12847  */
12848 Roo.Toolbar.Separator = function(){
12849     var s = document.createElement("span");
12850     s.className = "ytb-sep";
12851     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12852 };
12853 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12854     enable:Roo.emptyFn,
12855     disable:Roo.emptyFn,
12856     focus:Roo.emptyFn
12857 });
12858
12859 /**
12860  * @class Roo.Toolbar.Spacer
12861  * @extends Roo.Toolbar.Item
12862  * A simple element that adds extra horizontal space to a toolbar.
12863  * @constructor
12864  * Creates a new Spacer
12865  */
12866 Roo.Toolbar.Spacer = function(){
12867     var s = document.createElement("div");
12868     s.className = "ytb-spacer";
12869     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12870 };
12871 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12872     enable:Roo.emptyFn,
12873     disable:Roo.emptyFn,
12874     focus:Roo.emptyFn
12875 });
12876
12877 /**
12878  * @class Roo.Toolbar.Fill
12879  * @extends Roo.Toolbar.Spacer
12880  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12881  * @constructor
12882  * Creates a new Spacer
12883  */
12884 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12885     // private
12886     render : function(td){
12887         td.style.width = '100%';
12888         Roo.Toolbar.Fill.superclass.render.call(this, td);
12889     }
12890 });
12891
12892 /**
12893  * @class Roo.Toolbar.TextItem
12894  * @extends Roo.Toolbar.Item
12895  * A simple class that renders text directly into a toolbar.
12896  * @constructor
12897  * Creates a new TextItem
12898  * @param {String} text
12899  */
12900 Roo.Toolbar.TextItem = function(text){
12901     if (typeof(text) == 'object') {
12902         text = text.text;
12903     }
12904     var s = document.createElement("span");
12905     s.className = "ytb-text";
12906     s.innerHTML = text;
12907     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12908 };
12909 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12910     enable:Roo.emptyFn,
12911     disable:Roo.emptyFn,
12912     focus:Roo.emptyFn
12913 });
12914
12915 /**
12916  * @class Roo.Toolbar.Button
12917  * @extends Roo.Button
12918  * A button that renders into a toolbar.
12919  * @constructor
12920  * Creates a new Button
12921  * @param {Object} config A standard {@link Roo.Button} config object
12922  */
12923 Roo.Toolbar.Button = function(config){
12924     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12925 };
12926 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12927     render : function(td){
12928         this.td = td;
12929         Roo.Toolbar.Button.superclass.render.call(this, td);
12930     },
12931     
12932     /**
12933      * Removes and destroys this button
12934      */
12935     destroy : function(){
12936         Roo.Toolbar.Button.superclass.destroy.call(this);
12937         this.td.parentNode.removeChild(this.td);
12938     },
12939     
12940     /**
12941      * Shows this button
12942      */
12943     show: function(){
12944         this.hidden = false;
12945         this.td.style.display = "";
12946     },
12947     
12948     /**
12949      * Hides this button
12950      */
12951     hide: function(){
12952         this.hidden = true;
12953         this.td.style.display = "none";
12954     },
12955
12956     /**
12957      * Disables this item
12958      */
12959     disable : function(){
12960         Roo.fly(this.td).addClass("x-item-disabled");
12961         this.disabled = true;
12962     },
12963
12964     /**
12965      * Enables this item
12966      */
12967     enable : function(){
12968         Roo.fly(this.td).removeClass("x-item-disabled");
12969         this.disabled = false;
12970     }
12971 });
12972 // backwards compat
12973 Roo.ToolbarButton = Roo.Toolbar.Button;
12974
12975 /**
12976  * @class Roo.Toolbar.SplitButton
12977  * @extends Roo.SplitButton
12978  * A menu button that renders into a toolbar.
12979  * @constructor
12980  * Creates a new SplitButton
12981  * @param {Object} config A standard {@link Roo.SplitButton} config object
12982  */
12983 Roo.Toolbar.SplitButton = function(config){
12984     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12985 };
12986 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12987     render : function(td){
12988         this.td = td;
12989         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12990     },
12991     
12992     /**
12993      * Removes and destroys this button
12994      */
12995     destroy : function(){
12996         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12997         this.td.parentNode.removeChild(this.td);
12998     },
12999     
13000     /**
13001      * Shows this button
13002      */
13003     show: function(){
13004         this.hidden = false;
13005         this.td.style.display = "";
13006     },
13007     
13008     /**
13009      * Hides this button
13010      */
13011     hide: function(){
13012         this.hidden = true;
13013         this.td.style.display = "none";
13014     }
13015 });
13016
13017 // backwards compat
13018 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13019  * Based on:
13020  * Ext JS Library 1.1.1
13021  * Copyright(c) 2006-2007, Ext JS, LLC.
13022  *
13023  * Originally Released Under LGPL - original licence link has changed is not relivant.
13024  *
13025  * Fork - LGPL
13026  * <script type="text/javascript">
13027  */
13028  
13029 /**
13030  * @class Roo.PagingToolbar
13031  * @extends Roo.Toolbar
13032  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13033  * @constructor
13034  * Create a new PagingToolbar
13035  * @param {Object} config The config object
13036  */
13037 Roo.PagingToolbar = function(el, ds, config)
13038 {
13039     // old args format still supported... - xtype is prefered..
13040     if (typeof(el) == 'object' && el.xtype) {
13041         // created from xtype...
13042         config = el;
13043         ds = el.dataSource;
13044         el = config.container;
13045     }
13046     var items = [];
13047     if (config.items) {
13048         items = config.items;
13049         config.items = [];
13050     }
13051     
13052     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13053     this.ds = ds;
13054     this.cursor = 0;
13055     this.renderButtons(this.el);
13056     this.bind(ds);
13057     
13058     // supprot items array.
13059    
13060     Roo.each(items, function(e) {
13061         this.add(Roo.factory(e));
13062     },this);
13063     
13064 };
13065
13066 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13067     /**
13068      * @cfg {Roo.data.Store} dataSource
13069      * The underlying data store providing the paged data
13070      */
13071     /**
13072      * @cfg {String/HTMLElement/Element} container
13073      * container The id or element that will contain the toolbar
13074      */
13075     /**
13076      * @cfg {Boolean} displayInfo
13077      * True to display the displayMsg (defaults to false)
13078      */
13079     /**
13080      * @cfg {Number} pageSize
13081      * The number of records to display per page (defaults to 20)
13082      */
13083     pageSize: 20,
13084     /**
13085      * @cfg {String} displayMsg
13086      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13087      */
13088     displayMsg : 'Displaying {0} - {1} of {2}',
13089     /**
13090      * @cfg {String} emptyMsg
13091      * The message to display when no records are found (defaults to "No data to display")
13092      */
13093     emptyMsg : 'No data to display',
13094     /**
13095      * Customizable piece of the default paging text (defaults to "Page")
13096      * @type String
13097      */
13098     beforePageText : "Page",
13099     /**
13100      * Customizable piece of the default paging text (defaults to "of %0")
13101      * @type String
13102      */
13103     afterPageText : "of {0}",
13104     /**
13105      * Customizable piece of the default paging text (defaults to "First Page")
13106      * @type String
13107      */
13108     firstText : "First Page",
13109     /**
13110      * Customizable piece of the default paging text (defaults to "Previous Page")
13111      * @type String
13112      */
13113     prevText : "Previous Page",
13114     /**
13115      * Customizable piece of the default paging text (defaults to "Next Page")
13116      * @type String
13117      */
13118     nextText : "Next Page",
13119     /**
13120      * Customizable piece of the default paging text (defaults to "Last Page")
13121      * @type String
13122      */
13123     lastText : "Last Page",
13124     /**
13125      * Customizable piece of the default paging text (defaults to "Refresh")
13126      * @type String
13127      */
13128     refreshText : "Refresh",
13129
13130     // private
13131     renderButtons : function(el){
13132         Roo.PagingToolbar.superclass.render.call(this, el);
13133         this.first = this.addButton({
13134             tooltip: this.firstText,
13135             cls: "x-btn-icon x-grid-page-first",
13136             disabled: true,
13137             handler: this.onClick.createDelegate(this, ["first"])
13138         });
13139         this.prev = this.addButton({
13140             tooltip: this.prevText,
13141             cls: "x-btn-icon x-grid-page-prev",
13142             disabled: true,
13143             handler: this.onClick.createDelegate(this, ["prev"])
13144         });
13145         //this.addSeparator();
13146         this.add(this.beforePageText);
13147         this.field = Roo.get(this.addDom({
13148            tag: "input",
13149            type: "text",
13150            size: "3",
13151            value: "1",
13152            cls: "x-grid-page-number"
13153         }).el);
13154         this.field.on("keydown", this.onPagingKeydown, this);
13155         this.field.on("focus", function(){this.dom.select();});
13156         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13157         this.field.setHeight(18);
13158         //this.addSeparator();
13159         this.next = this.addButton({
13160             tooltip: this.nextText,
13161             cls: "x-btn-icon x-grid-page-next",
13162             disabled: true,
13163             handler: this.onClick.createDelegate(this, ["next"])
13164         });
13165         this.last = this.addButton({
13166             tooltip: this.lastText,
13167             cls: "x-btn-icon x-grid-page-last",
13168             disabled: true,
13169             handler: this.onClick.createDelegate(this, ["last"])
13170         });
13171         //this.addSeparator();
13172         this.loading = this.addButton({
13173             tooltip: this.refreshText,
13174             cls: "x-btn-icon x-grid-loading",
13175             handler: this.onClick.createDelegate(this, ["refresh"])
13176         });
13177
13178         if(this.displayInfo){
13179             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13180         }
13181     },
13182
13183     // private
13184     updateInfo : function(){
13185         if(this.displayEl){
13186             var count = this.ds.getCount();
13187             var msg = count == 0 ?
13188                 this.emptyMsg :
13189                 String.format(
13190                     this.displayMsg,
13191                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13192                 );
13193             this.displayEl.update(msg);
13194         }
13195     },
13196
13197     // private
13198     onLoad : function(ds, r, o){
13199        this.cursor = o.params ? o.params.start : 0;
13200        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13201
13202        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13203        this.field.dom.value = ap;
13204        this.first.setDisabled(ap == 1);
13205        this.prev.setDisabled(ap == 1);
13206        this.next.setDisabled(ap == ps);
13207        this.last.setDisabled(ap == ps);
13208        this.loading.enable();
13209        this.updateInfo();
13210     },
13211
13212     // private
13213     getPageData : function(){
13214         var total = this.ds.getTotalCount();
13215         return {
13216             total : total,
13217             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13218             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13219         };
13220     },
13221
13222     // private
13223     onLoadError : function(){
13224         this.loading.enable();
13225     },
13226
13227     // private
13228     onPagingKeydown : function(e){
13229         var k = e.getKey();
13230         var d = this.getPageData();
13231         if(k == e.RETURN){
13232             var v = this.field.dom.value, pageNum;
13233             if(!v || isNaN(pageNum = parseInt(v, 10))){
13234                 this.field.dom.value = d.activePage;
13235                 return;
13236             }
13237             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13238             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13239             e.stopEvent();
13240         }
13241         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
13242         {
13243           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13244           this.field.dom.value = pageNum;
13245           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13246           e.stopEvent();
13247         }
13248         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13249         {
13250           var v = this.field.dom.value, pageNum; 
13251           var increment = (e.shiftKey) ? 10 : 1;
13252           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13253             increment *= -1;
13254           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13255             this.field.dom.value = d.activePage;
13256             return;
13257           }
13258           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13259           {
13260             this.field.dom.value = parseInt(v, 10) + increment;
13261             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13262             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13263           }
13264           e.stopEvent();
13265         }
13266     },
13267
13268     // private
13269     beforeLoad : function(){
13270         if(this.loading){
13271             this.loading.disable();
13272         }
13273     },
13274
13275     // private
13276     onClick : function(which){
13277         var ds = this.ds;
13278         switch(which){
13279             case "first":
13280                 ds.load({params:{start: 0, limit: this.pageSize}});
13281             break;
13282             case "prev":
13283                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13284             break;
13285             case "next":
13286                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13287             break;
13288             case "last":
13289                 var total = ds.getTotalCount();
13290                 var extra = total % this.pageSize;
13291                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13292                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13293             break;
13294             case "refresh":
13295                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13296             break;
13297         }
13298     },
13299
13300     /**
13301      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13302      * @param {Roo.data.Store} store The data store to unbind
13303      */
13304     unbind : function(ds){
13305         ds.un("beforeload", this.beforeLoad, this);
13306         ds.un("load", this.onLoad, this);
13307         ds.un("loadexception", this.onLoadError, this);
13308         ds.un("remove", this.updateInfo, this);
13309         ds.un("add", this.updateInfo, this);
13310         this.ds = undefined;
13311     },
13312
13313     /**
13314      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13315      * @param {Roo.data.Store} store The data store to bind
13316      */
13317     bind : function(ds){
13318         ds.on("beforeload", this.beforeLoad, this);
13319         ds.on("load", this.onLoad, this);
13320         ds.on("loadexception", this.onLoadError, this);
13321         ds.on("remove", this.updateInfo, this);
13322         ds.on("add", this.updateInfo, this);
13323         this.ds = ds;
13324     }
13325 });/*
13326  * Based on:
13327  * Ext JS Library 1.1.1
13328  * Copyright(c) 2006-2007, Ext JS, LLC.
13329  *
13330  * Originally Released Under LGPL - original licence link has changed is not relivant.
13331  *
13332  * Fork - LGPL
13333  * <script type="text/javascript">
13334  */
13335
13336 /**
13337  * @class Roo.Resizable
13338  * @extends Roo.util.Observable
13339  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13340  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13341  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
13342  * the element will be wrapped for you automatically.</p>
13343  * <p>Here is the list of valid resize handles:</p>
13344  * <pre>
13345 Value   Description
13346 ------  -------------------
13347  'n'     north
13348  's'     south
13349  'e'     east
13350  'w'     west
13351  'nw'    northwest
13352  'sw'    southwest
13353  'se'    southeast
13354  'ne'    northeast
13355  'hd'    horizontal drag
13356  'all'   all
13357 </pre>
13358  * <p>Here's an example showing the creation of a typical Resizable:</p>
13359  * <pre><code>
13360 var resizer = new Roo.Resizable("element-id", {
13361     handles: 'all',
13362     minWidth: 200,
13363     minHeight: 100,
13364     maxWidth: 500,
13365     maxHeight: 400,
13366     pinned: true
13367 });
13368 resizer.on("resize", myHandler);
13369 </code></pre>
13370  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13371  * resizer.east.setDisplayed(false);</p>
13372  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13373  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13374  * resize operation's new size (defaults to [0, 0])
13375  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13376  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13377  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13378  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13379  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13380  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13381  * @cfg {Number} width The width of the element in pixels (defaults to null)
13382  * @cfg {Number} height The height of the element in pixels (defaults to null)
13383  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13384  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13385  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13386  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13387  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13388  * in favor of the handles config option (defaults to false)
13389  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13390  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13391  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13392  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13393  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13394  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13395  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13396  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13397  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13398  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13399  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13400  * @constructor
13401  * Create a new resizable component
13402  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13403  * @param {Object} config configuration options
13404   */
13405 Roo.Resizable = function(el, config)
13406 {
13407     this.el = Roo.get(el);
13408
13409     if(config && config.wrap){
13410         config.resizeChild = this.el;
13411         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13412         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13413         this.el.setStyle("overflow", "hidden");
13414         this.el.setPositioning(config.resizeChild.getPositioning());
13415         config.resizeChild.clearPositioning();
13416         if(!config.width || !config.height){
13417             var csize = config.resizeChild.getSize();
13418             this.el.setSize(csize.width, csize.height);
13419         }
13420         if(config.pinned && !config.adjustments){
13421             config.adjustments = "auto";
13422         }
13423     }
13424
13425     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13426     this.proxy.unselectable();
13427     this.proxy.enableDisplayMode('block');
13428
13429     Roo.apply(this, config);
13430
13431     if(this.pinned){
13432         this.disableTrackOver = true;
13433         this.el.addClass("x-resizable-pinned");
13434     }
13435     // if the element isn't positioned, make it relative
13436     var position = this.el.getStyle("position");
13437     if(position != "absolute" && position != "fixed"){
13438         this.el.setStyle("position", "relative");
13439     }
13440     if(!this.handles){ // no handles passed, must be legacy style
13441         this.handles = 's,e,se';
13442         if(this.multiDirectional){
13443             this.handles += ',n,w';
13444         }
13445     }
13446     if(this.handles == "all"){
13447         this.handles = "n s e w ne nw se sw";
13448     }
13449     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13450     var ps = Roo.Resizable.positions;
13451     for(var i = 0, len = hs.length; i < len; i++){
13452         if(hs[i] && ps[hs[i]]){
13453             var pos = ps[hs[i]];
13454             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13455         }
13456     }
13457     // legacy
13458     this.corner = this.southeast;
13459     
13460     // updateBox = the box can move..
13461     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13462         this.updateBox = true;
13463     }
13464
13465     this.activeHandle = null;
13466
13467     if(this.resizeChild){
13468         if(typeof this.resizeChild == "boolean"){
13469             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13470         }else{
13471             this.resizeChild = Roo.get(this.resizeChild, true);
13472         }
13473     }
13474     
13475     if(this.adjustments == "auto"){
13476         var rc = this.resizeChild;
13477         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13478         if(rc && (hw || hn)){
13479             rc.position("relative");
13480             rc.setLeft(hw ? hw.el.getWidth() : 0);
13481             rc.setTop(hn ? hn.el.getHeight() : 0);
13482         }
13483         this.adjustments = [
13484             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13485             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13486         ];
13487     }
13488
13489     if(this.draggable){
13490         this.dd = this.dynamic ?
13491             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13492         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13493     }
13494
13495     // public events
13496     this.addEvents({
13497         /**
13498          * @event beforeresize
13499          * Fired before resize is allowed. Set enabled to false to cancel resize.
13500          * @param {Roo.Resizable} this
13501          * @param {Roo.EventObject} e The mousedown event
13502          */
13503         "beforeresize" : true,
13504         /**
13505          * @event resize
13506          * Fired after a resize.
13507          * @param {Roo.Resizable} this
13508          * @param {Number} width The new width
13509          * @param {Number} height The new height
13510          * @param {Roo.EventObject} e The mouseup event
13511          */
13512         "resize" : true
13513     });
13514
13515     if(this.width !== null && this.height !== null){
13516         this.resizeTo(this.width, this.height);
13517     }else{
13518         this.updateChildSize();
13519     }
13520     if(Roo.isIE){
13521         this.el.dom.style.zoom = 1;
13522     }
13523     Roo.Resizable.superclass.constructor.call(this);
13524 };
13525
13526 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13527         resizeChild : false,
13528         adjustments : [0, 0],
13529         minWidth : 5,
13530         minHeight : 5,
13531         maxWidth : 10000,
13532         maxHeight : 10000,
13533         enabled : true,
13534         animate : false,
13535         duration : .35,
13536         dynamic : false,
13537         handles : false,
13538         multiDirectional : false,
13539         disableTrackOver : false,
13540         easing : 'easeOutStrong',
13541         widthIncrement : 0,
13542         heightIncrement : 0,
13543         pinned : false,
13544         width : null,
13545         height : null,
13546         preserveRatio : false,
13547         transparent: false,
13548         minX: 0,
13549         minY: 0,
13550         draggable: false,
13551
13552         /**
13553          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13554          */
13555         constrainTo: undefined,
13556         /**
13557          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13558          */
13559         resizeRegion: undefined,
13560
13561
13562     /**
13563      * Perform a manual resize
13564      * @param {Number} width
13565      * @param {Number} height
13566      */
13567     resizeTo : function(width, height){
13568         this.el.setSize(width, height);
13569         this.updateChildSize();
13570         this.fireEvent("resize", this, width, height, null);
13571     },
13572
13573     // private
13574     startSizing : function(e, handle){
13575         this.fireEvent("beforeresize", this, e);
13576         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13577
13578             if(!this.overlay){
13579                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13580                 this.overlay.unselectable();
13581                 this.overlay.enableDisplayMode("block");
13582                 this.overlay.on("mousemove", this.onMouseMove, this);
13583                 this.overlay.on("mouseup", this.onMouseUp, this);
13584             }
13585             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13586
13587             this.resizing = true;
13588             this.startBox = this.el.getBox();
13589             this.startPoint = e.getXY();
13590             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13591                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13592
13593             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13594             this.overlay.show();
13595
13596             if(this.constrainTo) {
13597                 var ct = Roo.get(this.constrainTo);
13598                 this.resizeRegion = ct.getRegion().adjust(
13599                     ct.getFrameWidth('t'),
13600                     ct.getFrameWidth('l'),
13601                     -ct.getFrameWidth('b'),
13602                     -ct.getFrameWidth('r')
13603                 );
13604             }
13605
13606             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13607             this.proxy.show();
13608             this.proxy.setBox(this.startBox);
13609             if(!this.dynamic){
13610                 this.proxy.setStyle('visibility', 'visible');
13611             }
13612         }
13613     },
13614
13615     // private
13616     onMouseDown : function(handle, e){
13617         if(this.enabled){
13618             e.stopEvent();
13619             this.activeHandle = handle;
13620             this.startSizing(e, handle);
13621         }
13622     },
13623
13624     // private
13625     onMouseUp : function(e){
13626         var size = this.resizeElement();
13627         this.resizing = false;
13628         this.handleOut();
13629         this.overlay.hide();
13630         this.proxy.hide();
13631         this.fireEvent("resize", this, size.width, size.height, e);
13632     },
13633
13634     // private
13635     updateChildSize : function(){
13636         if(this.resizeChild){
13637             var el = this.el;
13638             var child = this.resizeChild;
13639             var adj = this.adjustments;
13640             if(el.dom.offsetWidth){
13641                 var b = el.getSize(true);
13642                 child.setSize(b.width+adj[0], b.height+adj[1]);
13643             }
13644             // Second call here for IE
13645             // The first call enables instant resizing and
13646             // the second call corrects scroll bars if they
13647             // exist
13648             if(Roo.isIE){
13649                 setTimeout(function(){
13650                     if(el.dom.offsetWidth){
13651                         var b = el.getSize(true);
13652                         child.setSize(b.width+adj[0], b.height+adj[1]);
13653                     }
13654                 }, 10);
13655             }
13656         }
13657     },
13658
13659     // private
13660     snap : function(value, inc, min){
13661         if(!inc || !value) return value;
13662         var newValue = value;
13663         var m = value % inc;
13664         if(m > 0){
13665             if(m > (inc/2)){
13666                 newValue = value + (inc-m);
13667             }else{
13668                 newValue = value - m;
13669             }
13670         }
13671         return Math.max(min, newValue);
13672     },
13673
13674     // private
13675     resizeElement : function(){
13676         var box = this.proxy.getBox();
13677         if(this.updateBox){
13678             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13679         }else{
13680             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13681         }
13682         this.updateChildSize();
13683         if(!this.dynamic){
13684             this.proxy.hide();
13685         }
13686         return box;
13687     },
13688
13689     // private
13690     constrain : function(v, diff, m, mx){
13691         if(v - diff < m){
13692             diff = v - m;
13693         }else if(v - diff > mx){
13694             diff = mx - v;
13695         }
13696         return diff;
13697     },
13698
13699     // private
13700     onMouseMove : function(e){
13701         if(this.enabled){
13702             try{// try catch so if something goes wrong the user doesn't get hung
13703
13704             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13705                 return;
13706             }
13707
13708             //var curXY = this.startPoint;
13709             var curSize = this.curSize || this.startBox;
13710             var x = this.startBox.x, y = this.startBox.y;
13711             var ox = x, oy = y;
13712             var w = curSize.width, h = curSize.height;
13713             var ow = w, oh = h;
13714             var mw = this.minWidth, mh = this.minHeight;
13715             var mxw = this.maxWidth, mxh = this.maxHeight;
13716             var wi = this.widthIncrement;
13717             var hi = this.heightIncrement;
13718
13719             var eventXY = e.getXY();
13720             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13721             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13722
13723             var pos = this.activeHandle.position;
13724
13725             switch(pos){
13726                 case "east":
13727                     w += diffX;
13728                     w = Math.min(Math.max(mw, w), mxw);
13729                     break;
13730              
13731                 case "south":
13732                     h += diffY;
13733                     h = Math.min(Math.max(mh, h), mxh);
13734                     break;
13735                 case "southeast":
13736                     w += diffX;
13737                     h += diffY;
13738                     w = Math.min(Math.max(mw, w), mxw);
13739                     h = Math.min(Math.max(mh, h), mxh);
13740                     break;
13741                 case "north":
13742                     diffY = this.constrain(h, diffY, mh, mxh);
13743                     y += diffY;
13744                     h -= diffY;
13745                     break;
13746                 case "hdrag":
13747                     
13748                     if (wi) {
13749                         var adiffX = Math.abs(diffX);
13750                         var sub = (adiffX % wi); // how much 
13751                         if (sub > (wi/2)) { // far enough to snap
13752                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13753                         } else {
13754                             // remove difference.. 
13755                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13756                         }
13757                     }
13758                     x += diffX;
13759                     x = Math.max(this.minX, x);
13760                     break;
13761                 case "west":
13762                     diffX = this.constrain(w, diffX, mw, mxw);
13763                     x += diffX;
13764                     w -= diffX;
13765                     break;
13766                 case "northeast":
13767                     w += diffX;
13768                     w = Math.min(Math.max(mw, w), mxw);
13769                     diffY = this.constrain(h, diffY, mh, mxh);
13770                     y += diffY;
13771                     h -= diffY;
13772                     break;
13773                 case "northwest":
13774                     diffX = this.constrain(w, diffX, mw, mxw);
13775                     diffY = this.constrain(h, diffY, mh, mxh);
13776                     y += diffY;
13777                     h -= diffY;
13778                     x += diffX;
13779                     w -= diffX;
13780                     break;
13781                case "southwest":
13782                     diffX = this.constrain(w, diffX, mw, mxw);
13783                     h += diffY;
13784                     h = Math.min(Math.max(mh, h), mxh);
13785                     x += diffX;
13786                     w -= diffX;
13787                     break;
13788             }
13789
13790             var sw = this.snap(w, wi, mw);
13791             var sh = this.snap(h, hi, mh);
13792             if(sw != w || sh != h){
13793                 switch(pos){
13794                     case "northeast":
13795                         y -= sh - h;
13796                     break;
13797                     case "north":
13798                         y -= sh - h;
13799                         break;
13800                     case "southwest":
13801                         x -= sw - w;
13802                     break;
13803                     case "west":
13804                         x -= sw - w;
13805                         break;
13806                     case "northwest":
13807                         x -= sw - w;
13808                         y -= sh - h;
13809                     break;
13810                 }
13811                 w = sw;
13812                 h = sh;
13813             }
13814
13815             if(this.preserveRatio){
13816                 switch(pos){
13817                     case "southeast":
13818                     case "east":
13819                         h = oh * (w/ow);
13820                         h = Math.min(Math.max(mh, h), mxh);
13821                         w = ow * (h/oh);
13822                        break;
13823                     case "south":
13824                         w = ow * (h/oh);
13825                         w = Math.min(Math.max(mw, w), mxw);
13826                         h = oh * (w/ow);
13827                         break;
13828                     case "northeast":
13829                         w = ow * (h/oh);
13830                         w = Math.min(Math.max(mw, w), mxw);
13831                         h = oh * (w/ow);
13832                     break;
13833                     case "north":
13834                         var tw = w;
13835                         w = ow * (h/oh);
13836                         w = Math.min(Math.max(mw, w), mxw);
13837                         h = oh * (w/ow);
13838                         x += (tw - w) / 2;
13839                         break;
13840                     case "southwest":
13841                         h = oh * (w/ow);
13842                         h = Math.min(Math.max(mh, h), mxh);
13843                         var tw = w;
13844                         w = ow * (h/oh);
13845                         x += tw - w;
13846                         break;
13847                     case "west":
13848                         var th = h;
13849                         h = oh * (w/ow);
13850                         h = Math.min(Math.max(mh, h), mxh);
13851                         y += (th - h) / 2;
13852                         var tw = w;
13853                         w = ow * (h/oh);
13854                         x += tw - w;
13855                        break;
13856                     case "northwest":
13857                         var tw = w;
13858                         var th = h;
13859                         h = oh * (w/ow);
13860                         h = Math.min(Math.max(mh, h), mxh);
13861                         w = ow * (h/oh);
13862                         y += th - h;
13863                         x += tw - w;
13864                        break;
13865
13866                 }
13867             }
13868             if (pos == 'hdrag') {
13869                 w = ow;
13870             }
13871             this.proxy.setBounds(x, y, w, h);
13872             if(this.dynamic){
13873                 this.resizeElement();
13874             }
13875             }catch(e){}
13876         }
13877     },
13878
13879     // private
13880     handleOver : function(){
13881         if(this.enabled){
13882             this.el.addClass("x-resizable-over");
13883         }
13884     },
13885
13886     // private
13887     handleOut : function(){
13888         if(!this.resizing){
13889             this.el.removeClass("x-resizable-over");
13890         }
13891     },
13892
13893     /**
13894      * Returns the element this component is bound to.
13895      * @return {Roo.Element}
13896      */
13897     getEl : function(){
13898         return this.el;
13899     },
13900
13901     /**
13902      * Returns the resizeChild element (or null).
13903      * @return {Roo.Element}
13904      */
13905     getResizeChild : function(){
13906         return this.resizeChild;
13907     },
13908
13909     /**
13910      * Destroys this resizable. If the element was wrapped and
13911      * removeEl is not true then the element remains.
13912      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13913      */
13914     destroy : function(removeEl){
13915         this.proxy.remove();
13916         if(this.overlay){
13917             this.overlay.removeAllListeners();
13918             this.overlay.remove();
13919         }
13920         var ps = Roo.Resizable.positions;
13921         for(var k in ps){
13922             if(typeof ps[k] != "function" && this[ps[k]]){
13923                 var h = this[ps[k]];
13924                 h.el.removeAllListeners();
13925                 h.el.remove();
13926             }
13927         }
13928         if(removeEl){
13929             this.el.update("");
13930             this.el.remove();
13931         }
13932     }
13933 });
13934
13935 // private
13936 // hash to map config positions to true positions
13937 Roo.Resizable.positions = {
13938     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13939     hd: "hdrag"
13940 };
13941
13942 // private
13943 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13944     if(!this.tpl){
13945         // only initialize the template if resizable is used
13946         var tpl = Roo.DomHelper.createTemplate(
13947             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13948         );
13949         tpl.compile();
13950         Roo.Resizable.Handle.prototype.tpl = tpl;
13951     }
13952     this.position = pos;
13953     this.rz = rz;
13954     // show north drag fro topdra
13955     var handlepos = pos == 'hdrag' ? 'north' : pos;
13956     
13957     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13958     if (pos == 'hdrag') {
13959         this.el.setStyle('cursor', 'pointer');
13960     }
13961     this.el.unselectable();
13962     if(transparent){
13963         this.el.setOpacity(0);
13964     }
13965     this.el.on("mousedown", this.onMouseDown, this);
13966     if(!disableTrackOver){
13967         this.el.on("mouseover", this.onMouseOver, this);
13968         this.el.on("mouseout", this.onMouseOut, this);
13969     }
13970 };
13971
13972 // private
13973 Roo.Resizable.Handle.prototype = {
13974     afterResize : function(rz){
13975         // do nothing
13976     },
13977     // private
13978     onMouseDown : function(e){
13979         this.rz.onMouseDown(this, e);
13980     },
13981     // private
13982     onMouseOver : function(e){
13983         this.rz.handleOver(this, e);
13984     },
13985     // private
13986     onMouseOut : function(e){
13987         this.rz.handleOut(this, e);
13988     }
13989 };/*
13990  * Based on:
13991  * Ext JS Library 1.1.1
13992  * Copyright(c) 2006-2007, Ext JS, LLC.
13993  *
13994  * Originally Released Under LGPL - original licence link has changed is not relivant.
13995  *
13996  * Fork - LGPL
13997  * <script type="text/javascript">
13998  */
13999
14000 /**
14001  * @class Roo.Editor
14002  * @extends Roo.Component
14003  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14004  * @constructor
14005  * Create a new Editor
14006  * @param {Roo.form.Field} field The Field object (or descendant)
14007  * @param {Object} config The config object
14008  */
14009 Roo.Editor = function(field, config){
14010     Roo.Editor.superclass.constructor.call(this, config);
14011     this.field = field;
14012     this.addEvents({
14013         /**
14014              * @event beforestartedit
14015              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14016              * false from the handler of this event.
14017              * @param {Editor} this
14018              * @param {Roo.Element} boundEl The underlying element bound to this editor
14019              * @param {Mixed} value The field value being set
14020              */
14021         "beforestartedit" : true,
14022         /**
14023              * @event startedit
14024              * Fires when this editor is displayed
14025              * @param {Roo.Element} boundEl The underlying element bound to this editor
14026              * @param {Mixed} value The starting field value
14027              */
14028         "startedit" : true,
14029         /**
14030              * @event beforecomplete
14031              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14032              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14033              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14034              * event will not fire since no edit actually occurred.
14035              * @param {Editor} this
14036              * @param {Mixed} value The current field value
14037              * @param {Mixed} startValue The original field value
14038              */
14039         "beforecomplete" : true,
14040         /**
14041              * @event complete
14042              * Fires after editing is complete and any changed value has been written to the underlying field.
14043              * @param {Editor} this
14044              * @param {Mixed} value The current field value
14045              * @param {Mixed} startValue The original field value
14046              */
14047         "complete" : true,
14048         /**
14049          * @event specialkey
14050          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14051          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14052          * @param {Roo.form.Field} this
14053          * @param {Roo.EventObject} e The event object
14054          */
14055         "specialkey" : true
14056     });
14057 };
14058
14059 Roo.extend(Roo.Editor, Roo.Component, {
14060     /**
14061      * @cfg {Boolean/String} autosize
14062      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14063      * or "height" to adopt the height only (defaults to false)
14064      */
14065     /**
14066      * @cfg {Boolean} revertInvalid
14067      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14068      * validation fails (defaults to true)
14069      */
14070     /**
14071      * @cfg {Boolean} ignoreNoChange
14072      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14073      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14074      * will never be ignored.
14075      */
14076     /**
14077      * @cfg {Boolean} hideEl
14078      * False to keep the bound element visible while the editor is displayed (defaults to true)
14079      */
14080     /**
14081      * @cfg {Mixed} value
14082      * The data value of the underlying field (defaults to "")
14083      */
14084     value : "",
14085     /**
14086      * @cfg {String} alignment
14087      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14088      */
14089     alignment: "c-c?",
14090     /**
14091      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14092      * for bottom-right shadow (defaults to "frame")
14093      */
14094     shadow : "frame",
14095     /**
14096      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14097      */
14098     constrain : false,
14099     /**
14100      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14101      */
14102     completeOnEnter : false,
14103     /**
14104      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14105      */
14106     cancelOnEsc : false,
14107     /**
14108      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14109      */
14110     updateEl : false,
14111
14112     // private
14113     onRender : function(ct, position){
14114         this.el = new Roo.Layer({
14115             shadow: this.shadow,
14116             cls: "x-editor",
14117             parentEl : ct,
14118             shim : this.shim,
14119             shadowOffset:4,
14120             id: this.id,
14121             constrain: this.constrain
14122         });
14123         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14124         if(this.field.msgTarget != 'title'){
14125             this.field.msgTarget = 'qtip';
14126         }
14127         this.field.render(this.el);
14128         if(Roo.isGecko){
14129             this.field.el.dom.setAttribute('autocomplete', 'off');
14130         }
14131         this.field.on("specialkey", this.onSpecialKey, this);
14132         if(this.swallowKeys){
14133             this.field.el.swallowEvent(['keydown','keypress']);
14134         }
14135         this.field.show();
14136         this.field.on("blur", this.onBlur, this);
14137         if(this.field.grow){
14138             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14139         }
14140     },
14141
14142     onSpecialKey : function(field, e)
14143     {
14144         //Roo.log('editor onSpecialKey');
14145         if(this.completeOnEnter && e.getKey() == e.ENTER){
14146             e.stopEvent();
14147             this.completeEdit();
14148             return;
14149         }
14150         // do not fire special key otherwise it might hide close the editor...
14151         if(e.getKey() == e.ENTER){    
14152             return;
14153         }
14154         if(this.cancelOnEsc && e.getKey() == e.ESC){
14155             this.cancelEdit();
14156             return;
14157         } 
14158         this.fireEvent('specialkey', field, e);
14159     
14160     },
14161
14162     /**
14163      * Starts the editing process and shows the editor.
14164      * @param {String/HTMLElement/Element} el The element to edit
14165      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14166       * to the innerHTML of el.
14167      */
14168     startEdit : function(el, value){
14169         if(this.editing){
14170             this.completeEdit();
14171         }
14172         this.boundEl = Roo.get(el);
14173         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14174         if(!this.rendered){
14175             this.render(this.parentEl || document.body);
14176         }
14177         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14178             return;
14179         }
14180         this.startValue = v;
14181         this.field.setValue(v);
14182         if(this.autoSize){
14183             var sz = this.boundEl.getSize();
14184             switch(this.autoSize){
14185                 case "width":
14186                 this.setSize(sz.width,  "");
14187                 break;
14188                 case "height":
14189                 this.setSize("",  sz.height);
14190                 break;
14191                 default:
14192                 this.setSize(sz.width,  sz.height);
14193             }
14194         }
14195         this.el.alignTo(this.boundEl, this.alignment);
14196         this.editing = true;
14197         if(Roo.QuickTips){
14198             Roo.QuickTips.disable();
14199         }
14200         this.show();
14201     },
14202
14203     /**
14204      * Sets the height and width of this editor.
14205      * @param {Number} width The new width
14206      * @param {Number} height The new height
14207      */
14208     setSize : function(w, h){
14209         this.field.setSize(w, h);
14210         if(this.el){
14211             this.el.sync();
14212         }
14213     },
14214
14215     /**
14216      * Realigns the editor to the bound field based on the current alignment config value.
14217      */
14218     realign : function(){
14219         this.el.alignTo(this.boundEl, this.alignment);
14220     },
14221
14222     /**
14223      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14224      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14225      */
14226     completeEdit : function(remainVisible){
14227         if(!this.editing){
14228             return;
14229         }
14230         var v = this.getValue();
14231         if(this.revertInvalid !== false && !this.field.isValid()){
14232             v = this.startValue;
14233             this.cancelEdit(true);
14234         }
14235         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14236             this.editing = false;
14237             this.hide();
14238             return;
14239         }
14240         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14241             this.editing = false;
14242             if(this.updateEl && this.boundEl){
14243                 this.boundEl.update(v);
14244             }
14245             if(remainVisible !== true){
14246                 this.hide();
14247             }
14248             this.fireEvent("complete", this, v, this.startValue);
14249         }
14250     },
14251
14252     // private
14253     onShow : function(){
14254         this.el.show();
14255         if(this.hideEl !== false){
14256             this.boundEl.hide();
14257         }
14258         this.field.show();
14259         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14260             this.fixIEFocus = true;
14261             this.deferredFocus.defer(50, this);
14262         }else{
14263             this.field.focus();
14264         }
14265         this.fireEvent("startedit", this.boundEl, this.startValue);
14266     },
14267
14268     deferredFocus : function(){
14269         if(this.editing){
14270             this.field.focus();
14271         }
14272     },
14273
14274     /**
14275      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14276      * reverted to the original starting value.
14277      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14278      * cancel (defaults to false)
14279      */
14280     cancelEdit : function(remainVisible){
14281         if(this.editing){
14282             this.setValue(this.startValue);
14283             if(remainVisible !== true){
14284                 this.hide();
14285             }
14286         }
14287     },
14288
14289     // private
14290     onBlur : function(){
14291         if(this.allowBlur !== true && this.editing){
14292             this.completeEdit();
14293         }
14294     },
14295
14296     // private
14297     onHide : function(){
14298         if(this.editing){
14299             this.completeEdit();
14300             return;
14301         }
14302         this.field.blur();
14303         if(this.field.collapse){
14304             this.field.collapse();
14305         }
14306         this.el.hide();
14307         if(this.hideEl !== false){
14308             this.boundEl.show();
14309         }
14310         if(Roo.QuickTips){
14311             Roo.QuickTips.enable();
14312         }
14313     },
14314
14315     /**
14316      * Sets the data value of the editor
14317      * @param {Mixed} value Any valid value supported by the underlying field
14318      */
14319     setValue : function(v){
14320         this.field.setValue(v);
14321     },
14322
14323     /**
14324      * Gets the data value of the editor
14325      * @return {Mixed} The data value
14326      */
14327     getValue : function(){
14328         return this.field.getValue();
14329     }
14330 });/*
14331  * Based on:
14332  * Ext JS Library 1.1.1
14333  * Copyright(c) 2006-2007, Ext JS, LLC.
14334  *
14335  * Originally Released Under LGPL - original licence link has changed is not relivant.
14336  *
14337  * Fork - LGPL
14338  * <script type="text/javascript">
14339  */
14340  
14341 /**
14342  * @class Roo.BasicDialog
14343  * @extends Roo.util.Observable
14344  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14345  * <pre><code>
14346 var dlg = new Roo.BasicDialog("my-dlg", {
14347     height: 200,
14348     width: 300,
14349     minHeight: 100,
14350     minWidth: 150,
14351     modal: true,
14352     proxyDrag: true,
14353     shadow: true
14354 });
14355 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14356 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14357 dlg.addButton('Cancel', dlg.hide, dlg);
14358 dlg.show();
14359 </code></pre>
14360   <b>A Dialog should always be a direct child of the body element.</b>
14361  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14362  * @cfg {String} title Default text to display in the title bar (defaults to null)
14363  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14364  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14365  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14366  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14367  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14368  * (defaults to null with no animation)
14369  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14370  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14371  * property for valid values (defaults to 'all')
14372  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14373  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14374  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14375  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14376  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14377  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14378  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14379  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14380  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14381  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14382  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14383  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14384  * draggable = true (defaults to false)
14385  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14386  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14387  * shadow (defaults to false)
14388  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14389  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14390  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14391  * @cfg {Array} buttons Array of buttons
14392  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14393  * @constructor
14394  * Create a new BasicDialog.
14395  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14396  * @param {Object} config Configuration options
14397  */
14398 Roo.BasicDialog = function(el, config){
14399     this.el = Roo.get(el);
14400     var dh = Roo.DomHelper;
14401     if(!this.el && config && config.autoCreate){
14402         if(typeof config.autoCreate == "object"){
14403             if(!config.autoCreate.id){
14404                 config.autoCreate.id = el;
14405             }
14406             this.el = dh.append(document.body,
14407                         config.autoCreate, true);
14408         }else{
14409             this.el = dh.append(document.body,
14410                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14411         }
14412     }
14413     el = this.el;
14414     el.setDisplayed(true);
14415     el.hide = this.hideAction;
14416     this.id = el.id;
14417     el.addClass("x-dlg");
14418
14419     Roo.apply(this, config);
14420
14421     this.proxy = el.createProxy("x-dlg-proxy");
14422     this.proxy.hide = this.hideAction;
14423     this.proxy.setOpacity(.5);
14424     this.proxy.hide();
14425
14426     if(config.width){
14427         el.setWidth(config.width);
14428     }
14429     if(config.height){
14430         el.setHeight(config.height);
14431     }
14432     this.size = el.getSize();
14433     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14434         this.xy = [config.x,config.y];
14435     }else{
14436         this.xy = el.getCenterXY(true);
14437     }
14438     /** The header element @type Roo.Element */
14439     this.header = el.child("> .x-dlg-hd");
14440     /** The body element @type Roo.Element */
14441     this.body = el.child("> .x-dlg-bd");
14442     /** The footer element @type Roo.Element */
14443     this.footer = el.child("> .x-dlg-ft");
14444
14445     if(!this.header){
14446         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14447     }
14448     if(!this.body){
14449         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14450     }
14451
14452     this.header.unselectable();
14453     if(this.title){
14454         this.header.update(this.title);
14455     }
14456     // this element allows the dialog to be focused for keyboard event
14457     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14458     this.focusEl.swallowEvent("click", true);
14459
14460     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14461
14462     // wrap the body and footer for special rendering
14463     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14464     if(this.footer){
14465         this.bwrap.dom.appendChild(this.footer.dom);
14466     }
14467
14468     this.bg = this.el.createChild({
14469         tag: "div", cls:"x-dlg-bg",
14470         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14471     });
14472     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14473
14474
14475     if(this.autoScroll !== false && !this.autoTabs){
14476         this.body.setStyle("overflow", "auto");
14477     }
14478
14479     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14480
14481     if(this.closable !== false){
14482         this.el.addClass("x-dlg-closable");
14483         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14484         this.close.on("click", this.closeClick, this);
14485         this.close.addClassOnOver("x-dlg-close-over");
14486     }
14487     if(this.collapsible !== false){
14488         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14489         this.collapseBtn.on("click", this.collapseClick, this);
14490         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14491         this.header.on("dblclick", this.collapseClick, this);
14492     }
14493     if(this.resizable !== false){
14494         this.el.addClass("x-dlg-resizable");
14495         this.resizer = new Roo.Resizable(el, {
14496             minWidth: this.minWidth || 80,
14497             minHeight:this.minHeight || 80,
14498             handles: this.resizeHandles || "all",
14499             pinned: true
14500         });
14501         this.resizer.on("beforeresize", this.beforeResize, this);
14502         this.resizer.on("resize", this.onResize, this);
14503     }
14504     if(this.draggable !== false){
14505         el.addClass("x-dlg-draggable");
14506         if (!this.proxyDrag) {
14507             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14508         }
14509         else {
14510             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14511         }
14512         dd.setHandleElId(this.header.id);
14513         dd.endDrag = this.endMove.createDelegate(this);
14514         dd.startDrag = this.startMove.createDelegate(this);
14515         dd.onDrag = this.onDrag.createDelegate(this);
14516         dd.scroll = false;
14517         this.dd = dd;
14518     }
14519     if(this.modal){
14520         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14521         this.mask.enableDisplayMode("block");
14522         this.mask.hide();
14523         this.el.addClass("x-dlg-modal");
14524     }
14525     if(this.shadow){
14526         this.shadow = new Roo.Shadow({
14527             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14528             offset : this.shadowOffset
14529         });
14530     }else{
14531         this.shadowOffset = 0;
14532     }
14533     if(Roo.useShims && this.shim !== false){
14534         this.shim = this.el.createShim();
14535         this.shim.hide = this.hideAction;
14536         this.shim.hide();
14537     }else{
14538         this.shim = false;
14539     }
14540     if(this.autoTabs){
14541         this.initTabs();
14542     }
14543     if (this.buttons) { 
14544         var bts= this.buttons;
14545         this.buttons = [];
14546         Roo.each(bts, function(b) {
14547             this.addButton(b);
14548         }, this);
14549     }
14550     
14551     
14552     this.addEvents({
14553         /**
14554          * @event keydown
14555          * Fires when a key is pressed
14556          * @param {Roo.BasicDialog} this
14557          * @param {Roo.EventObject} e
14558          */
14559         "keydown" : true,
14560         /**
14561          * @event move
14562          * Fires when this dialog is moved by the user.
14563          * @param {Roo.BasicDialog} this
14564          * @param {Number} x The new page X
14565          * @param {Number} y The new page Y
14566          */
14567         "move" : true,
14568         /**
14569          * @event resize
14570          * Fires when this dialog is resized by the user.
14571          * @param {Roo.BasicDialog} this
14572          * @param {Number} width The new width
14573          * @param {Number} height The new height
14574          */
14575         "resize" : true,
14576         /**
14577          * @event beforehide
14578          * Fires before this dialog is hidden.
14579          * @param {Roo.BasicDialog} this
14580          */
14581         "beforehide" : true,
14582         /**
14583          * @event hide
14584          * Fires when this dialog is hidden.
14585          * @param {Roo.BasicDialog} this
14586          */
14587         "hide" : true,
14588         /**
14589          * @event beforeshow
14590          * Fires before this dialog is shown.
14591          * @param {Roo.BasicDialog} this
14592          */
14593         "beforeshow" : true,
14594         /**
14595          * @event show
14596          * Fires when this dialog is shown.
14597          * @param {Roo.BasicDialog} this
14598          */
14599         "show" : true
14600     });
14601     el.on("keydown", this.onKeyDown, this);
14602     el.on("mousedown", this.toFront, this);
14603     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14604     this.el.hide();
14605     Roo.DialogManager.register(this);
14606     Roo.BasicDialog.superclass.constructor.call(this);
14607 };
14608
14609 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14610     shadowOffset: Roo.isIE ? 6 : 5,
14611     minHeight: 80,
14612     minWidth: 200,
14613     minButtonWidth: 75,
14614     defaultButton: null,
14615     buttonAlign: "right",
14616     tabTag: 'div',
14617     firstShow: true,
14618
14619     /**
14620      * Sets the dialog title text
14621      * @param {String} text The title text to display
14622      * @return {Roo.BasicDialog} this
14623      */
14624     setTitle : function(text){
14625         this.header.update(text);
14626         return this;
14627     },
14628
14629     // private
14630     closeClick : function(){
14631         this.hide();
14632     },
14633
14634     // private
14635     collapseClick : function(){
14636         this[this.collapsed ? "expand" : "collapse"]();
14637     },
14638
14639     /**
14640      * Collapses the dialog to its minimized state (only the title bar is visible).
14641      * Equivalent to the user clicking the collapse dialog button.
14642      */
14643     collapse : function(){
14644         if(!this.collapsed){
14645             this.collapsed = true;
14646             this.el.addClass("x-dlg-collapsed");
14647             this.restoreHeight = this.el.getHeight();
14648             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14649         }
14650     },
14651
14652     /**
14653      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14654      * clicking the expand dialog button.
14655      */
14656     expand : function(){
14657         if(this.collapsed){
14658             this.collapsed = false;
14659             this.el.removeClass("x-dlg-collapsed");
14660             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14661         }
14662     },
14663
14664     /**
14665      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14666      * @return {Roo.TabPanel} The tabs component
14667      */
14668     initTabs : function(){
14669         var tabs = this.getTabs();
14670         while(tabs.getTab(0)){
14671             tabs.removeTab(0);
14672         }
14673         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14674             var dom = el.dom;
14675             tabs.addTab(Roo.id(dom), dom.title);
14676             dom.title = "";
14677         });
14678         tabs.activate(0);
14679         return tabs;
14680     },
14681
14682     // private
14683     beforeResize : function(){
14684         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14685     },
14686
14687     // private
14688     onResize : function(){
14689         this.refreshSize();
14690         this.syncBodyHeight();
14691         this.adjustAssets();
14692         this.focus();
14693         this.fireEvent("resize", this, this.size.width, this.size.height);
14694     },
14695
14696     // private
14697     onKeyDown : function(e){
14698         if(this.isVisible()){
14699             this.fireEvent("keydown", this, e);
14700         }
14701     },
14702
14703     /**
14704      * Resizes the dialog.
14705      * @param {Number} width
14706      * @param {Number} height
14707      * @return {Roo.BasicDialog} this
14708      */
14709     resizeTo : function(width, height){
14710         this.el.setSize(width, height);
14711         this.size = {width: width, height: height};
14712         this.syncBodyHeight();
14713         if(this.fixedcenter){
14714             this.center();
14715         }
14716         if(this.isVisible()){
14717             this.constrainXY();
14718             this.adjustAssets();
14719         }
14720         this.fireEvent("resize", this, width, height);
14721         return this;
14722     },
14723
14724
14725     /**
14726      * Resizes the dialog to fit the specified content size.
14727      * @param {Number} width
14728      * @param {Number} height
14729      * @return {Roo.BasicDialog} this
14730      */
14731     setContentSize : function(w, h){
14732         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14733         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14734         //if(!this.el.isBorderBox()){
14735             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14736             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14737         //}
14738         if(this.tabs){
14739             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14740             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14741         }
14742         this.resizeTo(w, h);
14743         return this;
14744     },
14745
14746     /**
14747      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14748      * executed in response to a particular key being pressed while the dialog is active.
14749      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14750      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14751      * @param {Function} fn The function to call
14752      * @param {Object} scope (optional) The scope of the function
14753      * @return {Roo.BasicDialog} this
14754      */
14755     addKeyListener : function(key, fn, scope){
14756         var keyCode, shift, ctrl, alt;
14757         if(typeof key == "object" && !(key instanceof Array)){
14758             keyCode = key["key"];
14759             shift = key["shift"];
14760             ctrl = key["ctrl"];
14761             alt = key["alt"];
14762         }else{
14763             keyCode = key;
14764         }
14765         var handler = function(dlg, e){
14766             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14767                 var k = e.getKey();
14768                 if(keyCode instanceof Array){
14769                     for(var i = 0, len = keyCode.length; i < len; i++){
14770                         if(keyCode[i] == k){
14771                           fn.call(scope || window, dlg, k, e);
14772                           return;
14773                         }
14774                     }
14775                 }else{
14776                     if(k == keyCode){
14777                         fn.call(scope || window, dlg, k, e);
14778                     }
14779                 }
14780             }
14781         };
14782         this.on("keydown", handler);
14783         return this;
14784     },
14785
14786     /**
14787      * Returns the TabPanel component (creates it if it doesn't exist).
14788      * Note: If you wish to simply check for the existence of tabs without creating them,
14789      * check for a null 'tabs' property.
14790      * @return {Roo.TabPanel} The tabs component
14791      */
14792     getTabs : function(){
14793         if(!this.tabs){
14794             this.el.addClass("x-dlg-auto-tabs");
14795             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14796             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14797         }
14798         return this.tabs;
14799     },
14800
14801     /**
14802      * Adds a button to the footer section of the dialog.
14803      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14804      * object or a valid Roo.DomHelper element config
14805      * @param {Function} handler The function called when the button is clicked
14806      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14807      * @return {Roo.Button} The new button
14808      */
14809     addButton : function(config, handler, scope){
14810         var dh = Roo.DomHelper;
14811         if(!this.footer){
14812             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14813         }
14814         if(!this.btnContainer){
14815             var tb = this.footer.createChild({
14816
14817                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14818                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14819             }, null, true);
14820             this.btnContainer = tb.firstChild.firstChild.firstChild;
14821         }
14822         var bconfig = {
14823             handler: handler,
14824             scope: scope,
14825             minWidth: this.minButtonWidth,
14826             hideParent:true
14827         };
14828         if(typeof config == "string"){
14829             bconfig.text = config;
14830         }else{
14831             if(config.tag){
14832                 bconfig.dhconfig = config;
14833             }else{
14834                 Roo.apply(bconfig, config);
14835             }
14836         }
14837         var fc = false;
14838         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14839             bconfig.position = Math.max(0, bconfig.position);
14840             fc = this.btnContainer.childNodes[bconfig.position];
14841         }
14842          
14843         var btn = new Roo.Button(
14844             fc ? 
14845                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14846                 : this.btnContainer.appendChild(document.createElement("td")),
14847             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14848             bconfig
14849         );
14850         this.syncBodyHeight();
14851         if(!this.buttons){
14852             /**
14853              * Array of all the buttons that have been added to this dialog via addButton
14854              * @type Array
14855              */
14856             this.buttons = [];
14857         }
14858         this.buttons.push(btn);
14859         return btn;
14860     },
14861
14862     /**
14863      * Sets the default button to be focused when the dialog is displayed.
14864      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14865      * @return {Roo.BasicDialog} this
14866      */
14867     setDefaultButton : function(btn){
14868         this.defaultButton = btn;
14869         return this;
14870     },
14871
14872     // private
14873     getHeaderFooterHeight : function(safe){
14874         var height = 0;
14875         if(this.header){
14876            height += this.header.getHeight();
14877         }
14878         if(this.footer){
14879            var fm = this.footer.getMargins();
14880             height += (this.footer.getHeight()+fm.top+fm.bottom);
14881         }
14882         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14883         height += this.centerBg.getPadding("tb");
14884         return height;
14885     },
14886
14887     // private
14888     syncBodyHeight : function()
14889     {
14890         var bd = this.body, // the text
14891             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14892             bw = this.bwrap;
14893         var height = this.size.height - this.getHeaderFooterHeight(false);
14894         bd.setHeight(height-bd.getMargins("tb"));
14895         var hh = this.header.getHeight();
14896         var h = this.size.height-hh;
14897         cb.setHeight(h);
14898         
14899         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14900         bw.setHeight(h-cb.getPadding("tb"));
14901         
14902         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14903         bd.setWidth(bw.getWidth(true));
14904         if(this.tabs){
14905             this.tabs.syncHeight();
14906             if(Roo.isIE){
14907                 this.tabs.el.repaint();
14908             }
14909         }
14910     },
14911
14912     /**
14913      * Restores the previous state of the dialog if Roo.state is configured.
14914      * @return {Roo.BasicDialog} this
14915      */
14916     restoreState : function(){
14917         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14918         if(box && box.width){
14919             this.xy = [box.x, box.y];
14920             this.resizeTo(box.width, box.height);
14921         }
14922         return this;
14923     },
14924
14925     // private
14926     beforeShow : function(){
14927         this.expand();
14928         if(this.fixedcenter){
14929             this.xy = this.el.getCenterXY(true);
14930         }
14931         if(this.modal){
14932             Roo.get(document.body).addClass("x-body-masked");
14933             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14934             this.mask.show();
14935         }
14936         this.constrainXY();
14937     },
14938
14939     // private
14940     animShow : function(){
14941         var b = Roo.get(this.animateTarget).getBox();
14942         this.proxy.setSize(b.width, b.height);
14943         this.proxy.setLocation(b.x, b.y);
14944         this.proxy.show();
14945         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14946                     true, .35, this.showEl.createDelegate(this));
14947     },
14948
14949     /**
14950      * Shows the dialog.
14951      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14952      * @return {Roo.BasicDialog} this
14953      */
14954     show : function(animateTarget){
14955         if (this.fireEvent("beforeshow", this) === false){
14956             return;
14957         }
14958         if(this.syncHeightBeforeShow){
14959             this.syncBodyHeight();
14960         }else if(this.firstShow){
14961             this.firstShow = false;
14962             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14963         }
14964         this.animateTarget = animateTarget || this.animateTarget;
14965         if(!this.el.isVisible()){
14966             this.beforeShow();
14967             if(this.animateTarget && Roo.get(this.animateTarget)){
14968                 this.animShow();
14969             }else{
14970                 this.showEl();
14971             }
14972         }
14973         return this;
14974     },
14975
14976     // private
14977     showEl : function(){
14978         this.proxy.hide();
14979         this.el.setXY(this.xy);
14980         this.el.show();
14981         this.adjustAssets(true);
14982         this.toFront();
14983         this.focus();
14984         // IE peekaboo bug - fix found by Dave Fenwick
14985         if(Roo.isIE){
14986             this.el.repaint();
14987         }
14988         this.fireEvent("show", this);
14989     },
14990
14991     /**
14992      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14993      * dialog itself will receive focus.
14994      */
14995     focus : function(){
14996         if(this.defaultButton){
14997             this.defaultButton.focus();
14998         }else{
14999             this.focusEl.focus();
15000         }
15001     },
15002
15003     // private
15004     constrainXY : function(){
15005         if(this.constraintoviewport !== false){
15006             if(!this.viewSize){
15007                 if(this.container){
15008                     var s = this.container.getSize();
15009                     this.viewSize = [s.width, s.height];
15010                 }else{
15011                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15012                 }
15013             }
15014             var s = Roo.get(this.container||document).getScroll();
15015
15016             var x = this.xy[0], y = this.xy[1];
15017             var w = this.size.width, h = this.size.height;
15018             var vw = this.viewSize[0], vh = this.viewSize[1];
15019             // only move it if it needs it
15020             var moved = false;
15021             // first validate right/bottom
15022             if(x + w > vw+s.left){
15023                 x = vw - w;
15024                 moved = true;
15025             }
15026             if(y + h > vh+s.top){
15027                 y = vh - h;
15028                 moved = true;
15029             }
15030             // then make sure top/left isn't negative
15031             if(x < s.left){
15032                 x = s.left;
15033                 moved = true;
15034             }
15035             if(y < s.top){
15036                 y = s.top;
15037                 moved = true;
15038             }
15039             if(moved){
15040                 // cache xy
15041                 this.xy = [x, y];
15042                 if(this.isVisible()){
15043                     this.el.setLocation(x, y);
15044                     this.adjustAssets();
15045                 }
15046             }
15047         }
15048     },
15049
15050     // private
15051     onDrag : function(){
15052         if(!this.proxyDrag){
15053             this.xy = this.el.getXY();
15054             this.adjustAssets();
15055         }
15056     },
15057
15058     // private
15059     adjustAssets : function(doShow){
15060         var x = this.xy[0], y = this.xy[1];
15061         var w = this.size.width, h = this.size.height;
15062         if(doShow === true){
15063             if(this.shadow){
15064                 this.shadow.show(this.el);
15065             }
15066             if(this.shim){
15067                 this.shim.show();
15068             }
15069         }
15070         if(this.shadow && this.shadow.isVisible()){
15071             this.shadow.show(this.el);
15072         }
15073         if(this.shim && this.shim.isVisible()){
15074             this.shim.setBounds(x, y, w, h);
15075         }
15076     },
15077
15078     // private
15079     adjustViewport : function(w, h){
15080         if(!w || !h){
15081             w = Roo.lib.Dom.getViewWidth();
15082             h = Roo.lib.Dom.getViewHeight();
15083         }
15084         // cache the size
15085         this.viewSize = [w, h];
15086         if(this.modal && this.mask.isVisible()){
15087             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15088             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15089         }
15090         if(this.isVisible()){
15091             this.constrainXY();
15092         }
15093     },
15094
15095     /**
15096      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15097      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15098      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15099      */
15100     destroy : function(removeEl){
15101         if(this.isVisible()){
15102             this.animateTarget = null;
15103             this.hide();
15104         }
15105         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15106         if(this.tabs){
15107             this.tabs.destroy(removeEl);
15108         }
15109         Roo.destroy(
15110              this.shim,
15111              this.proxy,
15112              this.resizer,
15113              this.close,
15114              this.mask
15115         );
15116         if(this.dd){
15117             this.dd.unreg();
15118         }
15119         if(this.buttons){
15120            for(var i = 0, len = this.buttons.length; i < len; i++){
15121                this.buttons[i].destroy();
15122            }
15123         }
15124         this.el.removeAllListeners();
15125         if(removeEl === true){
15126             this.el.update("");
15127             this.el.remove();
15128         }
15129         Roo.DialogManager.unregister(this);
15130     },
15131
15132     // private
15133     startMove : function(){
15134         if(this.proxyDrag){
15135             this.proxy.show();
15136         }
15137         if(this.constraintoviewport !== false){
15138             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15139         }
15140     },
15141
15142     // private
15143     endMove : function(){
15144         if(!this.proxyDrag){
15145             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15146         }else{
15147             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15148             this.proxy.hide();
15149         }
15150         this.refreshSize();
15151         this.adjustAssets();
15152         this.focus();
15153         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15154     },
15155
15156     /**
15157      * Brings this dialog to the front of any other visible dialogs
15158      * @return {Roo.BasicDialog} this
15159      */
15160     toFront : function(){
15161         Roo.DialogManager.bringToFront(this);
15162         return this;
15163     },
15164
15165     /**
15166      * Sends this dialog to the back (under) of any other visible dialogs
15167      * @return {Roo.BasicDialog} this
15168      */
15169     toBack : function(){
15170         Roo.DialogManager.sendToBack(this);
15171         return this;
15172     },
15173
15174     /**
15175      * Centers this dialog in the viewport
15176      * @return {Roo.BasicDialog} this
15177      */
15178     center : function(){
15179         var xy = this.el.getCenterXY(true);
15180         this.moveTo(xy[0], xy[1]);
15181         return this;
15182     },
15183
15184     /**
15185      * Moves the dialog's top-left corner to the specified point
15186      * @param {Number} x
15187      * @param {Number} y
15188      * @return {Roo.BasicDialog} this
15189      */
15190     moveTo : function(x, y){
15191         this.xy = [x,y];
15192         if(this.isVisible()){
15193             this.el.setXY(this.xy);
15194             this.adjustAssets();
15195         }
15196         return this;
15197     },
15198
15199     /**
15200      * Aligns the dialog to the specified element
15201      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15202      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15203      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15204      * @return {Roo.BasicDialog} this
15205      */
15206     alignTo : function(element, position, offsets){
15207         this.xy = this.el.getAlignToXY(element, position, offsets);
15208         if(this.isVisible()){
15209             this.el.setXY(this.xy);
15210             this.adjustAssets();
15211         }
15212         return this;
15213     },
15214
15215     /**
15216      * Anchors an element to another element and realigns it when the window is resized.
15217      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15218      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15219      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15220      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15221      * is a number, it is used as the buffer delay (defaults to 50ms).
15222      * @return {Roo.BasicDialog} this
15223      */
15224     anchorTo : function(el, alignment, offsets, monitorScroll){
15225         var action = function(){
15226             this.alignTo(el, alignment, offsets);
15227         };
15228         Roo.EventManager.onWindowResize(action, this);
15229         var tm = typeof monitorScroll;
15230         if(tm != 'undefined'){
15231             Roo.EventManager.on(window, 'scroll', action, this,
15232                 {buffer: tm == 'number' ? monitorScroll : 50});
15233         }
15234         action.call(this);
15235         return this;
15236     },
15237
15238     /**
15239      * Returns true if the dialog is visible
15240      * @return {Boolean}
15241      */
15242     isVisible : function(){
15243         return this.el.isVisible();
15244     },
15245
15246     // private
15247     animHide : function(callback){
15248         var b = Roo.get(this.animateTarget).getBox();
15249         this.proxy.show();
15250         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15251         this.el.hide();
15252         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15253                     this.hideEl.createDelegate(this, [callback]));
15254     },
15255
15256     /**
15257      * Hides the dialog.
15258      * @param {Function} callback (optional) Function to call when the dialog is hidden
15259      * @return {Roo.BasicDialog} this
15260      */
15261     hide : function(callback){
15262         if (this.fireEvent("beforehide", this) === false){
15263             return;
15264         }
15265         if(this.shadow){
15266             this.shadow.hide();
15267         }
15268         if(this.shim) {
15269           this.shim.hide();
15270         }
15271         // sometimes animateTarget seems to get set.. causing problems...
15272         // this just double checks..
15273         if(this.animateTarget && Roo.get(this.animateTarget)) {
15274            this.animHide(callback);
15275         }else{
15276             this.el.hide();
15277             this.hideEl(callback);
15278         }
15279         return this;
15280     },
15281
15282     // private
15283     hideEl : function(callback){
15284         this.proxy.hide();
15285         if(this.modal){
15286             this.mask.hide();
15287             Roo.get(document.body).removeClass("x-body-masked");
15288         }
15289         this.fireEvent("hide", this);
15290         if(typeof callback == "function"){
15291             callback();
15292         }
15293     },
15294
15295     // private
15296     hideAction : function(){
15297         this.setLeft("-10000px");
15298         this.setTop("-10000px");
15299         this.setStyle("visibility", "hidden");
15300     },
15301
15302     // private
15303     refreshSize : function(){
15304         this.size = this.el.getSize();
15305         this.xy = this.el.getXY();
15306         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15307     },
15308
15309     // private
15310     // z-index is managed by the DialogManager and may be overwritten at any time
15311     setZIndex : function(index){
15312         if(this.modal){
15313             this.mask.setStyle("z-index", index);
15314         }
15315         if(this.shim){
15316             this.shim.setStyle("z-index", ++index);
15317         }
15318         if(this.shadow){
15319             this.shadow.setZIndex(++index);
15320         }
15321         this.el.setStyle("z-index", ++index);
15322         if(this.proxy){
15323             this.proxy.setStyle("z-index", ++index);
15324         }
15325         if(this.resizer){
15326             this.resizer.proxy.setStyle("z-index", ++index);
15327         }
15328
15329         this.lastZIndex = index;
15330     },
15331
15332     /**
15333      * Returns the element for this dialog
15334      * @return {Roo.Element} The underlying dialog Element
15335      */
15336     getEl : function(){
15337         return this.el;
15338     }
15339 });
15340
15341 /**
15342  * @class Roo.DialogManager
15343  * Provides global access to BasicDialogs that have been created and
15344  * support for z-indexing (layering) multiple open dialogs.
15345  */
15346 Roo.DialogManager = function(){
15347     var list = {};
15348     var accessList = [];
15349     var front = null;
15350
15351     // private
15352     var sortDialogs = function(d1, d2){
15353         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15354     };
15355
15356     // private
15357     var orderDialogs = function(){
15358         accessList.sort(sortDialogs);
15359         var seed = Roo.DialogManager.zseed;
15360         for(var i = 0, len = accessList.length; i < len; i++){
15361             var dlg = accessList[i];
15362             if(dlg){
15363                 dlg.setZIndex(seed + (i*10));
15364             }
15365         }
15366     };
15367
15368     return {
15369         /**
15370          * The starting z-index for BasicDialogs (defaults to 9000)
15371          * @type Number The z-index value
15372          */
15373         zseed : 9000,
15374
15375         // private
15376         register : function(dlg){
15377             list[dlg.id] = dlg;
15378             accessList.push(dlg);
15379         },
15380
15381         // private
15382         unregister : function(dlg){
15383             delete list[dlg.id];
15384             var i=0;
15385             var len=0;
15386             if(!accessList.indexOf){
15387                 for(  i = 0, len = accessList.length; i < len; i++){
15388                     if(accessList[i] == dlg){
15389                         accessList.splice(i, 1);
15390                         return;
15391                     }
15392                 }
15393             }else{
15394                  i = accessList.indexOf(dlg);
15395                 if(i != -1){
15396                     accessList.splice(i, 1);
15397                 }
15398             }
15399         },
15400
15401         /**
15402          * Gets a registered dialog by id
15403          * @param {String/Object} id The id of the dialog or a dialog
15404          * @return {Roo.BasicDialog} this
15405          */
15406         get : function(id){
15407             return typeof id == "object" ? id : list[id];
15408         },
15409
15410         /**
15411          * Brings the specified dialog to the front
15412          * @param {String/Object} dlg The id of the dialog or a dialog
15413          * @return {Roo.BasicDialog} this
15414          */
15415         bringToFront : function(dlg){
15416             dlg = this.get(dlg);
15417             if(dlg != front){
15418                 front = dlg;
15419                 dlg._lastAccess = new Date().getTime();
15420                 orderDialogs();
15421             }
15422             return dlg;
15423         },
15424
15425         /**
15426          * Sends the specified dialog to the back
15427          * @param {String/Object} dlg The id of the dialog or a dialog
15428          * @return {Roo.BasicDialog} this
15429          */
15430         sendToBack : function(dlg){
15431             dlg = this.get(dlg);
15432             dlg._lastAccess = -(new Date().getTime());
15433             orderDialogs();
15434             return dlg;
15435         },
15436
15437         /**
15438          * Hides all dialogs
15439          */
15440         hideAll : function(){
15441             for(var id in list){
15442                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15443                     list[id].hide();
15444                 }
15445             }
15446         }
15447     };
15448 }();
15449
15450 /**
15451  * @class Roo.LayoutDialog
15452  * @extends Roo.BasicDialog
15453  * Dialog which provides adjustments for working with a layout in a Dialog.
15454  * Add your necessary layout config options to the dialog's config.<br>
15455  * Example usage (including a nested layout):
15456  * <pre><code>
15457 if(!dialog){
15458     dialog = new Roo.LayoutDialog("download-dlg", {
15459         modal: true,
15460         width:600,
15461         height:450,
15462         shadow:true,
15463         minWidth:500,
15464         minHeight:350,
15465         autoTabs:true,
15466         proxyDrag:true,
15467         // layout config merges with the dialog config
15468         center:{
15469             tabPosition: "top",
15470             alwaysShowTabs: true
15471         }
15472     });
15473     dialog.addKeyListener(27, dialog.hide, dialog);
15474     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15475     dialog.addButton("Build It!", this.getDownload, this);
15476
15477     // we can even add nested layouts
15478     var innerLayout = new Roo.BorderLayout("dl-inner", {
15479         east: {
15480             initialSize: 200,
15481             autoScroll:true,
15482             split:true
15483         },
15484         center: {
15485             autoScroll:true
15486         }
15487     });
15488     innerLayout.beginUpdate();
15489     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15490     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15491     innerLayout.endUpdate(true);
15492
15493     var layout = dialog.getLayout();
15494     layout.beginUpdate();
15495     layout.add("center", new Roo.ContentPanel("standard-panel",
15496                         {title: "Download the Source", fitToFrame:true}));
15497     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15498                {title: "Build your own roo.js"}));
15499     layout.getRegion("center").showPanel(sp);
15500     layout.endUpdate();
15501 }
15502 </code></pre>
15503     * @constructor
15504     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15505     * @param {Object} config configuration options
15506   */
15507 Roo.LayoutDialog = function(el, cfg){
15508     
15509     var config=  cfg;
15510     if (typeof(cfg) == 'undefined') {
15511         config = Roo.apply({}, el);
15512         // not sure why we use documentElement here.. - it should always be body.
15513         // IE7 borks horribly if we use documentElement.
15514         // webkit also does not like documentElement - it creates a body element...
15515         el = Roo.get( document.body || document.documentElement ).createChild();
15516         //config.autoCreate = true;
15517     }
15518     
15519     
15520     config.autoTabs = false;
15521     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15522     this.body.setStyle({overflow:"hidden", position:"relative"});
15523     this.layout = new Roo.BorderLayout(this.body.dom, config);
15524     this.layout.monitorWindowResize = false;
15525     this.el.addClass("x-dlg-auto-layout");
15526     // fix case when center region overwrites center function
15527     this.center = Roo.BasicDialog.prototype.center;
15528     this.on("show", this.layout.layout, this.layout, true);
15529     if (config.items) {
15530         var xitems = config.items;
15531         delete config.items;
15532         Roo.each(xitems, this.addxtype, this);
15533     }
15534     
15535     
15536 };
15537 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15538     /**
15539      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15540      * @deprecated
15541      */
15542     endUpdate : function(){
15543         this.layout.endUpdate();
15544     },
15545
15546     /**
15547      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15548      *  @deprecated
15549      */
15550     beginUpdate : function(){
15551         this.layout.beginUpdate();
15552     },
15553
15554     /**
15555      * Get the BorderLayout for this dialog
15556      * @return {Roo.BorderLayout}
15557      */
15558     getLayout : function(){
15559         return this.layout;
15560     },
15561
15562     showEl : function(){
15563         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15564         if(Roo.isIE7){
15565             this.layout.layout();
15566         }
15567     },
15568
15569     // private
15570     // Use the syncHeightBeforeShow config option to control this automatically
15571     syncBodyHeight : function(){
15572         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15573         if(this.layout){this.layout.layout();}
15574     },
15575     
15576       /**
15577      * Add an xtype element (actually adds to the layout.)
15578      * @return {Object} xdata xtype object data.
15579      */
15580     
15581     addxtype : function(c) {
15582         return this.layout.addxtype(c);
15583     }
15584 });/*
15585  * Based on:
15586  * Ext JS Library 1.1.1
15587  * Copyright(c) 2006-2007, Ext JS, LLC.
15588  *
15589  * Originally Released Under LGPL - original licence link has changed is not relivant.
15590  *
15591  * Fork - LGPL
15592  * <script type="text/javascript">
15593  */
15594  
15595 /**
15596  * @class Roo.MessageBox
15597  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15598  * Example usage:
15599  *<pre><code>
15600 // Basic alert:
15601 Roo.Msg.alert('Status', 'Changes saved successfully.');
15602
15603 // Prompt for user data:
15604 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15605     if (btn == 'ok'){
15606         // process text value...
15607     }
15608 });
15609
15610 // Show a dialog using config options:
15611 Roo.Msg.show({
15612    title:'Save Changes?',
15613    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15614    buttons: Roo.Msg.YESNOCANCEL,
15615    fn: processResult,
15616    animEl: 'elId'
15617 });
15618 </code></pre>
15619  * @singleton
15620  */
15621 Roo.MessageBox = function(){
15622     var dlg, opt, mask, waitTimer;
15623     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15624     var buttons, activeTextEl, bwidth;
15625
15626     // private
15627     var handleButton = function(button){
15628         dlg.hide();
15629         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15630     };
15631
15632     // private
15633     var handleHide = function(){
15634         if(opt && opt.cls){
15635             dlg.el.removeClass(opt.cls);
15636         }
15637         if(waitTimer){
15638             Roo.TaskMgr.stop(waitTimer);
15639             waitTimer = null;
15640         }
15641     };
15642
15643     // private
15644     var updateButtons = function(b){
15645         var width = 0;
15646         if(!b){
15647             buttons["ok"].hide();
15648             buttons["cancel"].hide();
15649             buttons["yes"].hide();
15650             buttons["no"].hide();
15651             dlg.footer.dom.style.display = 'none';
15652             return width;
15653         }
15654         dlg.footer.dom.style.display = '';
15655         for(var k in buttons){
15656             if(typeof buttons[k] != "function"){
15657                 if(b[k]){
15658                     buttons[k].show();
15659                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15660                     width += buttons[k].el.getWidth()+15;
15661                 }else{
15662                     buttons[k].hide();
15663                 }
15664             }
15665         }
15666         return width;
15667     };
15668
15669     // private
15670     var handleEsc = function(d, k, e){
15671         if(opt && opt.closable !== false){
15672             dlg.hide();
15673         }
15674         if(e){
15675             e.stopEvent();
15676         }
15677     };
15678
15679     return {
15680         /**
15681          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15682          * @return {Roo.BasicDialog} The BasicDialog element
15683          */
15684         getDialog : function(){
15685            if(!dlg){
15686                 dlg = new Roo.BasicDialog("x-msg-box", {
15687                     autoCreate : true,
15688                     shadow: true,
15689                     draggable: true,
15690                     resizable:false,
15691                     constraintoviewport:false,
15692                     fixedcenter:true,
15693                     collapsible : false,
15694                     shim:true,
15695                     modal: true,
15696                     width:400, height:100,
15697                     buttonAlign:"center",
15698                     closeClick : function(){
15699                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15700                             handleButton("no");
15701                         }else{
15702                             handleButton("cancel");
15703                         }
15704                     }
15705                 });
15706                 dlg.on("hide", handleHide);
15707                 mask = dlg.mask;
15708                 dlg.addKeyListener(27, handleEsc);
15709                 buttons = {};
15710                 var bt = this.buttonText;
15711                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15712                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15713                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15714                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15715                 bodyEl = dlg.body.createChild({
15716
15717                     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>'
15718                 });
15719                 msgEl = bodyEl.dom.firstChild;
15720                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15721                 textboxEl.enableDisplayMode();
15722                 textboxEl.addKeyListener([10,13], function(){
15723                     if(dlg.isVisible() && opt && opt.buttons){
15724                         if(opt.buttons.ok){
15725                             handleButton("ok");
15726                         }else if(opt.buttons.yes){
15727                             handleButton("yes");
15728                         }
15729                     }
15730                 });
15731                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15732                 textareaEl.enableDisplayMode();
15733                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15734                 progressEl.enableDisplayMode();
15735                 var pf = progressEl.dom.firstChild;
15736                 if (pf) {
15737                     pp = Roo.get(pf.firstChild);
15738                     pp.setHeight(pf.offsetHeight);
15739                 }
15740                 
15741             }
15742             return dlg;
15743         },
15744
15745         /**
15746          * Updates the message box body text
15747          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15748          * the XHTML-compliant non-breaking space character '&amp;#160;')
15749          * @return {Roo.MessageBox} This message box
15750          */
15751         updateText : function(text){
15752             if(!dlg.isVisible() && !opt.width){
15753                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15754             }
15755             msgEl.innerHTML = text || '&#160;';
15756       
15757             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15758             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15759             var w = Math.max(
15760                     Math.min(opt.width || cw , this.maxWidth), 
15761                     Math.max(opt.minWidth || this.minWidth, bwidth)
15762             );
15763             if(opt.prompt){
15764                 activeTextEl.setWidth(w);
15765             }
15766             if(dlg.isVisible()){
15767                 dlg.fixedcenter = false;
15768             }
15769             // to big, make it scroll. = But as usual stupid IE does not support
15770             // !important..
15771             
15772             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15773                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15774                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15775             } else {
15776                 bodyEl.dom.style.height = '';
15777                 bodyEl.dom.style.overflowY = '';
15778             }
15779             if (cw > w) {
15780                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15781             } else {
15782                 bodyEl.dom.style.overflowX = '';
15783             }
15784             
15785             dlg.setContentSize(w, bodyEl.getHeight());
15786             if(dlg.isVisible()){
15787                 dlg.fixedcenter = true;
15788             }
15789             return this;
15790         },
15791
15792         /**
15793          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15794          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15795          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15796          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15797          * @return {Roo.MessageBox} This message box
15798          */
15799         updateProgress : function(value, text){
15800             if(text){
15801                 this.updateText(text);
15802             }
15803             if (pp) { // weird bug on my firefox - for some reason this is not defined
15804                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15805             }
15806             return this;
15807         },        
15808
15809         /**
15810          * Returns true if the message box is currently displayed
15811          * @return {Boolean} True if the message box is visible, else false
15812          */
15813         isVisible : function(){
15814             return dlg && dlg.isVisible();  
15815         },
15816
15817         /**
15818          * Hides the message box if it is displayed
15819          */
15820         hide : function(){
15821             if(this.isVisible()){
15822                 dlg.hide();
15823             }  
15824         },
15825
15826         /**
15827          * Displays a new message box, or reinitializes an existing message box, based on the config options
15828          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15829          * The following config object properties are supported:
15830          * <pre>
15831 Property    Type             Description
15832 ----------  ---------------  ------------------------------------------------------------------------------------
15833 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15834                                    closes (defaults to undefined)
15835 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15836                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15837 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15838                                    progress and wait dialogs will ignore this property and always hide the
15839                                    close button as they can only be closed programmatically.
15840 cls               String           A custom CSS class to apply to the message box element
15841 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15842                                    displayed (defaults to 75)
15843 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15844                                    function will be btn (the name of the button that was clicked, if applicable,
15845                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15846                                    Progress and wait dialogs will ignore this option since they do not respond to
15847                                    user actions and can only be closed programmatically, so any required function
15848                                    should be called by the same code after it closes the dialog.
15849 icon              String           A CSS class that provides a background image to be used as an icon for
15850                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15851 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15852 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15853 modal             Boolean          False to allow user interaction with the page while the message box is
15854                                    displayed (defaults to true)
15855 msg               String           A string that will replace the existing message box body text (defaults
15856                                    to the XHTML-compliant non-breaking space character '&#160;')
15857 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15858 progress          Boolean          True to display a progress bar (defaults to false)
15859 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15860 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15861 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15862 title             String           The title text
15863 value             String           The string value to set into the active textbox element if displayed
15864 wait              Boolean          True to display a progress bar (defaults to false)
15865 width             Number           The width of the dialog in pixels
15866 </pre>
15867          *
15868          * Example usage:
15869          * <pre><code>
15870 Roo.Msg.show({
15871    title: 'Address',
15872    msg: 'Please enter your address:',
15873    width: 300,
15874    buttons: Roo.MessageBox.OKCANCEL,
15875    multiline: true,
15876    fn: saveAddress,
15877    animEl: 'addAddressBtn'
15878 });
15879 </code></pre>
15880          * @param {Object} config Configuration options
15881          * @return {Roo.MessageBox} This message box
15882          */
15883         show : function(options)
15884         {
15885             
15886             // this causes nightmares if you show one dialog after another
15887             // especially on callbacks..
15888              
15889             if(this.isVisible()){
15890                 
15891                 this.hide();
15892                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15893                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15894                 Roo.log("New Dialog Message:" +  options.msg )
15895                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15896                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15897                 
15898             }
15899             var d = this.getDialog();
15900             opt = options;
15901             d.setTitle(opt.title || "&#160;");
15902             d.close.setDisplayed(opt.closable !== false);
15903             activeTextEl = textboxEl;
15904             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15905             if(opt.prompt){
15906                 if(opt.multiline){
15907                     textboxEl.hide();
15908                     textareaEl.show();
15909                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15910                         opt.multiline : this.defaultTextHeight);
15911                     activeTextEl = textareaEl;
15912                 }else{
15913                     textboxEl.show();
15914                     textareaEl.hide();
15915                 }
15916             }else{
15917                 textboxEl.hide();
15918                 textareaEl.hide();
15919             }
15920             progressEl.setDisplayed(opt.progress === true);
15921             this.updateProgress(0);
15922             activeTextEl.dom.value = opt.value || "";
15923             if(opt.prompt){
15924                 dlg.setDefaultButton(activeTextEl);
15925             }else{
15926                 var bs = opt.buttons;
15927                 var db = null;
15928                 if(bs && bs.ok){
15929                     db = buttons["ok"];
15930                 }else if(bs && bs.yes){
15931                     db = buttons["yes"];
15932                 }
15933                 dlg.setDefaultButton(db);
15934             }
15935             bwidth = updateButtons(opt.buttons);
15936             this.updateText(opt.msg);
15937             if(opt.cls){
15938                 d.el.addClass(opt.cls);
15939             }
15940             d.proxyDrag = opt.proxyDrag === true;
15941             d.modal = opt.modal !== false;
15942             d.mask = opt.modal !== false ? mask : false;
15943             if(!d.isVisible()){
15944                 // force it to the end of the z-index stack so it gets a cursor in FF
15945                 document.body.appendChild(dlg.el.dom);
15946                 d.animateTarget = null;
15947                 d.show(options.animEl);
15948             }
15949             return this;
15950         },
15951
15952         /**
15953          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15954          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15955          * and closing the message box when the process is complete.
15956          * @param {String} title The title bar text
15957          * @param {String} msg The message box body text
15958          * @return {Roo.MessageBox} This message box
15959          */
15960         progress : function(title, msg){
15961             this.show({
15962                 title : title,
15963                 msg : msg,
15964                 buttons: false,
15965                 progress:true,
15966                 closable:false,
15967                 minWidth: this.minProgressWidth,
15968                 modal : true
15969             });
15970             return this;
15971         },
15972
15973         /**
15974          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15975          * If a callback function is passed it will be called after the user clicks the button, and the
15976          * id of the button that was clicked will be passed as the only parameter to the callback
15977          * (could also be the top-right close button).
15978          * @param {String} title The title bar text
15979          * @param {String} msg The message box body text
15980          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15981          * @param {Object} scope (optional) The scope of the callback function
15982          * @return {Roo.MessageBox} This message box
15983          */
15984         alert : function(title, msg, fn, scope){
15985             this.show({
15986                 title : title,
15987                 msg : msg,
15988                 buttons: this.OK,
15989                 fn: fn,
15990                 scope : scope,
15991                 modal : true
15992             });
15993             return this;
15994         },
15995
15996         /**
15997          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15998          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15999          * You are responsible for closing the message box when the process is complete.
16000          * @param {String} msg The message box body text
16001          * @param {String} title (optional) The title bar text
16002          * @return {Roo.MessageBox} This message box
16003          */
16004         wait : function(msg, title){
16005             this.show({
16006                 title : title,
16007                 msg : msg,
16008                 buttons: false,
16009                 closable:false,
16010                 progress:true,
16011                 modal:true,
16012                 width:300,
16013                 wait:true
16014             });
16015             waitTimer = Roo.TaskMgr.start({
16016                 run: function(i){
16017                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16018                 },
16019                 interval: 1000
16020             });
16021             return this;
16022         },
16023
16024         /**
16025          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16026          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16027          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16028          * @param {String} title The title bar text
16029          * @param {String} msg The message box body text
16030          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16031          * @param {Object} scope (optional) The scope of the callback function
16032          * @return {Roo.MessageBox} This message box
16033          */
16034         confirm : function(title, msg, fn, scope){
16035             this.show({
16036                 title : title,
16037                 msg : msg,
16038                 buttons: this.YESNO,
16039                 fn: fn,
16040                 scope : scope,
16041                 modal : true
16042             });
16043             return this;
16044         },
16045
16046         /**
16047          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16048          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16049          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16050          * (could also be the top-right close button) and the text that was entered will be passed as the two
16051          * parameters to the callback.
16052          * @param {String} title The title bar text
16053          * @param {String} msg The message box body text
16054          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16055          * @param {Object} scope (optional) The scope of the callback function
16056          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16057          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16058          * @return {Roo.MessageBox} This message box
16059          */
16060         prompt : function(title, msg, fn, scope, multiline){
16061             this.show({
16062                 title : title,
16063                 msg : msg,
16064                 buttons: this.OKCANCEL,
16065                 fn: fn,
16066                 minWidth:250,
16067                 scope : scope,
16068                 prompt:true,
16069                 multiline: multiline,
16070                 modal : true
16071             });
16072             return this;
16073         },
16074
16075         /**
16076          * Button config that displays a single OK button
16077          * @type Object
16078          */
16079         OK : {ok:true},
16080         /**
16081          * Button config that displays Yes and No buttons
16082          * @type Object
16083          */
16084         YESNO : {yes:true, no:true},
16085         /**
16086          * Button config that displays OK and Cancel buttons
16087          * @type Object
16088          */
16089         OKCANCEL : {ok:true, cancel:true},
16090         /**
16091          * Button config that displays Yes, No and Cancel buttons
16092          * @type Object
16093          */
16094         YESNOCANCEL : {yes:true, no:true, cancel:true},
16095
16096         /**
16097          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16098          * @type Number
16099          */
16100         defaultTextHeight : 75,
16101         /**
16102          * The maximum width in pixels of the message box (defaults to 600)
16103          * @type Number
16104          */
16105         maxWidth : 600,
16106         /**
16107          * The minimum width in pixels of the message box (defaults to 100)
16108          * @type Number
16109          */
16110         minWidth : 100,
16111         /**
16112          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16113          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16114          * @type Number
16115          */
16116         minProgressWidth : 250,
16117         /**
16118          * An object containing the default button text strings that can be overriden for localized language support.
16119          * Supported properties are: ok, cancel, yes and no.
16120          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16121          * @type Object
16122          */
16123         buttonText : {
16124             ok : "OK",
16125             cancel : "Cancel",
16126             yes : "Yes",
16127             no : "No"
16128         }
16129     };
16130 }();
16131
16132 /**
16133  * Shorthand for {@link Roo.MessageBox}
16134  */
16135 Roo.Msg = Roo.MessageBox;/*
16136  * Based on:
16137  * Ext JS Library 1.1.1
16138  * Copyright(c) 2006-2007, Ext JS, LLC.
16139  *
16140  * Originally Released Under LGPL - original licence link has changed is not relivant.
16141  *
16142  * Fork - LGPL
16143  * <script type="text/javascript">
16144  */
16145 /**
16146  * @class Roo.QuickTips
16147  * Provides attractive and customizable tooltips for any element.
16148  * @singleton
16149  */
16150 Roo.QuickTips = function(){
16151     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16152     var ce, bd, xy, dd;
16153     var visible = false, disabled = true, inited = false;
16154     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16155     
16156     var onOver = function(e){
16157         if(disabled){
16158             return;
16159         }
16160         var t = e.getTarget();
16161         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16162             return;
16163         }
16164         if(ce && t == ce.el){
16165             clearTimeout(hideProc);
16166             return;
16167         }
16168         if(t && tagEls[t.id]){
16169             tagEls[t.id].el = t;
16170             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16171             return;
16172         }
16173         var ttp, et = Roo.fly(t);
16174         var ns = cfg.namespace;
16175         if(tm.interceptTitles && t.title){
16176             ttp = t.title;
16177             t.qtip = ttp;
16178             t.removeAttribute("title");
16179             e.preventDefault();
16180         }else{
16181             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16182         }
16183         if(ttp){
16184             showProc = show.defer(tm.showDelay, tm, [{
16185                 el: t, 
16186                 text: ttp, 
16187                 width: et.getAttributeNS(ns, cfg.width),
16188                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16189                 title: et.getAttributeNS(ns, cfg.title),
16190                     cls: et.getAttributeNS(ns, cfg.cls)
16191             }]);
16192         }
16193     };
16194     
16195     var onOut = function(e){
16196         clearTimeout(showProc);
16197         var t = e.getTarget();
16198         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16199             hideProc = setTimeout(hide, tm.hideDelay);
16200         }
16201     };
16202     
16203     var onMove = function(e){
16204         if(disabled){
16205             return;
16206         }
16207         xy = e.getXY();
16208         xy[1] += 18;
16209         if(tm.trackMouse && ce){
16210             el.setXY(xy);
16211         }
16212     };
16213     
16214     var onDown = function(e){
16215         clearTimeout(showProc);
16216         clearTimeout(hideProc);
16217         if(!e.within(el)){
16218             if(tm.hideOnClick){
16219                 hide();
16220                 tm.disable();
16221                 tm.enable.defer(100, tm);
16222             }
16223         }
16224     };
16225     
16226     var getPad = function(){
16227         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16228     };
16229
16230     var show = function(o){
16231         if(disabled){
16232             return;
16233         }
16234         clearTimeout(dismissProc);
16235         ce = o;
16236         if(removeCls){ // in case manually hidden
16237             el.removeClass(removeCls);
16238             removeCls = null;
16239         }
16240         if(ce.cls){
16241             el.addClass(ce.cls);
16242             removeCls = ce.cls;
16243         }
16244         if(ce.title){
16245             tipTitle.update(ce.title);
16246             tipTitle.show();
16247         }else{
16248             tipTitle.update('');
16249             tipTitle.hide();
16250         }
16251         el.dom.style.width  = tm.maxWidth+'px';
16252         //tipBody.dom.style.width = '';
16253         tipBodyText.update(o.text);
16254         var p = getPad(), w = ce.width;
16255         if(!w){
16256             var td = tipBodyText.dom;
16257             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16258             if(aw > tm.maxWidth){
16259                 w = tm.maxWidth;
16260             }else if(aw < tm.minWidth){
16261                 w = tm.minWidth;
16262             }else{
16263                 w = aw;
16264             }
16265         }
16266         //tipBody.setWidth(w);
16267         el.setWidth(parseInt(w, 10) + p);
16268         if(ce.autoHide === false){
16269             close.setDisplayed(true);
16270             if(dd){
16271                 dd.unlock();
16272             }
16273         }else{
16274             close.setDisplayed(false);
16275             if(dd){
16276                 dd.lock();
16277             }
16278         }
16279         if(xy){
16280             el.avoidY = xy[1]-18;
16281             el.setXY(xy);
16282         }
16283         if(tm.animate){
16284             el.setOpacity(.1);
16285             el.setStyle("visibility", "visible");
16286             el.fadeIn({callback: afterShow});
16287         }else{
16288             afterShow();
16289         }
16290     };
16291     
16292     var afterShow = function(){
16293         if(ce){
16294             el.show();
16295             esc.enable();
16296             if(tm.autoDismiss && ce.autoHide !== false){
16297                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16298             }
16299         }
16300     };
16301     
16302     var hide = function(noanim){
16303         clearTimeout(dismissProc);
16304         clearTimeout(hideProc);
16305         ce = null;
16306         if(el.isVisible()){
16307             esc.disable();
16308             if(noanim !== true && tm.animate){
16309                 el.fadeOut({callback: afterHide});
16310             }else{
16311                 afterHide();
16312             } 
16313         }
16314     };
16315     
16316     var afterHide = function(){
16317         el.hide();
16318         if(removeCls){
16319             el.removeClass(removeCls);
16320             removeCls = null;
16321         }
16322     };
16323     
16324     return {
16325         /**
16326         * @cfg {Number} minWidth
16327         * The minimum width of the quick tip (defaults to 40)
16328         */
16329        minWidth : 40,
16330         /**
16331         * @cfg {Number} maxWidth
16332         * The maximum width of the quick tip (defaults to 300)
16333         */
16334        maxWidth : 300,
16335         /**
16336         * @cfg {Boolean} interceptTitles
16337         * True to automatically use the element's DOM title value if available (defaults to false)
16338         */
16339        interceptTitles : false,
16340         /**
16341         * @cfg {Boolean} trackMouse
16342         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16343         */
16344        trackMouse : false,
16345         /**
16346         * @cfg {Boolean} hideOnClick
16347         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16348         */
16349        hideOnClick : true,
16350         /**
16351         * @cfg {Number} showDelay
16352         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16353         */
16354        showDelay : 500,
16355         /**
16356         * @cfg {Number} hideDelay
16357         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16358         */
16359        hideDelay : 200,
16360         /**
16361         * @cfg {Boolean} autoHide
16362         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16363         * Used in conjunction with hideDelay.
16364         */
16365        autoHide : true,
16366         /**
16367         * @cfg {Boolean}
16368         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16369         * (defaults to true).  Used in conjunction with autoDismissDelay.
16370         */
16371        autoDismiss : true,
16372         /**
16373         * @cfg {Number}
16374         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16375         */
16376        autoDismissDelay : 5000,
16377        /**
16378         * @cfg {Boolean} animate
16379         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16380         */
16381        animate : false,
16382
16383        /**
16384         * @cfg {String} title
16385         * Title text to display (defaults to '').  This can be any valid HTML markup.
16386         */
16387         title: '',
16388        /**
16389         * @cfg {String} text
16390         * Body text to display (defaults to '').  This can be any valid HTML markup.
16391         */
16392         text : '',
16393        /**
16394         * @cfg {String} cls
16395         * A CSS class to apply to the base quick tip element (defaults to '').
16396         */
16397         cls : '',
16398        /**
16399         * @cfg {Number} width
16400         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16401         * minWidth or maxWidth.
16402         */
16403         width : null,
16404
16405     /**
16406      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16407      * or display QuickTips in a page.
16408      */
16409        init : function(){
16410           tm = Roo.QuickTips;
16411           cfg = tm.tagConfig;
16412           if(!inited){
16413               if(!Roo.isReady){ // allow calling of init() before onReady
16414                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16415                   return;
16416               }
16417               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16418               el.fxDefaults = {stopFx: true};
16419               // maximum custom styling
16420               //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>');
16421               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>');              
16422               tipTitle = el.child('h3');
16423               tipTitle.enableDisplayMode("block");
16424               tipBody = el.child('div.x-tip-bd');
16425               tipBodyText = el.child('div.x-tip-bd-inner');
16426               //bdLeft = el.child('div.x-tip-bd-left');
16427               //bdRight = el.child('div.x-tip-bd-right');
16428               close = el.child('div.x-tip-close');
16429               close.enableDisplayMode("block");
16430               close.on("click", hide);
16431               var d = Roo.get(document);
16432               d.on("mousedown", onDown);
16433               d.on("mouseover", onOver);
16434               d.on("mouseout", onOut);
16435               d.on("mousemove", onMove);
16436               esc = d.addKeyListener(27, hide);
16437               esc.disable();
16438               if(Roo.dd.DD){
16439                   dd = el.initDD("default", null, {
16440                       onDrag : function(){
16441                           el.sync();  
16442                       }
16443                   });
16444                   dd.setHandleElId(tipTitle.id);
16445                   dd.lock();
16446               }
16447               inited = true;
16448           }
16449           this.enable(); 
16450        },
16451
16452     /**
16453      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16454      * are supported:
16455      * <pre>
16456 Property    Type                   Description
16457 ----------  ---------------------  ------------------------------------------------------------------------
16458 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16459      * </ul>
16460      * @param {Object} config The config object
16461      */
16462        register : function(config){
16463            var cs = config instanceof Array ? config : arguments;
16464            for(var i = 0, len = cs.length; i < len; i++) {
16465                var c = cs[i];
16466                var target = c.target;
16467                if(target){
16468                    if(target instanceof Array){
16469                        for(var j = 0, jlen = target.length; j < jlen; j++){
16470                            tagEls[target[j]] = c;
16471                        }
16472                    }else{
16473                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16474                    }
16475                }
16476            }
16477        },
16478
16479     /**
16480      * Removes this quick tip from its element and destroys it.
16481      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16482      */
16483        unregister : function(el){
16484            delete tagEls[Roo.id(el)];
16485        },
16486
16487     /**
16488      * Enable this quick tip.
16489      */
16490        enable : function(){
16491            if(inited && disabled){
16492                locks.pop();
16493                if(locks.length < 1){
16494                    disabled = false;
16495                }
16496            }
16497        },
16498
16499     /**
16500      * Disable this quick tip.
16501      */
16502        disable : function(){
16503           disabled = true;
16504           clearTimeout(showProc);
16505           clearTimeout(hideProc);
16506           clearTimeout(dismissProc);
16507           if(ce){
16508               hide(true);
16509           }
16510           locks.push(1);
16511        },
16512
16513     /**
16514      * Returns true if the quick tip is enabled, else false.
16515      */
16516        isEnabled : function(){
16517             return !disabled;
16518        },
16519
16520         // private
16521        tagConfig : {
16522            namespace : "ext",
16523            attribute : "qtip",
16524            width : "width",
16525            target : "target",
16526            title : "qtitle",
16527            hide : "hide",
16528            cls : "qclass"
16529        }
16530    };
16531 }();
16532
16533 // backwards compat
16534 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16535  * Based on:
16536  * Ext JS Library 1.1.1
16537  * Copyright(c) 2006-2007, Ext JS, LLC.
16538  *
16539  * Originally Released Under LGPL - original licence link has changed is not relivant.
16540  *
16541  * Fork - LGPL
16542  * <script type="text/javascript">
16543  */
16544  
16545
16546 /**
16547  * @class Roo.tree.TreePanel
16548  * @extends Roo.data.Tree
16549
16550  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16551  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16552  * @cfg {Boolean} enableDD true to enable drag and drop
16553  * @cfg {Boolean} enableDrag true to enable just drag
16554  * @cfg {Boolean} enableDrop true to enable just drop
16555  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16556  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16557  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16558  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16559  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16560  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16561  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16562  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16563  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16564  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16565  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16566  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16567  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16568  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16569  * @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>
16570  * @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>
16571  * 
16572  * @constructor
16573  * @param {String/HTMLElement/Element} el The container element
16574  * @param {Object} config
16575  */
16576 Roo.tree.TreePanel = function(el, config){
16577     var root = false;
16578     var loader = false;
16579     if (config.root) {
16580         root = config.root;
16581         delete config.root;
16582     }
16583     if (config.loader) {
16584         loader = config.loader;
16585         delete config.loader;
16586     }
16587     
16588     Roo.apply(this, config);
16589     Roo.tree.TreePanel.superclass.constructor.call(this);
16590     this.el = Roo.get(el);
16591     this.el.addClass('x-tree');
16592     //console.log(root);
16593     if (root) {
16594         this.setRootNode( Roo.factory(root, Roo.tree));
16595     }
16596     if (loader) {
16597         this.loader = Roo.factory(loader, Roo.tree);
16598     }
16599    /**
16600     * Read-only. The id of the container element becomes this TreePanel's id.
16601     */
16602     this.id = this.el.id;
16603     this.addEvents({
16604         /**
16605         * @event beforeload
16606         * Fires before a node is loaded, return false to cancel
16607         * @param {Node} node The node being loaded
16608         */
16609         "beforeload" : true,
16610         /**
16611         * @event load
16612         * Fires when a node is loaded
16613         * @param {Node} node The node that was loaded
16614         */
16615         "load" : true,
16616         /**
16617         * @event textchange
16618         * Fires when the text for a node is changed
16619         * @param {Node} node The node
16620         * @param {String} text The new text
16621         * @param {String} oldText The old text
16622         */
16623         "textchange" : true,
16624         /**
16625         * @event beforeexpand
16626         * Fires before a node is expanded, return false to cancel.
16627         * @param {Node} node The node
16628         * @param {Boolean} deep
16629         * @param {Boolean} anim
16630         */
16631         "beforeexpand" : true,
16632         /**
16633         * @event beforecollapse
16634         * Fires before a node is collapsed, return false to cancel.
16635         * @param {Node} node The node
16636         * @param {Boolean} deep
16637         * @param {Boolean} anim
16638         */
16639         "beforecollapse" : true,
16640         /**
16641         * @event expand
16642         * Fires when a node is expanded
16643         * @param {Node} node The node
16644         */
16645         "expand" : true,
16646         /**
16647         * @event disabledchange
16648         * Fires when the disabled status of a node changes
16649         * @param {Node} node The node
16650         * @param {Boolean} disabled
16651         */
16652         "disabledchange" : true,
16653         /**
16654         * @event collapse
16655         * Fires when a node is collapsed
16656         * @param {Node} node The node
16657         */
16658         "collapse" : true,
16659         /**
16660         * @event beforeclick
16661         * Fires before click processing on a node. Return false to cancel the default action.
16662         * @param {Node} node The node
16663         * @param {Roo.EventObject} e The event object
16664         */
16665         "beforeclick":true,
16666         /**
16667         * @event checkchange
16668         * Fires when a node with a checkbox's checked property changes
16669         * @param {Node} this This node
16670         * @param {Boolean} checked
16671         */
16672         "checkchange":true,
16673         /**
16674         * @event click
16675         * Fires when a node is clicked
16676         * @param {Node} node The node
16677         * @param {Roo.EventObject} e The event object
16678         */
16679         "click":true,
16680         /**
16681         * @event dblclick
16682         * Fires when a node is double clicked
16683         * @param {Node} node The node
16684         * @param {Roo.EventObject} e The event object
16685         */
16686         "dblclick":true,
16687         /**
16688         * @event contextmenu
16689         * Fires when a node is right clicked
16690         * @param {Node} node The node
16691         * @param {Roo.EventObject} e The event object
16692         */
16693         "contextmenu":true,
16694         /**
16695         * @event beforechildrenrendered
16696         * Fires right before the child nodes for a node are rendered
16697         * @param {Node} node The node
16698         */
16699         "beforechildrenrendered":true,
16700         /**
16701         * @event startdrag
16702         * Fires when a node starts being dragged
16703         * @param {Roo.tree.TreePanel} this
16704         * @param {Roo.tree.TreeNode} node
16705         * @param {event} e The raw browser event
16706         */ 
16707        "startdrag" : true,
16708        /**
16709         * @event enddrag
16710         * Fires when a drag operation is complete
16711         * @param {Roo.tree.TreePanel} this
16712         * @param {Roo.tree.TreeNode} node
16713         * @param {event} e The raw browser event
16714         */
16715        "enddrag" : true,
16716        /**
16717         * @event dragdrop
16718         * Fires when a dragged node is dropped on a valid DD target
16719         * @param {Roo.tree.TreePanel} this
16720         * @param {Roo.tree.TreeNode} node
16721         * @param {DD} dd The dd it was dropped on
16722         * @param {event} e The raw browser event
16723         */
16724        "dragdrop" : true,
16725        /**
16726         * @event beforenodedrop
16727         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16728         * passed to handlers has the following properties:<br />
16729         * <ul style="padding:5px;padding-left:16px;">
16730         * <li>tree - The TreePanel</li>
16731         * <li>target - The node being targeted for the drop</li>
16732         * <li>data - The drag data from the drag source</li>
16733         * <li>point - The point of the drop - append, above or below</li>
16734         * <li>source - The drag source</li>
16735         * <li>rawEvent - Raw mouse event</li>
16736         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16737         * to be inserted by setting them on this object.</li>
16738         * <li>cancel - Set this to true to cancel the drop.</li>
16739         * </ul>
16740         * @param {Object} dropEvent
16741         */
16742        "beforenodedrop" : true,
16743        /**
16744         * @event nodedrop
16745         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16746         * passed to handlers has the following properties:<br />
16747         * <ul style="padding:5px;padding-left:16px;">
16748         * <li>tree - The TreePanel</li>
16749         * <li>target - The node being targeted for the drop</li>
16750         * <li>data - The drag data from the drag source</li>
16751         * <li>point - The point of the drop - append, above or below</li>
16752         * <li>source - The drag source</li>
16753         * <li>rawEvent - Raw mouse event</li>
16754         * <li>dropNode - Dropped node(s).</li>
16755         * </ul>
16756         * @param {Object} dropEvent
16757         */
16758        "nodedrop" : true,
16759         /**
16760         * @event nodedragover
16761         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16762         * passed to handlers has the following properties:<br />
16763         * <ul style="padding:5px;padding-left:16px;">
16764         * <li>tree - The TreePanel</li>
16765         * <li>target - The node being targeted for the drop</li>
16766         * <li>data - The drag data from the drag source</li>
16767         * <li>point - The point of the drop - append, above or below</li>
16768         * <li>source - The drag source</li>
16769         * <li>rawEvent - Raw mouse event</li>
16770         * <li>dropNode - Drop node(s) provided by the source.</li>
16771         * <li>cancel - Set this to true to signal drop not allowed.</li>
16772         * </ul>
16773         * @param {Object} dragOverEvent
16774         */
16775        "nodedragover" : true
16776         
16777     });
16778     if(this.singleExpand){
16779        this.on("beforeexpand", this.restrictExpand, this);
16780     }
16781     if (this.editor) {
16782         this.editor.tree = this;
16783         this.editor = Roo.factory(this.editor, Roo.tree);
16784     }
16785     
16786     if (this.selModel) {
16787         this.selModel = Roo.factory(this.selModel, Roo.tree);
16788     }
16789    
16790 };
16791 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16792     rootVisible : true,
16793     animate: Roo.enableFx,
16794     lines : true,
16795     enableDD : false,
16796     hlDrop : Roo.enableFx,
16797   
16798     renderer: false,
16799     
16800     rendererTip: false,
16801     // private
16802     restrictExpand : function(node){
16803         var p = node.parentNode;
16804         if(p){
16805             if(p.expandedChild && p.expandedChild.parentNode == p){
16806                 p.expandedChild.collapse();
16807             }
16808             p.expandedChild = node;
16809         }
16810     },
16811
16812     // private override
16813     setRootNode : function(node){
16814         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16815         if(!this.rootVisible){
16816             node.ui = new Roo.tree.RootTreeNodeUI(node);
16817         }
16818         return node;
16819     },
16820
16821     /**
16822      * Returns the container element for this TreePanel
16823      */
16824     getEl : function(){
16825         return this.el;
16826     },
16827
16828     /**
16829      * Returns the default TreeLoader for this TreePanel
16830      */
16831     getLoader : function(){
16832         return this.loader;
16833     },
16834
16835     /**
16836      * Expand all nodes
16837      */
16838     expandAll : function(){
16839         this.root.expand(true);
16840     },
16841
16842     /**
16843      * Collapse all nodes
16844      */
16845     collapseAll : function(){
16846         this.root.collapse(true);
16847     },
16848
16849     /**
16850      * Returns the selection model used by this TreePanel
16851      */
16852     getSelectionModel : function(){
16853         if(!this.selModel){
16854             this.selModel = new Roo.tree.DefaultSelectionModel();
16855         }
16856         return this.selModel;
16857     },
16858
16859     /**
16860      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16861      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16862      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16863      * @return {Array}
16864      */
16865     getChecked : function(a, startNode){
16866         startNode = startNode || this.root;
16867         var r = [];
16868         var f = function(){
16869             if(this.attributes.checked){
16870                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16871             }
16872         }
16873         startNode.cascade(f);
16874         return r;
16875     },
16876
16877     /**
16878      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16879      * @param {String} path
16880      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16881      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16882      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16883      */
16884     expandPath : function(path, attr, callback){
16885         attr = attr || "id";
16886         var keys = path.split(this.pathSeparator);
16887         var curNode = this.root;
16888         if(curNode.attributes[attr] != keys[1]){ // invalid root
16889             if(callback){
16890                 callback(false, null);
16891             }
16892             return;
16893         }
16894         var index = 1;
16895         var f = function(){
16896             if(++index == keys.length){
16897                 if(callback){
16898                     callback(true, curNode);
16899                 }
16900                 return;
16901             }
16902             var c = curNode.findChild(attr, keys[index]);
16903             if(!c){
16904                 if(callback){
16905                     callback(false, curNode);
16906                 }
16907                 return;
16908             }
16909             curNode = c;
16910             c.expand(false, false, f);
16911         };
16912         curNode.expand(false, false, f);
16913     },
16914
16915     /**
16916      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16917      * @param {String} path
16918      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16919      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16920      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16921      */
16922     selectPath : function(path, attr, callback){
16923         attr = attr || "id";
16924         var keys = path.split(this.pathSeparator);
16925         var v = keys.pop();
16926         if(keys.length > 0){
16927             var f = function(success, node){
16928                 if(success && node){
16929                     var n = node.findChild(attr, v);
16930                     if(n){
16931                         n.select();
16932                         if(callback){
16933                             callback(true, n);
16934                         }
16935                     }else if(callback){
16936                         callback(false, n);
16937                     }
16938                 }else{
16939                     if(callback){
16940                         callback(false, n);
16941                     }
16942                 }
16943             };
16944             this.expandPath(keys.join(this.pathSeparator), attr, f);
16945         }else{
16946             this.root.select();
16947             if(callback){
16948                 callback(true, this.root);
16949             }
16950         }
16951     },
16952
16953     getTreeEl : function(){
16954         return this.el;
16955     },
16956
16957     /**
16958      * Trigger rendering of this TreePanel
16959      */
16960     render : function(){
16961         if (this.innerCt) {
16962             return this; // stop it rendering more than once!!
16963         }
16964         
16965         this.innerCt = this.el.createChild({tag:"ul",
16966                cls:"x-tree-root-ct " +
16967                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16968
16969         if(this.containerScroll){
16970             Roo.dd.ScrollManager.register(this.el);
16971         }
16972         if((this.enableDD || this.enableDrop) && !this.dropZone){
16973            /**
16974             * The dropZone used by this tree if drop is enabled
16975             * @type Roo.tree.TreeDropZone
16976             */
16977              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16978                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16979            });
16980         }
16981         if((this.enableDD || this.enableDrag) && !this.dragZone){
16982            /**
16983             * The dragZone used by this tree if drag is enabled
16984             * @type Roo.tree.TreeDragZone
16985             */
16986             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16987                ddGroup: this.ddGroup || "TreeDD",
16988                scroll: this.ddScroll
16989            });
16990         }
16991         this.getSelectionModel().init(this);
16992         if (!this.root) {
16993             Roo.log("ROOT not set in tree");
16994             return this;
16995         }
16996         this.root.render();
16997         if(!this.rootVisible){
16998             this.root.renderChildren();
16999         }
17000         return this;
17001     }
17002 });/*
17003  * Based on:
17004  * Ext JS Library 1.1.1
17005  * Copyright(c) 2006-2007, Ext JS, LLC.
17006  *
17007  * Originally Released Under LGPL - original licence link has changed is not relivant.
17008  *
17009  * Fork - LGPL
17010  * <script type="text/javascript">
17011  */
17012  
17013
17014 /**
17015  * @class Roo.tree.DefaultSelectionModel
17016  * @extends Roo.util.Observable
17017  * The default single selection for a TreePanel.
17018  * @param {Object} cfg Configuration
17019  */
17020 Roo.tree.DefaultSelectionModel = function(cfg){
17021    this.selNode = null;
17022    
17023    
17024    
17025    this.addEvents({
17026        /**
17027         * @event selectionchange
17028         * Fires when the selected node changes
17029         * @param {DefaultSelectionModel} this
17030         * @param {TreeNode} node the new selection
17031         */
17032        "selectionchange" : true,
17033
17034        /**
17035         * @event beforeselect
17036         * Fires before the selected node changes, return false to cancel the change
17037         * @param {DefaultSelectionModel} this
17038         * @param {TreeNode} node the new selection
17039         * @param {TreeNode} node the old selection
17040         */
17041        "beforeselect" : true
17042    });
17043    
17044     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17045 };
17046
17047 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17048     init : function(tree){
17049         this.tree = tree;
17050         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17051         tree.on("click", this.onNodeClick, this);
17052     },
17053     
17054     onNodeClick : function(node, e){
17055         if (e.ctrlKey && this.selNode == node)  {
17056             this.unselect(node);
17057             return;
17058         }
17059         this.select(node);
17060     },
17061     
17062     /**
17063      * Select a node.
17064      * @param {TreeNode} node The node to select
17065      * @return {TreeNode} The selected node
17066      */
17067     select : function(node){
17068         var last = this.selNode;
17069         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17070             if(last){
17071                 last.ui.onSelectedChange(false);
17072             }
17073             this.selNode = node;
17074             node.ui.onSelectedChange(true);
17075             this.fireEvent("selectionchange", this, node, last);
17076         }
17077         return node;
17078     },
17079     
17080     /**
17081      * Deselect a node.
17082      * @param {TreeNode} node The node to unselect
17083      */
17084     unselect : function(node){
17085         if(this.selNode == node){
17086             this.clearSelections();
17087         }    
17088     },
17089     
17090     /**
17091      * Clear all selections
17092      */
17093     clearSelections : function(){
17094         var n = this.selNode;
17095         if(n){
17096             n.ui.onSelectedChange(false);
17097             this.selNode = null;
17098             this.fireEvent("selectionchange", this, null);
17099         }
17100         return n;
17101     },
17102     
17103     /**
17104      * Get the selected node
17105      * @return {TreeNode} The selected node
17106      */
17107     getSelectedNode : function(){
17108         return this.selNode;    
17109     },
17110     
17111     /**
17112      * Returns true if the node is selected
17113      * @param {TreeNode} node The node to check
17114      * @return {Boolean}
17115      */
17116     isSelected : function(node){
17117         return this.selNode == node;  
17118     },
17119
17120     /**
17121      * Selects the node above the selected node in the tree, intelligently walking the nodes
17122      * @return TreeNode The new selection
17123      */
17124     selectPrevious : function(){
17125         var s = this.selNode || this.lastSelNode;
17126         if(!s){
17127             return null;
17128         }
17129         var ps = s.previousSibling;
17130         if(ps){
17131             if(!ps.isExpanded() || ps.childNodes.length < 1){
17132                 return this.select(ps);
17133             } else{
17134                 var lc = ps.lastChild;
17135                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17136                     lc = lc.lastChild;
17137                 }
17138                 return this.select(lc);
17139             }
17140         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17141             return this.select(s.parentNode);
17142         }
17143         return null;
17144     },
17145
17146     /**
17147      * Selects the node above the selected node in the tree, intelligently walking the nodes
17148      * @return TreeNode The new selection
17149      */
17150     selectNext : function(){
17151         var s = this.selNode || this.lastSelNode;
17152         if(!s){
17153             return null;
17154         }
17155         if(s.firstChild && s.isExpanded()){
17156              return this.select(s.firstChild);
17157          }else if(s.nextSibling){
17158              return this.select(s.nextSibling);
17159          }else if(s.parentNode){
17160             var newS = null;
17161             s.parentNode.bubble(function(){
17162                 if(this.nextSibling){
17163                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17164                     return false;
17165                 }
17166             });
17167             return newS;
17168          }
17169         return null;
17170     },
17171
17172     onKeyDown : function(e){
17173         var s = this.selNode || this.lastSelNode;
17174         // undesirable, but required
17175         var sm = this;
17176         if(!s){
17177             return;
17178         }
17179         var k = e.getKey();
17180         switch(k){
17181              case e.DOWN:
17182                  e.stopEvent();
17183                  this.selectNext();
17184              break;
17185              case e.UP:
17186                  e.stopEvent();
17187                  this.selectPrevious();
17188              break;
17189              case e.RIGHT:
17190                  e.preventDefault();
17191                  if(s.hasChildNodes()){
17192                      if(!s.isExpanded()){
17193                          s.expand();
17194                      }else if(s.firstChild){
17195                          this.select(s.firstChild, e);
17196                      }
17197                  }
17198              break;
17199              case e.LEFT:
17200                  e.preventDefault();
17201                  if(s.hasChildNodes() && s.isExpanded()){
17202                      s.collapse();
17203                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17204                      this.select(s.parentNode, e);
17205                  }
17206              break;
17207         };
17208     }
17209 });
17210
17211 /**
17212  * @class Roo.tree.MultiSelectionModel
17213  * @extends Roo.util.Observable
17214  * Multi selection for a TreePanel.
17215  * @param {Object} cfg Configuration
17216  */
17217 Roo.tree.MultiSelectionModel = function(){
17218    this.selNodes = [];
17219    this.selMap = {};
17220    this.addEvents({
17221        /**
17222         * @event selectionchange
17223         * Fires when the selected nodes change
17224         * @param {MultiSelectionModel} this
17225         * @param {Array} nodes Array of the selected nodes
17226         */
17227        "selectionchange" : true
17228    });
17229    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17230    
17231 };
17232
17233 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17234     init : function(tree){
17235         this.tree = tree;
17236         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17237         tree.on("click", this.onNodeClick, this);
17238     },
17239     
17240     onNodeClick : function(node, e){
17241         this.select(node, e, e.ctrlKey);
17242     },
17243     
17244     /**
17245      * Select a node.
17246      * @param {TreeNode} node The node to select
17247      * @param {EventObject} e (optional) An event associated with the selection
17248      * @param {Boolean} keepExisting True to retain existing selections
17249      * @return {TreeNode} The selected node
17250      */
17251     select : function(node, e, keepExisting){
17252         if(keepExisting !== true){
17253             this.clearSelections(true);
17254         }
17255         if(this.isSelected(node)){
17256             this.lastSelNode = node;
17257             return node;
17258         }
17259         this.selNodes.push(node);
17260         this.selMap[node.id] = node;
17261         this.lastSelNode = node;
17262         node.ui.onSelectedChange(true);
17263         this.fireEvent("selectionchange", this, this.selNodes);
17264         return node;
17265     },
17266     
17267     /**
17268      * Deselect a node.
17269      * @param {TreeNode} node The node to unselect
17270      */
17271     unselect : function(node){
17272         if(this.selMap[node.id]){
17273             node.ui.onSelectedChange(false);
17274             var sn = this.selNodes;
17275             var index = -1;
17276             if(sn.indexOf){
17277                 index = sn.indexOf(node);
17278             }else{
17279                 for(var i = 0, len = sn.length; i < len; i++){
17280                     if(sn[i] == node){
17281                         index = i;
17282                         break;
17283                     }
17284                 }
17285             }
17286             if(index != -1){
17287                 this.selNodes.splice(index, 1);
17288             }
17289             delete this.selMap[node.id];
17290             this.fireEvent("selectionchange", this, this.selNodes);
17291         }
17292     },
17293     
17294     /**
17295      * Clear all selections
17296      */
17297     clearSelections : function(suppressEvent){
17298         var sn = this.selNodes;
17299         if(sn.length > 0){
17300             for(var i = 0, len = sn.length; i < len; i++){
17301                 sn[i].ui.onSelectedChange(false);
17302             }
17303             this.selNodes = [];
17304             this.selMap = {};
17305             if(suppressEvent !== true){
17306                 this.fireEvent("selectionchange", this, this.selNodes);
17307             }
17308         }
17309     },
17310     
17311     /**
17312      * Returns true if the node is selected
17313      * @param {TreeNode} node The node to check
17314      * @return {Boolean}
17315      */
17316     isSelected : function(node){
17317         return this.selMap[node.id] ? true : false;  
17318     },
17319     
17320     /**
17321      * Returns an array of the selected nodes
17322      * @return {Array}
17323      */
17324     getSelectedNodes : function(){
17325         return this.selNodes;    
17326     },
17327
17328     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17329
17330     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17331
17332     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17333 });/*
17334  * Based on:
17335  * Ext JS Library 1.1.1
17336  * Copyright(c) 2006-2007, Ext JS, LLC.
17337  *
17338  * Originally Released Under LGPL - original licence link has changed is not relivant.
17339  *
17340  * Fork - LGPL
17341  * <script type="text/javascript">
17342  */
17343  
17344 /**
17345  * @class Roo.tree.TreeNode
17346  * @extends Roo.data.Node
17347  * @cfg {String} text The text for this node
17348  * @cfg {Boolean} expanded true to start the node expanded
17349  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17350  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17351  * @cfg {Boolean} disabled true to start the node disabled
17352  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17353  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17354  * @cfg {String} cls A css class to be added to the node
17355  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17356  * @cfg {String} href URL of the link used for the node (defaults to #)
17357  * @cfg {String} hrefTarget target frame for the link
17358  * @cfg {String} qtip An Ext QuickTip for the node
17359  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17360  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17361  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17362  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17363  * (defaults to undefined with no checkbox rendered)
17364  * @constructor
17365  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17366  */
17367 Roo.tree.TreeNode = function(attributes){
17368     attributes = attributes || {};
17369     if(typeof attributes == "string"){
17370         attributes = {text: attributes};
17371     }
17372     this.childrenRendered = false;
17373     this.rendered = false;
17374     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17375     this.expanded = attributes.expanded === true;
17376     this.isTarget = attributes.isTarget !== false;
17377     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17378     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17379
17380     /**
17381      * Read-only. The text for this node. To change it use setText().
17382      * @type String
17383      */
17384     this.text = attributes.text;
17385     /**
17386      * True if this node is disabled.
17387      * @type Boolean
17388      */
17389     this.disabled = attributes.disabled === true;
17390
17391     this.addEvents({
17392         /**
17393         * @event textchange
17394         * Fires when the text for this node is changed
17395         * @param {Node} this This node
17396         * @param {String} text The new text
17397         * @param {String} oldText The old text
17398         */
17399         "textchange" : true,
17400         /**
17401         * @event beforeexpand
17402         * Fires before this node is expanded, return false to cancel.
17403         * @param {Node} this This node
17404         * @param {Boolean} deep
17405         * @param {Boolean} anim
17406         */
17407         "beforeexpand" : true,
17408         /**
17409         * @event beforecollapse
17410         * Fires before this node is collapsed, return false to cancel.
17411         * @param {Node} this This node
17412         * @param {Boolean} deep
17413         * @param {Boolean} anim
17414         */
17415         "beforecollapse" : true,
17416         /**
17417         * @event expand
17418         * Fires when this node is expanded
17419         * @param {Node} this This node
17420         */
17421         "expand" : true,
17422         /**
17423         * @event disabledchange
17424         * Fires when the disabled status of this node changes
17425         * @param {Node} this This node
17426         * @param {Boolean} disabled
17427         */
17428         "disabledchange" : true,
17429         /**
17430         * @event collapse
17431         * Fires when this node is collapsed
17432         * @param {Node} this This node
17433         */
17434         "collapse" : true,
17435         /**
17436         * @event beforeclick
17437         * Fires before click processing. Return false to cancel the default action.
17438         * @param {Node} this This node
17439         * @param {Roo.EventObject} e The event object
17440         */
17441         "beforeclick":true,
17442         /**
17443         * @event checkchange
17444         * Fires when a node with a checkbox's checked property changes
17445         * @param {Node} this This node
17446         * @param {Boolean} checked
17447         */
17448         "checkchange":true,
17449         /**
17450         * @event click
17451         * Fires when this node is clicked
17452         * @param {Node} this This node
17453         * @param {Roo.EventObject} e The event object
17454         */
17455         "click":true,
17456         /**
17457         * @event dblclick
17458         * Fires when this node is double clicked
17459         * @param {Node} this This node
17460         * @param {Roo.EventObject} e The event object
17461         */
17462         "dblclick":true,
17463         /**
17464         * @event contextmenu
17465         * Fires when this node is right clicked
17466         * @param {Node} this This node
17467         * @param {Roo.EventObject} e The event object
17468         */
17469         "contextmenu":true,
17470         /**
17471         * @event beforechildrenrendered
17472         * Fires right before the child nodes for this node are rendered
17473         * @param {Node} this This node
17474         */
17475         "beforechildrenrendered":true
17476     });
17477
17478     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17479
17480     /**
17481      * Read-only. The UI for this node
17482      * @type TreeNodeUI
17483      */
17484     this.ui = new uiClass(this);
17485     
17486     // finally support items[]
17487     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17488         return;
17489     }
17490     
17491     
17492     Roo.each(this.attributes.items, function(c) {
17493         this.appendChild(Roo.factory(c,Roo.Tree));
17494     }, this);
17495     delete this.attributes.items;
17496     
17497     
17498     
17499 };
17500 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17501     preventHScroll: true,
17502     /**
17503      * Returns true if this node is expanded
17504      * @return {Boolean}
17505      */
17506     isExpanded : function(){
17507         return this.expanded;
17508     },
17509
17510     /**
17511      * Returns the UI object for this node
17512      * @return {TreeNodeUI}
17513      */
17514     getUI : function(){
17515         return this.ui;
17516     },
17517
17518     // private override
17519     setFirstChild : function(node){
17520         var of = this.firstChild;
17521         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17522         if(this.childrenRendered && of && node != of){
17523             of.renderIndent(true, true);
17524         }
17525         if(this.rendered){
17526             this.renderIndent(true, true);
17527         }
17528     },
17529
17530     // private override
17531     setLastChild : function(node){
17532         var ol = this.lastChild;
17533         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17534         if(this.childrenRendered && ol && node != ol){
17535             ol.renderIndent(true, true);
17536         }
17537         if(this.rendered){
17538             this.renderIndent(true, true);
17539         }
17540     },
17541
17542     // these methods are overridden to provide lazy rendering support
17543     // private override
17544     appendChild : function()
17545     {
17546         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17547         if(node && this.childrenRendered){
17548             node.render();
17549         }
17550         this.ui.updateExpandIcon();
17551         return node;
17552     },
17553
17554     // private override
17555     removeChild : function(node){
17556         this.ownerTree.getSelectionModel().unselect(node);
17557         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17558         // if it's been rendered remove dom node
17559         if(this.childrenRendered){
17560             node.ui.remove();
17561         }
17562         if(this.childNodes.length < 1){
17563             this.collapse(false, false);
17564         }else{
17565             this.ui.updateExpandIcon();
17566         }
17567         if(!this.firstChild) {
17568             this.childrenRendered = false;
17569         }
17570         return node;
17571     },
17572
17573     // private override
17574     insertBefore : function(node, refNode){
17575         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17576         if(newNode && refNode && this.childrenRendered){
17577             node.render();
17578         }
17579         this.ui.updateExpandIcon();
17580         return newNode;
17581     },
17582
17583     /**
17584      * Sets the text for this node
17585      * @param {String} text
17586      */
17587     setText : function(text){
17588         var oldText = this.text;
17589         this.text = text;
17590         this.attributes.text = text;
17591         if(this.rendered){ // event without subscribing
17592             this.ui.onTextChange(this, text, oldText);
17593         }
17594         this.fireEvent("textchange", this, text, oldText);
17595     },
17596
17597     /**
17598      * Triggers selection of this node
17599      */
17600     select : function(){
17601         this.getOwnerTree().getSelectionModel().select(this);
17602     },
17603
17604     /**
17605      * Triggers deselection of this node
17606      */
17607     unselect : function(){
17608         this.getOwnerTree().getSelectionModel().unselect(this);
17609     },
17610
17611     /**
17612      * Returns true if this node is selected
17613      * @return {Boolean}
17614      */
17615     isSelected : function(){
17616         return this.getOwnerTree().getSelectionModel().isSelected(this);
17617     },
17618
17619     /**
17620      * Expand this node.
17621      * @param {Boolean} deep (optional) True to expand all children as well
17622      * @param {Boolean} anim (optional) false to cancel the default animation
17623      * @param {Function} callback (optional) A callback to be called when
17624      * expanding this node completes (does not wait for deep expand to complete).
17625      * Called with 1 parameter, this node.
17626      */
17627     expand : function(deep, anim, callback){
17628         if(!this.expanded){
17629             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17630                 return;
17631             }
17632             if(!this.childrenRendered){
17633                 this.renderChildren();
17634             }
17635             this.expanded = true;
17636             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17637                 this.ui.animExpand(function(){
17638                     this.fireEvent("expand", this);
17639                     if(typeof callback == "function"){
17640                         callback(this);
17641                     }
17642                     if(deep === true){
17643                         this.expandChildNodes(true);
17644                     }
17645                 }.createDelegate(this));
17646                 return;
17647             }else{
17648                 this.ui.expand();
17649                 this.fireEvent("expand", this);
17650                 if(typeof callback == "function"){
17651                     callback(this);
17652                 }
17653             }
17654         }else{
17655            if(typeof callback == "function"){
17656                callback(this);
17657            }
17658         }
17659         if(deep === true){
17660             this.expandChildNodes(true);
17661         }
17662     },
17663
17664     isHiddenRoot : function(){
17665         return this.isRoot && !this.getOwnerTree().rootVisible;
17666     },
17667
17668     /**
17669      * Collapse this node.
17670      * @param {Boolean} deep (optional) True to collapse all children as well
17671      * @param {Boolean} anim (optional) false to cancel the default animation
17672      */
17673     collapse : function(deep, anim){
17674         if(this.expanded && !this.isHiddenRoot()){
17675             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17676                 return;
17677             }
17678             this.expanded = false;
17679             if((this.getOwnerTree().animate && anim !== false) || anim){
17680                 this.ui.animCollapse(function(){
17681                     this.fireEvent("collapse", this);
17682                     if(deep === true){
17683                         this.collapseChildNodes(true);
17684                     }
17685                 }.createDelegate(this));
17686                 return;
17687             }else{
17688                 this.ui.collapse();
17689                 this.fireEvent("collapse", this);
17690             }
17691         }
17692         if(deep === true){
17693             var cs = this.childNodes;
17694             for(var i = 0, len = cs.length; i < len; i++) {
17695                 cs[i].collapse(true, false);
17696             }
17697         }
17698     },
17699
17700     // private
17701     delayedExpand : function(delay){
17702         if(!this.expandProcId){
17703             this.expandProcId = this.expand.defer(delay, this);
17704         }
17705     },
17706
17707     // private
17708     cancelExpand : function(){
17709         if(this.expandProcId){
17710             clearTimeout(this.expandProcId);
17711         }
17712         this.expandProcId = false;
17713     },
17714
17715     /**
17716      * Toggles expanded/collapsed state of the node
17717      */
17718     toggle : function(){
17719         if(this.expanded){
17720             this.collapse();
17721         }else{
17722             this.expand();
17723         }
17724     },
17725
17726     /**
17727      * Ensures all parent nodes are expanded
17728      */
17729     ensureVisible : function(callback){
17730         var tree = this.getOwnerTree();
17731         tree.expandPath(this.parentNode.getPath(), false, function(){
17732             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17733             Roo.callback(callback);
17734         }.createDelegate(this));
17735     },
17736
17737     /**
17738      * Expand all child nodes
17739      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17740      */
17741     expandChildNodes : function(deep){
17742         var cs = this.childNodes;
17743         for(var i = 0, len = cs.length; i < len; i++) {
17744                 cs[i].expand(deep);
17745         }
17746     },
17747
17748     /**
17749      * Collapse all child nodes
17750      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17751      */
17752     collapseChildNodes : function(deep){
17753         var cs = this.childNodes;
17754         for(var i = 0, len = cs.length; i < len; i++) {
17755                 cs[i].collapse(deep);
17756         }
17757     },
17758
17759     /**
17760      * Disables this node
17761      */
17762     disable : function(){
17763         this.disabled = true;
17764         this.unselect();
17765         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17766             this.ui.onDisableChange(this, true);
17767         }
17768         this.fireEvent("disabledchange", this, true);
17769     },
17770
17771     /**
17772      * Enables this node
17773      */
17774     enable : function(){
17775         this.disabled = false;
17776         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17777             this.ui.onDisableChange(this, false);
17778         }
17779         this.fireEvent("disabledchange", this, false);
17780     },
17781
17782     // private
17783     renderChildren : function(suppressEvent){
17784         if(suppressEvent !== false){
17785             this.fireEvent("beforechildrenrendered", this);
17786         }
17787         var cs = this.childNodes;
17788         for(var i = 0, len = cs.length; i < len; i++){
17789             cs[i].render(true);
17790         }
17791         this.childrenRendered = true;
17792     },
17793
17794     // private
17795     sort : function(fn, scope){
17796         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17797         if(this.childrenRendered){
17798             var cs = this.childNodes;
17799             for(var i = 0, len = cs.length; i < len; i++){
17800                 cs[i].render(true);
17801             }
17802         }
17803     },
17804
17805     // private
17806     render : function(bulkRender){
17807         this.ui.render(bulkRender);
17808         if(!this.rendered){
17809             this.rendered = true;
17810             if(this.expanded){
17811                 this.expanded = false;
17812                 this.expand(false, false);
17813             }
17814         }
17815     },
17816
17817     // private
17818     renderIndent : function(deep, refresh){
17819         if(refresh){
17820             this.ui.childIndent = null;
17821         }
17822         this.ui.renderIndent();
17823         if(deep === true && this.childrenRendered){
17824             var cs = this.childNodes;
17825             for(var i = 0, len = cs.length; i < len; i++){
17826                 cs[i].renderIndent(true, refresh);
17827             }
17828         }
17829     }
17830 });/*
17831  * Based on:
17832  * Ext JS Library 1.1.1
17833  * Copyright(c) 2006-2007, Ext JS, LLC.
17834  *
17835  * Originally Released Under LGPL - original licence link has changed is not relivant.
17836  *
17837  * Fork - LGPL
17838  * <script type="text/javascript">
17839  */
17840  
17841 /**
17842  * @class Roo.tree.AsyncTreeNode
17843  * @extends Roo.tree.TreeNode
17844  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17845  * @constructor
17846  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17847  */
17848  Roo.tree.AsyncTreeNode = function(config){
17849     this.loaded = false;
17850     this.loading = false;
17851     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17852     /**
17853     * @event beforeload
17854     * Fires before this node is loaded, return false to cancel
17855     * @param {Node} this This node
17856     */
17857     this.addEvents({'beforeload':true, 'load': true});
17858     /**
17859     * @event load
17860     * Fires when this node is loaded
17861     * @param {Node} this This node
17862     */
17863     /**
17864      * The loader used by this node (defaults to using the tree's defined loader)
17865      * @type TreeLoader
17866      * @property loader
17867      */
17868 };
17869 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17870     expand : function(deep, anim, callback){
17871         if(this.loading){ // if an async load is already running, waiting til it's done
17872             var timer;
17873             var f = function(){
17874                 if(!this.loading){ // done loading
17875                     clearInterval(timer);
17876                     this.expand(deep, anim, callback);
17877                 }
17878             }.createDelegate(this);
17879             timer = setInterval(f, 200);
17880             return;
17881         }
17882         if(!this.loaded){
17883             if(this.fireEvent("beforeload", this) === false){
17884                 return;
17885             }
17886             this.loading = true;
17887             this.ui.beforeLoad(this);
17888             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17889             if(loader){
17890                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17891                 return;
17892             }
17893         }
17894         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17895     },
17896     
17897     /**
17898      * Returns true if this node is currently loading
17899      * @return {Boolean}
17900      */
17901     isLoading : function(){
17902         return this.loading;  
17903     },
17904     
17905     loadComplete : function(deep, anim, callback){
17906         this.loading = false;
17907         this.loaded = true;
17908         this.ui.afterLoad(this);
17909         this.fireEvent("load", this);
17910         this.expand(deep, anim, callback);
17911     },
17912     
17913     /**
17914      * Returns true if this node has been loaded
17915      * @return {Boolean}
17916      */
17917     isLoaded : function(){
17918         return this.loaded;
17919     },
17920     
17921     hasChildNodes : function(){
17922         if(!this.isLeaf() && !this.loaded){
17923             return true;
17924         }else{
17925             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17926         }
17927     },
17928
17929     /**
17930      * Trigger a reload for this node
17931      * @param {Function} callback
17932      */
17933     reload : function(callback){
17934         this.collapse(false, false);
17935         while(this.firstChild){
17936             this.removeChild(this.firstChild);
17937         }
17938         this.childrenRendered = false;
17939         this.loaded = false;
17940         if(this.isHiddenRoot()){
17941             this.expanded = false;
17942         }
17943         this.expand(false, false, callback);
17944     }
17945 });/*
17946  * Based on:
17947  * Ext JS Library 1.1.1
17948  * Copyright(c) 2006-2007, Ext JS, LLC.
17949  *
17950  * Originally Released Under LGPL - original licence link has changed is not relivant.
17951  *
17952  * Fork - LGPL
17953  * <script type="text/javascript">
17954  */
17955  
17956 /**
17957  * @class Roo.tree.TreeNodeUI
17958  * @constructor
17959  * @param {Object} node The node to render
17960  * The TreeNode UI implementation is separate from the
17961  * tree implementation. Unless you are customizing the tree UI,
17962  * you should never have to use this directly.
17963  */
17964 Roo.tree.TreeNodeUI = function(node){
17965     this.node = node;
17966     this.rendered = false;
17967     this.animating = false;
17968     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17969 };
17970
17971 Roo.tree.TreeNodeUI.prototype = {
17972     removeChild : function(node){
17973         if(this.rendered){
17974             this.ctNode.removeChild(node.ui.getEl());
17975         }
17976     },
17977
17978     beforeLoad : function(){
17979          this.addClass("x-tree-node-loading");
17980     },
17981
17982     afterLoad : function(){
17983          this.removeClass("x-tree-node-loading");
17984     },
17985
17986     onTextChange : function(node, text, oldText){
17987         if(this.rendered){
17988             this.textNode.innerHTML = text;
17989         }
17990     },
17991
17992     onDisableChange : function(node, state){
17993         this.disabled = state;
17994         if(state){
17995             this.addClass("x-tree-node-disabled");
17996         }else{
17997             this.removeClass("x-tree-node-disabled");
17998         }
17999     },
18000
18001     onSelectedChange : function(state){
18002         if(state){
18003             this.focus();
18004             this.addClass("x-tree-selected");
18005         }else{
18006             //this.blur();
18007             this.removeClass("x-tree-selected");
18008         }
18009     },
18010
18011     onMove : function(tree, node, oldParent, newParent, index, refNode){
18012         this.childIndent = null;
18013         if(this.rendered){
18014             var targetNode = newParent.ui.getContainer();
18015             if(!targetNode){//target not rendered
18016                 this.holder = document.createElement("div");
18017                 this.holder.appendChild(this.wrap);
18018                 return;
18019             }
18020             var insertBefore = refNode ? refNode.ui.getEl() : null;
18021             if(insertBefore){
18022                 targetNode.insertBefore(this.wrap, insertBefore);
18023             }else{
18024                 targetNode.appendChild(this.wrap);
18025             }
18026             this.node.renderIndent(true);
18027         }
18028     },
18029
18030     addClass : function(cls){
18031         if(this.elNode){
18032             Roo.fly(this.elNode).addClass(cls);
18033         }
18034     },
18035
18036     removeClass : function(cls){
18037         if(this.elNode){
18038             Roo.fly(this.elNode).removeClass(cls);
18039         }
18040     },
18041
18042     remove : function(){
18043         if(this.rendered){
18044             this.holder = document.createElement("div");
18045             this.holder.appendChild(this.wrap);
18046         }
18047     },
18048
18049     fireEvent : function(){
18050         return this.node.fireEvent.apply(this.node, arguments);
18051     },
18052
18053     initEvents : function(){
18054         this.node.on("move", this.onMove, this);
18055         var E = Roo.EventManager;
18056         var a = this.anchor;
18057
18058         var el = Roo.fly(a, '_treeui');
18059
18060         if(Roo.isOpera){ // opera render bug ignores the CSS
18061             el.setStyle("text-decoration", "none");
18062         }
18063
18064         el.on("click", this.onClick, this);
18065         el.on("dblclick", this.onDblClick, this);
18066
18067         if(this.checkbox){
18068             Roo.EventManager.on(this.checkbox,
18069                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18070         }
18071
18072         el.on("contextmenu", this.onContextMenu, this);
18073
18074         var icon = Roo.fly(this.iconNode);
18075         icon.on("click", this.onClick, this);
18076         icon.on("dblclick", this.onDblClick, this);
18077         icon.on("contextmenu", this.onContextMenu, this);
18078         E.on(this.ecNode, "click", this.ecClick, this, true);
18079
18080         if(this.node.disabled){
18081             this.addClass("x-tree-node-disabled");
18082         }
18083         if(this.node.hidden){
18084             this.addClass("x-tree-node-disabled");
18085         }
18086         var ot = this.node.getOwnerTree();
18087         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18088         if(dd && (!this.node.isRoot || ot.rootVisible)){
18089             Roo.dd.Registry.register(this.elNode, {
18090                 node: this.node,
18091                 handles: this.getDDHandles(),
18092                 isHandle: false
18093             });
18094         }
18095     },
18096
18097     getDDHandles : function(){
18098         return [this.iconNode, this.textNode];
18099     },
18100
18101     hide : function(){
18102         if(this.rendered){
18103             this.wrap.style.display = "none";
18104         }
18105     },
18106
18107     show : function(){
18108         if(this.rendered){
18109             this.wrap.style.display = "";
18110         }
18111     },
18112
18113     onContextMenu : function(e){
18114         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18115             e.preventDefault();
18116             this.focus();
18117             this.fireEvent("contextmenu", this.node, e);
18118         }
18119     },
18120
18121     onClick : function(e){
18122         if(this.dropping){
18123             e.stopEvent();
18124             return;
18125         }
18126         if(this.fireEvent("beforeclick", this.node, e) !== false){
18127             if(!this.disabled && this.node.attributes.href){
18128                 this.fireEvent("click", this.node, e);
18129                 return;
18130             }
18131             e.preventDefault();
18132             if(this.disabled){
18133                 return;
18134             }
18135
18136             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18137                 this.node.toggle();
18138             }
18139
18140             this.fireEvent("click", this.node, e);
18141         }else{
18142             e.stopEvent();
18143         }
18144     },
18145
18146     onDblClick : function(e){
18147         e.preventDefault();
18148         if(this.disabled){
18149             return;
18150         }
18151         if(this.checkbox){
18152             this.toggleCheck();
18153         }
18154         if(!this.animating && this.node.hasChildNodes()){
18155             this.node.toggle();
18156         }
18157         this.fireEvent("dblclick", this.node, e);
18158     },
18159
18160     onCheckChange : function(){
18161         var checked = this.checkbox.checked;
18162         this.node.attributes.checked = checked;
18163         this.fireEvent('checkchange', this.node, checked);
18164     },
18165
18166     ecClick : function(e){
18167         if(!this.animating && this.node.hasChildNodes()){
18168             this.node.toggle();
18169         }
18170     },
18171
18172     startDrop : function(){
18173         this.dropping = true;
18174     },
18175
18176     // delayed drop so the click event doesn't get fired on a drop
18177     endDrop : function(){
18178        setTimeout(function(){
18179            this.dropping = false;
18180        }.createDelegate(this), 50);
18181     },
18182
18183     expand : function(){
18184         this.updateExpandIcon();
18185         this.ctNode.style.display = "";
18186     },
18187
18188     focus : function(){
18189         if(!this.node.preventHScroll){
18190             try{this.anchor.focus();
18191             }catch(e){}
18192         }else if(!Roo.isIE){
18193             try{
18194                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18195                 var l = noscroll.scrollLeft;
18196                 this.anchor.focus();
18197                 noscroll.scrollLeft = l;
18198             }catch(e){}
18199         }
18200     },
18201
18202     toggleCheck : function(value){
18203         var cb = this.checkbox;
18204         if(cb){
18205             cb.checked = (value === undefined ? !cb.checked : value);
18206         }
18207     },
18208
18209     blur : function(){
18210         try{
18211             this.anchor.blur();
18212         }catch(e){}
18213     },
18214
18215     animExpand : function(callback){
18216         var ct = Roo.get(this.ctNode);
18217         ct.stopFx();
18218         if(!this.node.hasChildNodes()){
18219             this.updateExpandIcon();
18220             this.ctNode.style.display = "";
18221             Roo.callback(callback);
18222             return;
18223         }
18224         this.animating = true;
18225         this.updateExpandIcon();
18226
18227         ct.slideIn('t', {
18228            callback : function(){
18229                this.animating = false;
18230                Roo.callback(callback);
18231             },
18232             scope: this,
18233             duration: this.node.ownerTree.duration || .25
18234         });
18235     },
18236
18237     highlight : function(){
18238         var tree = this.node.getOwnerTree();
18239         Roo.fly(this.wrap).highlight(
18240             tree.hlColor || "C3DAF9",
18241             {endColor: tree.hlBaseColor}
18242         );
18243     },
18244
18245     collapse : function(){
18246         this.updateExpandIcon();
18247         this.ctNode.style.display = "none";
18248     },
18249
18250     animCollapse : function(callback){
18251         var ct = Roo.get(this.ctNode);
18252         ct.enableDisplayMode('block');
18253         ct.stopFx();
18254
18255         this.animating = true;
18256         this.updateExpandIcon();
18257
18258         ct.slideOut('t', {
18259             callback : function(){
18260                this.animating = false;
18261                Roo.callback(callback);
18262             },
18263             scope: this,
18264             duration: this.node.ownerTree.duration || .25
18265         });
18266     },
18267
18268     getContainer : function(){
18269         return this.ctNode;
18270     },
18271
18272     getEl : function(){
18273         return this.wrap;
18274     },
18275
18276     appendDDGhost : function(ghostNode){
18277         ghostNode.appendChild(this.elNode.cloneNode(true));
18278     },
18279
18280     getDDRepairXY : function(){
18281         return Roo.lib.Dom.getXY(this.iconNode);
18282     },
18283
18284     onRender : function(){
18285         this.render();
18286     },
18287
18288     render : function(bulkRender){
18289         var n = this.node, a = n.attributes;
18290         var targetNode = n.parentNode ?
18291               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18292
18293         if(!this.rendered){
18294             this.rendered = true;
18295
18296             this.renderElements(n, a, targetNode, bulkRender);
18297
18298             if(a.qtip){
18299                if(this.textNode.setAttributeNS){
18300                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18301                    if(a.qtipTitle){
18302                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18303                    }
18304                }else{
18305                    this.textNode.setAttribute("ext:qtip", a.qtip);
18306                    if(a.qtipTitle){
18307                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18308                    }
18309                }
18310             }else if(a.qtipCfg){
18311                 a.qtipCfg.target = Roo.id(this.textNode);
18312                 Roo.QuickTips.register(a.qtipCfg);
18313             }
18314             this.initEvents();
18315             if(!this.node.expanded){
18316                 this.updateExpandIcon();
18317             }
18318         }else{
18319             if(bulkRender === true) {
18320                 targetNode.appendChild(this.wrap);
18321             }
18322         }
18323     },
18324
18325     renderElements : function(n, a, targetNode, bulkRender)
18326     {
18327         // add some indent caching, this helps performance when rendering a large tree
18328         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18329         var t = n.getOwnerTree();
18330         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18331         if (typeof(n.attributes.html) != 'undefined') {
18332             txt = n.attributes.html;
18333         }
18334         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18335         var cb = typeof a.checked == 'boolean';
18336         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18337         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18338             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18339             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18340             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18341             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18342             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18343              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18344                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18345             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18346             "</li>"];
18347
18348         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18349             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18350                                 n.nextSibling.ui.getEl(), buf.join(""));
18351         }else{
18352             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18353         }
18354
18355         this.elNode = this.wrap.childNodes[0];
18356         this.ctNode = this.wrap.childNodes[1];
18357         var cs = this.elNode.childNodes;
18358         this.indentNode = cs[0];
18359         this.ecNode = cs[1];
18360         this.iconNode = cs[2];
18361         var index = 3;
18362         if(cb){
18363             this.checkbox = cs[3];
18364             index++;
18365         }
18366         this.anchor = cs[index];
18367         this.textNode = cs[index].firstChild;
18368     },
18369
18370     getAnchor : function(){
18371         return this.anchor;
18372     },
18373
18374     getTextEl : function(){
18375         return this.textNode;
18376     },
18377
18378     getIconEl : function(){
18379         return this.iconNode;
18380     },
18381
18382     isChecked : function(){
18383         return this.checkbox ? this.checkbox.checked : false;
18384     },
18385
18386     updateExpandIcon : function(){
18387         if(this.rendered){
18388             var n = this.node, c1, c2;
18389             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18390             var hasChild = n.hasChildNodes();
18391             if(hasChild){
18392                 if(n.expanded){
18393                     cls += "-minus";
18394                     c1 = "x-tree-node-collapsed";
18395                     c2 = "x-tree-node-expanded";
18396                 }else{
18397                     cls += "-plus";
18398                     c1 = "x-tree-node-expanded";
18399                     c2 = "x-tree-node-collapsed";
18400                 }
18401                 if(this.wasLeaf){
18402                     this.removeClass("x-tree-node-leaf");
18403                     this.wasLeaf = false;
18404                 }
18405                 if(this.c1 != c1 || this.c2 != c2){
18406                     Roo.fly(this.elNode).replaceClass(c1, c2);
18407                     this.c1 = c1; this.c2 = c2;
18408                 }
18409             }else{
18410                 // this changes non-leafs into leafs if they have no children.
18411                 // it's not very rational behaviour..
18412                 
18413                 if(!this.wasLeaf && this.node.leaf){
18414                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18415                     delete this.c1;
18416                     delete this.c2;
18417                     this.wasLeaf = true;
18418                 }
18419             }
18420             var ecc = "x-tree-ec-icon "+cls;
18421             if(this.ecc != ecc){
18422                 this.ecNode.className = ecc;
18423                 this.ecc = ecc;
18424             }
18425         }
18426     },
18427
18428     getChildIndent : function(){
18429         if(!this.childIndent){
18430             var buf = [];
18431             var p = this.node;
18432             while(p){
18433                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18434                     if(!p.isLast()) {
18435                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18436                     } else {
18437                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18438                     }
18439                 }
18440                 p = p.parentNode;
18441             }
18442             this.childIndent = buf.join("");
18443         }
18444         return this.childIndent;
18445     },
18446
18447     renderIndent : function(){
18448         if(this.rendered){
18449             var indent = "";
18450             var p = this.node.parentNode;
18451             if(p){
18452                 indent = p.ui.getChildIndent();
18453             }
18454             if(this.indentMarkup != indent){ // don't rerender if not required
18455                 this.indentNode.innerHTML = indent;
18456                 this.indentMarkup = indent;
18457             }
18458             this.updateExpandIcon();
18459         }
18460     }
18461 };
18462
18463 Roo.tree.RootTreeNodeUI = function(){
18464     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18465 };
18466 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18467     render : function(){
18468         if(!this.rendered){
18469             var targetNode = this.node.ownerTree.innerCt.dom;
18470             this.node.expanded = true;
18471             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18472             this.wrap = this.ctNode = targetNode.firstChild;
18473         }
18474     },
18475     collapse : function(){
18476     },
18477     expand : function(){
18478     }
18479 });/*
18480  * Based on:
18481  * Ext JS Library 1.1.1
18482  * Copyright(c) 2006-2007, Ext JS, LLC.
18483  *
18484  * Originally Released Under LGPL - original licence link has changed is not relivant.
18485  *
18486  * Fork - LGPL
18487  * <script type="text/javascript">
18488  */
18489 /**
18490  * @class Roo.tree.TreeLoader
18491  * @extends Roo.util.Observable
18492  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18493  * nodes from a specified URL. The response must be a javascript Array definition
18494  * who's elements are node definition objects. eg:
18495  * <pre><code>
18496 {  success : true,
18497    data :      [
18498    
18499     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18500     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18501     ]
18502 }
18503
18504
18505 </code></pre>
18506  * <br><br>
18507  * The old style respose with just an array is still supported, but not recommended.
18508  * <br><br>
18509  *
18510  * A server request is sent, and child nodes are loaded only when a node is expanded.
18511  * The loading node's id is passed to the server under the parameter name "node" to
18512  * enable the server to produce the correct child nodes.
18513  * <br><br>
18514  * To pass extra parameters, an event handler may be attached to the "beforeload"
18515  * event, and the parameters specified in the TreeLoader's baseParams property:
18516  * <pre><code>
18517     myTreeLoader.on("beforeload", function(treeLoader, node) {
18518         this.baseParams.category = node.attributes.category;
18519     }, this);
18520 </code></pre><
18521  * This would pass an HTTP parameter called "category" to the server containing
18522  * the value of the Node's "category" attribute.
18523  * @constructor
18524  * Creates a new Treeloader.
18525  * @param {Object} config A config object containing config properties.
18526  */
18527 Roo.tree.TreeLoader = function(config){
18528     this.baseParams = {};
18529     this.requestMethod = "POST";
18530     Roo.apply(this, config);
18531
18532     this.addEvents({
18533     
18534         /**
18535          * @event beforeload
18536          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18537          * @param {Object} This TreeLoader object.
18538          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18539          * @param {Object} callback The callback function specified in the {@link #load} call.
18540          */
18541         beforeload : true,
18542         /**
18543          * @event load
18544          * Fires when the node has been successfuly loaded.
18545          * @param {Object} This TreeLoader object.
18546          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18547          * @param {Object} response The response object containing the data from the server.
18548          */
18549         load : true,
18550         /**
18551          * @event loadexception
18552          * Fires if the network request failed.
18553          * @param {Object} This TreeLoader object.
18554          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18555          * @param {Object} response The response object containing the data from the server.
18556          */
18557         loadexception : true,
18558         /**
18559          * @event create
18560          * Fires before a node is created, enabling you to return custom Node types 
18561          * @param {Object} This TreeLoader object.
18562          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18563          */
18564         create : true
18565     });
18566
18567     Roo.tree.TreeLoader.superclass.constructor.call(this);
18568 };
18569
18570 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18571     /**
18572     * @cfg {String} dataUrl The URL from which to request a Json string which
18573     * specifies an array of node definition object representing the child nodes
18574     * to be loaded.
18575     */
18576     /**
18577     * @cfg {String} requestMethod either GET or POST
18578     * defaults to POST (due to BC)
18579     * to be loaded.
18580     */
18581     /**
18582     * @cfg {Object} baseParams (optional) An object containing properties which
18583     * specify HTTP parameters to be passed to each request for child nodes.
18584     */
18585     /**
18586     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18587     * created by this loader. If the attributes sent by the server have an attribute in this object,
18588     * they take priority.
18589     */
18590     /**
18591     * @cfg {Object} uiProviders (optional) An object containing properties which
18592     * 
18593     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18594     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18595     * <i>uiProvider</i> attribute of a returned child node is a string rather
18596     * than a reference to a TreeNodeUI implementation, this that string value
18597     * is used as a property name in the uiProviders object. You can define the provider named
18598     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18599     */
18600     uiProviders : {},
18601
18602     /**
18603     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18604     * child nodes before loading.
18605     */
18606     clearOnLoad : true,
18607
18608     /**
18609     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18610     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18611     * Grid query { data : [ .....] }
18612     */
18613     
18614     root : false,
18615      /**
18616     * @cfg {String} queryParam (optional) 
18617     * Name of the query as it will be passed on the querystring (defaults to 'node')
18618     * eg. the request will be ?node=[id]
18619     */
18620     
18621     
18622     queryParam: false,
18623     
18624     /**
18625      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18626      * This is called automatically when a node is expanded, but may be used to reload
18627      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18628      * @param {Roo.tree.TreeNode} node
18629      * @param {Function} callback
18630      */
18631     load : function(node, callback){
18632         if(this.clearOnLoad){
18633             while(node.firstChild){
18634                 node.removeChild(node.firstChild);
18635             }
18636         }
18637         if(node.attributes.children){ // preloaded json children
18638             var cs = node.attributes.children;
18639             for(var i = 0, len = cs.length; i < len; i++){
18640                 node.appendChild(this.createNode(cs[i]));
18641             }
18642             if(typeof callback == "function"){
18643                 callback();
18644             }
18645         }else if(this.dataUrl){
18646             this.requestData(node, callback);
18647         }
18648     },
18649
18650     getParams: function(node){
18651         var buf = [], bp = this.baseParams;
18652         for(var key in bp){
18653             if(typeof bp[key] != "function"){
18654                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18655             }
18656         }
18657         var n = this.queryParam === false ? 'node' : this.queryParam;
18658         buf.push(n + "=", encodeURIComponent(node.id));
18659         return buf.join("");
18660     },
18661
18662     requestData : function(node, callback){
18663         if(this.fireEvent("beforeload", this, node, callback) !== false){
18664             this.transId = Roo.Ajax.request({
18665                 method:this.requestMethod,
18666                 url: this.dataUrl||this.url,
18667                 success: this.handleResponse,
18668                 failure: this.handleFailure,
18669                 scope: this,
18670                 argument: {callback: callback, node: node},
18671                 params: this.getParams(node)
18672             });
18673         }else{
18674             // if the load is cancelled, make sure we notify
18675             // the node that we are done
18676             if(typeof callback == "function"){
18677                 callback();
18678             }
18679         }
18680     },
18681
18682     isLoading : function(){
18683         return this.transId ? true : false;
18684     },
18685
18686     abort : function(){
18687         if(this.isLoading()){
18688             Roo.Ajax.abort(this.transId);
18689         }
18690     },
18691
18692     // private
18693     createNode : function(attr)
18694     {
18695         // apply baseAttrs, nice idea Corey!
18696         if(this.baseAttrs){
18697             Roo.applyIf(attr, this.baseAttrs);
18698         }
18699         if(this.applyLoader !== false){
18700             attr.loader = this;
18701         }
18702         // uiProvider = depreciated..
18703         
18704         if(typeof(attr.uiProvider) == 'string'){
18705            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18706                 /**  eval:var:attr */ eval(attr.uiProvider);
18707         }
18708         if(typeof(this.uiProviders['default']) != 'undefined') {
18709             attr.uiProvider = this.uiProviders['default'];
18710         }
18711         
18712         this.fireEvent('create', this, attr);
18713         
18714         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18715         return(attr.leaf ?
18716                         new Roo.tree.TreeNode(attr) :
18717                         new Roo.tree.AsyncTreeNode(attr));
18718     },
18719
18720     processResponse : function(response, node, callback)
18721     {
18722         var json = response.responseText;
18723         try {
18724             
18725             var o = Roo.decode(json);
18726             
18727             if (this.root === false && typeof(o.success) != undefined) {
18728                 this.root = 'data'; // the default behaviour for list like data..
18729                 }
18730                 
18731             if (this.root !== false &&  !o.success) {
18732                 // it's a failure condition.
18733                 var a = response.argument;
18734                 this.fireEvent("loadexception", this, a.node, response);
18735                 Roo.log("Load failed - should have a handler really");
18736                 return;
18737             }
18738             
18739             
18740             
18741             if (this.root !== false) {
18742                  o = o[this.root];
18743             }
18744             
18745             for(var i = 0, len = o.length; i < len; i++){
18746                 var n = this.createNode(o[i]);
18747                 if(n){
18748                     node.appendChild(n);
18749                 }
18750             }
18751             if(typeof callback == "function"){
18752                 callback(this, node);
18753             }
18754         }catch(e){
18755             this.handleFailure(response);
18756         }
18757     },
18758
18759     handleResponse : function(response){
18760         this.transId = false;
18761         var a = response.argument;
18762         this.processResponse(response, a.node, a.callback);
18763         this.fireEvent("load", this, a.node, response);
18764     },
18765
18766     handleFailure : function(response)
18767     {
18768         // should handle failure better..
18769         this.transId = false;
18770         var a = response.argument;
18771         this.fireEvent("loadexception", this, a.node, response);
18772         if(typeof a.callback == "function"){
18773             a.callback(this, a.node);
18774         }
18775     }
18776 });/*
18777  * Based on:
18778  * Ext JS Library 1.1.1
18779  * Copyright(c) 2006-2007, Ext JS, LLC.
18780  *
18781  * Originally Released Under LGPL - original licence link has changed is not relivant.
18782  *
18783  * Fork - LGPL
18784  * <script type="text/javascript">
18785  */
18786
18787 /**
18788 * @class Roo.tree.TreeFilter
18789 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18790 * @param {TreePanel} tree
18791 * @param {Object} config (optional)
18792  */
18793 Roo.tree.TreeFilter = function(tree, config){
18794     this.tree = tree;
18795     this.filtered = {};
18796     Roo.apply(this, config);
18797 };
18798
18799 Roo.tree.TreeFilter.prototype = {
18800     clearBlank:false,
18801     reverse:false,
18802     autoClear:false,
18803     remove:false,
18804
18805      /**
18806      * Filter the data by a specific attribute.
18807      * @param {String/RegExp} value Either string that the attribute value
18808      * should start with or a RegExp to test against the attribute
18809      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18810      * @param {TreeNode} startNode (optional) The node to start the filter at.
18811      */
18812     filter : function(value, attr, startNode){
18813         attr = attr || "text";
18814         var f;
18815         if(typeof value == "string"){
18816             var vlen = value.length;
18817             // auto clear empty filter
18818             if(vlen == 0 && this.clearBlank){
18819                 this.clear();
18820                 return;
18821             }
18822             value = value.toLowerCase();
18823             f = function(n){
18824                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18825             };
18826         }else if(value.exec){ // regex?
18827             f = function(n){
18828                 return value.test(n.attributes[attr]);
18829             };
18830         }else{
18831             throw 'Illegal filter type, must be string or regex';
18832         }
18833         this.filterBy(f, null, startNode);
18834         },
18835
18836     /**
18837      * Filter by a function. The passed function will be called with each
18838      * node in the tree (or from the startNode). If the function returns true, the node is kept
18839      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18840      * @param {Function} fn The filter function
18841      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18842      */
18843     filterBy : function(fn, scope, startNode){
18844         startNode = startNode || this.tree.root;
18845         if(this.autoClear){
18846             this.clear();
18847         }
18848         var af = this.filtered, rv = this.reverse;
18849         var f = function(n){
18850             if(n == startNode){
18851                 return true;
18852             }
18853             if(af[n.id]){
18854                 return false;
18855             }
18856             var m = fn.call(scope || n, n);
18857             if(!m || rv){
18858                 af[n.id] = n;
18859                 n.ui.hide();
18860                 return false;
18861             }
18862             return true;
18863         };
18864         startNode.cascade(f);
18865         if(this.remove){
18866            for(var id in af){
18867                if(typeof id != "function"){
18868                    var n = af[id];
18869                    if(n && n.parentNode){
18870                        n.parentNode.removeChild(n);
18871                    }
18872                }
18873            }
18874         }
18875     },
18876
18877     /**
18878      * Clears the current filter. Note: with the "remove" option
18879      * set a filter cannot be cleared.
18880      */
18881     clear : function(){
18882         var t = this.tree;
18883         var af = this.filtered;
18884         for(var id in af){
18885             if(typeof id != "function"){
18886                 var n = af[id];
18887                 if(n){
18888                     n.ui.show();
18889                 }
18890             }
18891         }
18892         this.filtered = {};
18893     }
18894 };
18895 /*
18896  * Based on:
18897  * Ext JS Library 1.1.1
18898  * Copyright(c) 2006-2007, Ext JS, LLC.
18899  *
18900  * Originally Released Under LGPL - original licence link has changed is not relivant.
18901  *
18902  * Fork - LGPL
18903  * <script type="text/javascript">
18904  */
18905  
18906
18907 /**
18908  * @class Roo.tree.TreeSorter
18909  * Provides sorting of nodes in a TreePanel
18910  * 
18911  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18912  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18913  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18914  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18915  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18916  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18917  * @constructor
18918  * @param {TreePanel} tree
18919  * @param {Object} config
18920  */
18921 Roo.tree.TreeSorter = function(tree, config){
18922     Roo.apply(this, config);
18923     tree.on("beforechildrenrendered", this.doSort, this);
18924     tree.on("append", this.updateSort, this);
18925     tree.on("insert", this.updateSort, this);
18926     
18927     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18928     var p = this.property || "text";
18929     var sortType = this.sortType;
18930     var fs = this.folderSort;
18931     var cs = this.caseSensitive === true;
18932     var leafAttr = this.leafAttr || 'leaf';
18933
18934     this.sortFn = function(n1, n2){
18935         if(fs){
18936             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18937                 return 1;
18938             }
18939             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18940                 return -1;
18941             }
18942         }
18943         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18944         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18945         if(v1 < v2){
18946                         return dsc ? +1 : -1;
18947                 }else if(v1 > v2){
18948                         return dsc ? -1 : +1;
18949         }else{
18950                 return 0;
18951         }
18952     };
18953 };
18954
18955 Roo.tree.TreeSorter.prototype = {
18956     doSort : function(node){
18957         node.sort(this.sortFn);
18958     },
18959     
18960     compareNodes : function(n1, n2){
18961         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18962     },
18963     
18964     updateSort : function(tree, node){
18965         if(node.childrenRendered){
18966             this.doSort.defer(1, this, [node]);
18967         }
18968     }
18969 };/*
18970  * Based on:
18971  * Ext JS Library 1.1.1
18972  * Copyright(c) 2006-2007, Ext JS, LLC.
18973  *
18974  * Originally Released Under LGPL - original licence link has changed is not relivant.
18975  *
18976  * Fork - LGPL
18977  * <script type="text/javascript">
18978  */
18979
18980 if(Roo.dd.DropZone){
18981     
18982 Roo.tree.TreeDropZone = function(tree, config){
18983     this.allowParentInsert = false;
18984     this.allowContainerDrop = false;
18985     this.appendOnly = false;
18986     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18987     this.tree = tree;
18988     this.lastInsertClass = "x-tree-no-status";
18989     this.dragOverData = {};
18990 };
18991
18992 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18993     ddGroup : "TreeDD",
18994     scroll:  true,
18995     
18996     expandDelay : 1000,
18997     
18998     expandNode : function(node){
18999         if(node.hasChildNodes() && !node.isExpanded()){
19000             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19001         }
19002     },
19003     
19004     queueExpand : function(node){
19005         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19006     },
19007     
19008     cancelExpand : function(){
19009         if(this.expandProcId){
19010             clearTimeout(this.expandProcId);
19011             this.expandProcId = false;
19012         }
19013     },
19014     
19015     isValidDropPoint : function(n, pt, dd, e, data){
19016         if(!n || !data){ return false; }
19017         var targetNode = n.node;
19018         var dropNode = data.node;
19019         // default drop rules
19020         if(!(targetNode && targetNode.isTarget && pt)){
19021             return false;
19022         }
19023         if(pt == "append" && targetNode.allowChildren === false){
19024             return false;
19025         }
19026         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19027             return false;
19028         }
19029         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19030             return false;
19031         }
19032         // reuse the object
19033         var overEvent = this.dragOverData;
19034         overEvent.tree = this.tree;
19035         overEvent.target = targetNode;
19036         overEvent.data = data;
19037         overEvent.point = pt;
19038         overEvent.source = dd;
19039         overEvent.rawEvent = e;
19040         overEvent.dropNode = dropNode;
19041         overEvent.cancel = false;  
19042         var result = this.tree.fireEvent("nodedragover", overEvent);
19043         return overEvent.cancel === false && result !== false;
19044     },
19045     
19046     getDropPoint : function(e, n, dd)
19047     {
19048         var tn = n.node;
19049         if(tn.isRoot){
19050             return tn.allowChildren !== false ? "append" : false; // always append for root
19051         }
19052         var dragEl = n.ddel;
19053         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19054         var y = Roo.lib.Event.getPageY(e);
19055         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19056         
19057         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19058         var noAppend = tn.allowChildren === false;
19059         if(this.appendOnly || tn.parentNode.allowChildren === false){
19060             return noAppend ? false : "append";
19061         }
19062         var noBelow = false;
19063         if(!this.allowParentInsert){
19064             noBelow = tn.hasChildNodes() && tn.isExpanded();
19065         }
19066         var q = (b - t) / (noAppend ? 2 : 3);
19067         if(y >= t && y < (t + q)){
19068             return "above";
19069         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19070             return "below";
19071         }else{
19072             return "append";
19073         }
19074     },
19075     
19076     onNodeEnter : function(n, dd, e, data)
19077     {
19078         this.cancelExpand();
19079     },
19080     
19081     onNodeOver : function(n, dd, e, data)
19082     {
19083        
19084         var pt = this.getDropPoint(e, n, dd);
19085         var node = n.node;
19086         
19087         // auto node expand check
19088         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19089             this.queueExpand(node);
19090         }else if(pt != "append"){
19091             this.cancelExpand();
19092         }
19093         
19094         // set the insert point style on the target node
19095         var returnCls = this.dropNotAllowed;
19096         if(this.isValidDropPoint(n, pt, dd, e, data)){
19097            if(pt){
19098                var el = n.ddel;
19099                var cls;
19100                if(pt == "above"){
19101                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19102                    cls = "x-tree-drag-insert-above";
19103                }else if(pt == "below"){
19104                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19105                    cls = "x-tree-drag-insert-below";
19106                }else{
19107                    returnCls = "x-tree-drop-ok-append";
19108                    cls = "x-tree-drag-append";
19109                }
19110                if(this.lastInsertClass != cls){
19111                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19112                    this.lastInsertClass = cls;
19113                }
19114            }
19115        }
19116        return returnCls;
19117     },
19118     
19119     onNodeOut : function(n, dd, e, data){
19120         
19121         this.cancelExpand();
19122         this.removeDropIndicators(n);
19123     },
19124     
19125     onNodeDrop : function(n, dd, e, data){
19126         var point = this.getDropPoint(e, n, dd);
19127         var targetNode = n.node;
19128         targetNode.ui.startDrop();
19129         if(!this.isValidDropPoint(n, point, dd, e, data)){
19130             targetNode.ui.endDrop();
19131             return false;
19132         }
19133         // first try to find the drop node
19134         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19135         var dropEvent = {
19136             tree : this.tree,
19137             target: targetNode,
19138             data: data,
19139             point: point,
19140             source: dd,
19141             rawEvent: e,
19142             dropNode: dropNode,
19143             cancel: !dropNode   
19144         };
19145         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19146         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19147             targetNode.ui.endDrop();
19148             return false;
19149         }
19150         // allow target changing
19151         targetNode = dropEvent.target;
19152         if(point == "append" && !targetNode.isExpanded()){
19153             targetNode.expand(false, null, function(){
19154                 this.completeDrop(dropEvent);
19155             }.createDelegate(this));
19156         }else{
19157             this.completeDrop(dropEvent);
19158         }
19159         return true;
19160     },
19161     
19162     completeDrop : function(de){
19163         var ns = de.dropNode, p = de.point, t = de.target;
19164         if(!(ns instanceof Array)){
19165             ns = [ns];
19166         }
19167         var n;
19168         for(var i = 0, len = ns.length; i < len; i++){
19169             n = ns[i];
19170             if(p == "above"){
19171                 t.parentNode.insertBefore(n, t);
19172             }else if(p == "below"){
19173                 t.parentNode.insertBefore(n, t.nextSibling);
19174             }else{
19175                 t.appendChild(n);
19176             }
19177         }
19178         n.ui.focus();
19179         if(this.tree.hlDrop){
19180             n.ui.highlight();
19181         }
19182         t.ui.endDrop();
19183         this.tree.fireEvent("nodedrop", de);
19184     },
19185     
19186     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19187         if(this.tree.hlDrop){
19188             dropNode.ui.focus();
19189             dropNode.ui.highlight();
19190         }
19191         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19192     },
19193     
19194     getTree : function(){
19195         return this.tree;
19196     },
19197     
19198     removeDropIndicators : function(n){
19199         if(n && n.ddel){
19200             var el = n.ddel;
19201             Roo.fly(el).removeClass([
19202                     "x-tree-drag-insert-above",
19203                     "x-tree-drag-insert-below",
19204                     "x-tree-drag-append"]);
19205             this.lastInsertClass = "_noclass";
19206         }
19207     },
19208     
19209     beforeDragDrop : function(target, e, id){
19210         this.cancelExpand();
19211         return true;
19212     },
19213     
19214     afterRepair : function(data){
19215         if(data && Roo.enableFx){
19216             data.node.ui.highlight();
19217         }
19218         this.hideProxy();
19219     } 
19220     
19221 });
19222
19223 }
19224 /*
19225  * Based on:
19226  * Ext JS Library 1.1.1
19227  * Copyright(c) 2006-2007, Ext JS, LLC.
19228  *
19229  * Originally Released Under LGPL - original licence link has changed is not relivant.
19230  *
19231  * Fork - LGPL
19232  * <script type="text/javascript">
19233  */
19234  
19235
19236 if(Roo.dd.DragZone){
19237 Roo.tree.TreeDragZone = function(tree, config){
19238     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19239     this.tree = tree;
19240 };
19241
19242 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19243     ddGroup : "TreeDD",
19244    
19245     onBeforeDrag : function(data, e){
19246         var n = data.node;
19247         return n && n.draggable && !n.disabled;
19248     },
19249      
19250     
19251     onInitDrag : function(e){
19252         var data = this.dragData;
19253         this.tree.getSelectionModel().select(data.node);
19254         this.proxy.update("");
19255         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19256         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19257     },
19258     
19259     getRepairXY : function(e, data){
19260         return data.node.ui.getDDRepairXY();
19261     },
19262     
19263     onEndDrag : function(data, e){
19264         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19265         
19266         
19267     },
19268     
19269     onValidDrop : function(dd, e, id){
19270         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19271         this.hideProxy();
19272     },
19273     
19274     beforeInvalidDrop : function(e, id){
19275         // this scrolls the original position back into view
19276         var sm = this.tree.getSelectionModel();
19277         sm.clearSelections();
19278         sm.select(this.dragData.node);
19279     }
19280 });
19281 }/*
19282  * Based on:
19283  * Ext JS Library 1.1.1
19284  * Copyright(c) 2006-2007, Ext JS, LLC.
19285  *
19286  * Originally Released Under LGPL - original licence link has changed is not relivant.
19287  *
19288  * Fork - LGPL
19289  * <script type="text/javascript">
19290  */
19291 /**
19292  * @class Roo.tree.TreeEditor
19293  * @extends Roo.Editor
19294  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19295  * as the editor field.
19296  * @constructor
19297  * @param {Object} config (used to be the tree panel.)
19298  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19299  * 
19300  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19301  * @cfg {Roo.form.TextField|Object} field The field configuration
19302  *
19303  * 
19304  */
19305 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19306     var tree = config;
19307     var field;
19308     if (oldconfig) { // old style..
19309         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19310     } else {
19311         // new style..
19312         tree = config.tree;
19313         config.field = config.field  || {};
19314         config.field.xtype = 'TextField';
19315         field = Roo.factory(config.field, Roo.form);
19316     }
19317     config = config || {};
19318     
19319     
19320     this.addEvents({
19321         /**
19322          * @event beforenodeedit
19323          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19324          * false from the handler of this event.
19325          * @param {Editor} this
19326          * @param {Roo.tree.Node} node 
19327          */
19328         "beforenodeedit" : true
19329     });
19330     
19331     //Roo.log(config);
19332     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19333
19334     this.tree = tree;
19335
19336     tree.on('beforeclick', this.beforeNodeClick, this);
19337     tree.getTreeEl().on('mousedown', this.hide, this);
19338     this.on('complete', this.updateNode, this);
19339     this.on('beforestartedit', this.fitToTree, this);
19340     this.on('startedit', this.bindScroll, this, {delay:10});
19341     this.on('specialkey', this.onSpecialKey, this);
19342 };
19343
19344 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19345     /**
19346      * @cfg {String} alignment
19347      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19348      */
19349     alignment: "l-l",
19350     // inherit
19351     autoSize: false,
19352     /**
19353      * @cfg {Boolean} hideEl
19354      * True to hide the bound element while the editor is displayed (defaults to false)
19355      */
19356     hideEl : false,
19357     /**
19358      * @cfg {String} cls
19359      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19360      */
19361     cls: "x-small-editor x-tree-editor",
19362     /**
19363      * @cfg {Boolean} shim
19364      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19365      */
19366     shim:false,
19367     // inherit
19368     shadow:"frame",
19369     /**
19370      * @cfg {Number} maxWidth
19371      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19372      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19373      * scroll and client offsets into account prior to each edit.
19374      */
19375     maxWidth: 250,
19376
19377     editDelay : 350,
19378
19379     // private
19380     fitToTree : function(ed, el){
19381         var td = this.tree.getTreeEl().dom, nd = el.dom;
19382         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19383             td.scrollLeft = nd.offsetLeft;
19384         }
19385         var w = Math.min(
19386                 this.maxWidth,
19387                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19388         this.setSize(w, '');
19389         
19390         return this.fireEvent('beforenodeedit', this, this.editNode);
19391         
19392     },
19393
19394     // private
19395     triggerEdit : function(node){
19396         this.completeEdit();
19397         this.editNode = node;
19398         this.startEdit(node.ui.textNode, node.text);
19399     },
19400
19401     // private
19402     bindScroll : function(){
19403         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19404     },
19405
19406     // private
19407     beforeNodeClick : function(node, e){
19408         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19409         this.lastClick = new Date();
19410         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19411             e.stopEvent();
19412             this.triggerEdit(node);
19413             return false;
19414         }
19415         return true;
19416     },
19417
19418     // private
19419     updateNode : function(ed, value){
19420         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19421         this.editNode.setText(value);
19422     },
19423
19424     // private
19425     onHide : function(){
19426         Roo.tree.TreeEditor.superclass.onHide.call(this);
19427         if(this.editNode){
19428             this.editNode.ui.focus();
19429         }
19430     },
19431
19432     // private
19433     onSpecialKey : function(field, e){
19434         var k = e.getKey();
19435         if(k == e.ESC){
19436             e.stopEvent();
19437             this.cancelEdit();
19438         }else if(k == e.ENTER && !e.hasModifier()){
19439             e.stopEvent();
19440             this.completeEdit();
19441         }
19442     }
19443 });//<Script type="text/javascript">
19444 /*
19445  * Based on:
19446  * Ext JS Library 1.1.1
19447  * Copyright(c) 2006-2007, Ext JS, LLC.
19448  *
19449  * Originally Released Under LGPL - original licence link has changed is not relivant.
19450  *
19451  * Fork - LGPL
19452  * <script type="text/javascript">
19453  */
19454  
19455 /**
19456  * Not documented??? - probably should be...
19457  */
19458
19459 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19460     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19461     
19462     renderElements : function(n, a, targetNode, bulkRender){
19463         //consel.log("renderElements?");
19464         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19465
19466         var t = n.getOwnerTree();
19467         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19468         
19469         var cols = t.columns;
19470         var bw = t.borderWidth;
19471         var c = cols[0];
19472         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19473          var cb = typeof a.checked == "boolean";
19474         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19475         var colcls = 'x-t-' + tid + '-c0';
19476         var buf = [
19477             '<li class="x-tree-node">',
19478             
19479                 
19480                 '<div class="x-tree-node-el ', a.cls,'">',
19481                     // extran...
19482                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19483                 
19484                 
19485                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19486                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19487                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19488                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19489                            (a.iconCls ? ' '+a.iconCls : ''),
19490                            '" unselectable="on" />',
19491                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19492                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19493                              
19494                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19495                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19496                             '<span unselectable="on" qtip="' + tx + '">',
19497                              tx,
19498                              '</span></a>' ,
19499                     '</div>',
19500                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19501                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19502                  ];
19503         for(var i = 1, len = cols.length; i < len; i++){
19504             c = cols[i];
19505             colcls = 'x-t-' + tid + '-c' +i;
19506             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19507             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19508                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19509                       "</div>");
19510          }
19511          
19512          buf.push(
19513             '</a>',
19514             '<div class="x-clear"></div></div>',
19515             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19516             "</li>");
19517         
19518         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19519             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19520                                 n.nextSibling.ui.getEl(), buf.join(""));
19521         }else{
19522             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19523         }
19524         var el = this.wrap.firstChild;
19525         this.elRow = el;
19526         this.elNode = el.firstChild;
19527         this.ranchor = el.childNodes[1];
19528         this.ctNode = this.wrap.childNodes[1];
19529         var cs = el.firstChild.childNodes;
19530         this.indentNode = cs[0];
19531         this.ecNode = cs[1];
19532         this.iconNode = cs[2];
19533         var index = 3;
19534         if(cb){
19535             this.checkbox = cs[3];
19536             index++;
19537         }
19538         this.anchor = cs[index];
19539         
19540         this.textNode = cs[index].firstChild;
19541         
19542         //el.on("click", this.onClick, this);
19543         //el.on("dblclick", this.onDblClick, this);
19544         
19545         
19546        // console.log(this);
19547     },
19548     initEvents : function(){
19549         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19550         
19551             
19552         var a = this.ranchor;
19553
19554         var el = Roo.get(a);
19555
19556         if(Roo.isOpera){ // opera render bug ignores the CSS
19557             el.setStyle("text-decoration", "none");
19558         }
19559
19560         el.on("click", this.onClick, this);
19561         el.on("dblclick", this.onDblClick, this);
19562         el.on("contextmenu", this.onContextMenu, this);
19563         
19564     },
19565     
19566     /*onSelectedChange : function(state){
19567         if(state){
19568             this.focus();
19569             this.addClass("x-tree-selected");
19570         }else{
19571             //this.blur();
19572             this.removeClass("x-tree-selected");
19573         }
19574     },*/
19575     addClass : function(cls){
19576         if(this.elRow){
19577             Roo.fly(this.elRow).addClass(cls);
19578         }
19579         
19580     },
19581     
19582     
19583     removeClass : function(cls){
19584         if(this.elRow){
19585             Roo.fly(this.elRow).removeClass(cls);
19586         }
19587     }
19588
19589     
19590     
19591 });//<Script type="text/javascript">
19592
19593 /*
19594  * Based on:
19595  * Ext JS Library 1.1.1
19596  * Copyright(c) 2006-2007, Ext JS, LLC.
19597  *
19598  * Originally Released Under LGPL - original licence link has changed is not relivant.
19599  *
19600  * Fork - LGPL
19601  * <script type="text/javascript">
19602  */
19603  
19604
19605 /**
19606  * @class Roo.tree.ColumnTree
19607  * @extends Roo.data.TreePanel
19608  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19609  * @cfg {int} borderWidth  compined right/left border allowance
19610  * @constructor
19611  * @param {String/HTMLElement/Element} el The container element
19612  * @param {Object} config
19613  */
19614 Roo.tree.ColumnTree =  function(el, config)
19615 {
19616    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19617    this.addEvents({
19618         /**
19619         * @event resize
19620         * Fire this event on a container when it resizes
19621         * @param {int} w Width
19622         * @param {int} h Height
19623         */
19624        "resize" : true
19625     });
19626     this.on('resize', this.onResize, this);
19627 };
19628
19629 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19630     //lines:false,
19631     
19632     
19633     borderWidth: Roo.isBorderBox ? 0 : 2, 
19634     headEls : false,
19635     
19636     render : function(){
19637         // add the header.....
19638        
19639         Roo.tree.ColumnTree.superclass.render.apply(this);
19640         
19641         this.el.addClass('x-column-tree');
19642         
19643         this.headers = this.el.createChild(
19644             {cls:'x-tree-headers'},this.innerCt.dom);
19645    
19646         var cols = this.columns, c;
19647         var totalWidth = 0;
19648         this.headEls = [];
19649         var  len = cols.length;
19650         for(var i = 0; i < len; i++){
19651              c = cols[i];
19652              totalWidth += c.width;
19653             this.headEls.push(this.headers.createChild({
19654                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19655                  cn: {
19656                      cls:'x-tree-hd-text',
19657                      html: c.header
19658                  },
19659                  style:'width:'+(c.width-this.borderWidth)+'px;'
19660              }));
19661         }
19662         this.headers.createChild({cls:'x-clear'});
19663         // prevent floats from wrapping when clipped
19664         this.headers.setWidth(totalWidth);
19665         //this.innerCt.setWidth(totalWidth);
19666         this.innerCt.setStyle({ overflow: 'auto' });
19667         this.onResize(this.width, this.height);
19668              
19669         
19670     },
19671     onResize : function(w,h)
19672     {
19673         this.height = h;
19674         this.width = w;
19675         // resize cols..
19676         this.innerCt.setWidth(this.width);
19677         this.innerCt.setHeight(this.height-20);
19678         
19679         // headers...
19680         var cols = this.columns, c;
19681         var totalWidth = 0;
19682         var expEl = false;
19683         var len = cols.length;
19684         for(var i = 0; i < len; i++){
19685             c = cols[i];
19686             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19687                 // it's the expander..
19688                 expEl  = this.headEls[i];
19689                 continue;
19690             }
19691             totalWidth += c.width;
19692             
19693         }
19694         if (expEl) {
19695             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19696         }
19697         this.headers.setWidth(w-20);
19698
19699         
19700         
19701         
19702     }
19703 });
19704 /*
19705  * Based on:
19706  * Ext JS Library 1.1.1
19707  * Copyright(c) 2006-2007, Ext JS, LLC.
19708  *
19709  * Originally Released Under LGPL - original licence link has changed is not relivant.
19710  *
19711  * Fork - LGPL
19712  * <script type="text/javascript">
19713  */
19714  
19715 /**
19716  * @class Roo.menu.Menu
19717  * @extends Roo.util.Observable
19718  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19719  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19720  * @constructor
19721  * Creates a new Menu
19722  * @param {Object} config Configuration options
19723  */
19724 Roo.menu.Menu = function(config){
19725     Roo.apply(this, config);
19726     this.id = this.id || Roo.id();
19727     this.addEvents({
19728         /**
19729          * @event beforeshow
19730          * Fires before this menu is displayed
19731          * @param {Roo.menu.Menu} this
19732          */
19733         beforeshow : true,
19734         /**
19735          * @event beforehide
19736          * Fires before this menu is hidden
19737          * @param {Roo.menu.Menu} this
19738          */
19739         beforehide : true,
19740         /**
19741          * @event show
19742          * Fires after this menu is displayed
19743          * @param {Roo.menu.Menu} this
19744          */
19745         show : true,
19746         /**
19747          * @event hide
19748          * Fires after this menu is hidden
19749          * @param {Roo.menu.Menu} this
19750          */
19751         hide : true,
19752         /**
19753          * @event click
19754          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19755          * @param {Roo.menu.Menu} this
19756          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19757          * @param {Roo.EventObject} e
19758          */
19759         click : true,
19760         /**
19761          * @event mouseover
19762          * Fires when the mouse is hovering over this menu
19763          * @param {Roo.menu.Menu} this
19764          * @param {Roo.EventObject} e
19765          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19766          */
19767         mouseover : true,
19768         /**
19769          * @event mouseout
19770          * Fires when the mouse exits this menu
19771          * @param {Roo.menu.Menu} this
19772          * @param {Roo.EventObject} e
19773          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19774          */
19775         mouseout : true,
19776         /**
19777          * @event itemclick
19778          * Fires when a menu item contained in this menu is clicked
19779          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19780          * @param {Roo.EventObject} e
19781          */
19782         itemclick: true
19783     });
19784     if (this.registerMenu) {
19785         Roo.menu.MenuMgr.register(this);
19786     }
19787     
19788     var mis = this.items;
19789     this.items = new Roo.util.MixedCollection();
19790     if(mis){
19791         this.add.apply(this, mis);
19792     }
19793 };
19794
19795 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19796     /**
19797      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19798      */
19799     minWidth : 120,
19800     /**
19801      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19802      * for bottom-right shadow (defaults to "sides")
19803      */
19804     shadow : "sides",
19805     /**
19806      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19807      * this menu (defaults to "tl-tr?")
19808      */
19809     subMenuAlign : "tl-tr?",
19810     /**
19811      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19812      * relative to its element of origin (defaults to "tl-bl?")
19813      */
19814     defaultAlign : "tl-bl?",
19815     /**
19816      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19817      */
19818     allowOtherMenus : false,
19819     /**
19820      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19821      */
19822     registerMenu : true,
19823
19824     hidden:true,
19825
19826     // private
19827     render : function(){
19828         if(this.el){
19829             return;
19830         }
19831         var el = this.el = new Roo.Layer({
19832             cls: "x-menu",
19833             shadow:this.shadow,
19834             constrain: false,
19835             parentEl: this.parentEl || document.body,
19836             zindex:15000
19837         });
19838
19839         this.keyNav = new Roo.menu.MenuNav(this);
19840
19841         if(this.plain){
19842             el.addClass("x-menu-plain");
19843         }
19844         if(this.cls){
19845             el.addClass(this.cls);
19846         }
19847         // generic focus element
19848         this.focusEl = el.createChild({
19849             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19850         });
19851         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19852         ul.on("click", this.onClick, this);
19853         ul.on("mouseover", this.onMouseOver, this);
19854         ul.on("mouseout", this.onMouseOut, this);
19855         this.items.each(function(item){
19856             if (item.hidden) {
19857                 return;
19858             }
19859             
19860             var li = document.createElement("li");
19861             li.className = "x-menu-list-item";
19862             ul.dom.appendChild(li);
19863             item.render(li, this);
19864         }, this);
19865         this.ul = ul;
19866         this.autoWidth();
19867     },
19868
19869     // private
19870     autoWidth : function(){
19871         var el = this.el, ul = this.ul;
19872         if(!el){
19873             return;
19874         }
19875         var w = this.width;
19876         if(w){
19877             el.setWidth(w);
19878         }else if(Roo.isIE){
19879             el.setWidth(this.minWidth);
19880             var t = el.dom.offsetWidth; // force recalc
19881             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19882         }
19883     },
19884
19885     // private
19886     delayAutoWidth : function(){
19887         if(this.rendered){
19888             if(!this.awTask){
19889                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19890             }
19891             this.awTask.delay(20);
19892         }
19893     },
19894
19895     // private
19896     findTargetItem : function(e){
19897         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19898         if(t && t.menuItemId){
19899             return this.items.get(t.menuItemId);
19900         }
19901     },
19902
19903     // private
19904     onClick : function(e){
19905         var t;
19906         if(t = this.findTargetItem(e)){
19907             t.onClick(e);
19908             this.fireEvent("click", this, t, e);
19909         }
19910     },
19911
19912     // private
19913     setActiveItem : function(item, autoExpand){
19914         if(item != this.activeItem){
19915             if(this.activeItem){
19916                 this.activeItem.deactivate();
19917             }
19918             this.activeItem = item;
19919             item.activate(autoExpand);
19920         }else if(autoExpand){
19921             item.expandMenu();
19922         }
19923     },
19924
19925     // private
19926     tryActivate : function(start, step){
19927         var items = this.items;
19928         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19929             var item = items.get(i);
19930             if(!item.disabled && item.canActivate){
19931                 this.setActiveItem(item, false);
19932                 return item;
19933             }
19934         }
19935         return false;
19936     },
19937
19938     // private
19939     onMouseOver : function(e){
19940         var t;
19941         if(t = this.findTargetItem(e)){
19942             if(t.canActivate && !t.disabled){
19943                 this.setActiveItem(t, true);
19944             }
19945         }
19946         this.fireEvent("mouseover", this, e, t);
19947     },
19948
19949     // private
19950     onMouseOut : function(e){
19951         var t;
19952         if(t = this.findTargetItem(e)){
19953             if(t == this.activeItem && t.shouldDeactivate(e)){
19954                 this.activeItem.deactivate();
19955                 delete this.activeItem;
19956             }
19957         }
19958         this.fireEvent("mouseout", this, e, t);
19959     },
19960
19961     /**
19962      * Read-only.  Returns true if the menu is currently displayed, else false.
19963      * @type Boolean
19964      */
19965     isVisible : function(){
19966         return this.el && !this.hidden;
19967     },
19968
19969     /**
19970      * Displays this menu relative to another element
19971      * @param {String/HTMLElement/Roo.Element} element The element to align to
19972      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19973      * the element (defaults to this.defaultAlign)
19974      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19975      */
19976     show : function(el, pos, parentMenu){
19977         this.parentMenu = parentMenu;
19978         if(!this.el){
19979             this.render();
19980         }
19981         this.fireEvent("beforeshow", this);
19982         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19983     },
19984
19985     /**
19986      * Displays this menu at a specific xy position
19987      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19988      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19989      */
19990     showAt : function(xy, parentMenu, /* private: */_e){
19991         this.parentMenu = parentMenu;
19992         if(!this.el){
19993             this.render();
19994         }
19995         if(_e !== false){
19996             this.fireEvent("beforeshow", this);
19997             xy = this.el.adjustForConstraints(xy);
19998         }
19999         this.el.setXY(xy);
20000         this.el.show();
20001         this.hidden = false;
20002         this.focus();
20003         this.fireEvent("show", this);
20004     },
20005
20006     focus : function(){
20007         if(!this.hidden){
20008             this.doFocus.defer(50, this);
20009         }
20010     },
20011
20012     doFocus : function(){
20013         if(!this.hidden){
20014             this.focusEl.focus();
20015         }
20016     },
20017
20018     /**
20019      * Hides this menu and optionally all parent menus
20020      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20021      */
20022     hide : function(deep){
20023         if(this.el && this.isVisible()){
20024             this.fireEvent("beforehide", this);
20025             if(this.activeItem){
20026                 this.activeItem.deactivate();
20027                 this.activeItem = null;
20028             }
20029             this.el.hide();
20030             this.hidden = true;
20031             this.fireEvent("hide", this);
20032         }
20033         if(deep === true && this.parentMenu){
20034             this.parentMenu.hide(true);
20035         }
20036     },
20037
20038     /**
20039      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20040      * Any of the following are valid:
20041      * <ul>
20042      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20043      * <li>An HTMLElement object which will be converted to a menu item</li>
20044      * <li>A menu item config object that will be created as a new menu item</li>
20045      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20046      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20047      * </ul>
20048      * Usage:
20049      * <pre><code>
20050 // Create the menu
20051 var menu = new Roo.menu.Menu();
20052
20053 // Create a menu item to add by reference
20054 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20055
20056 // Add a bunch of items at once using different methods.
20057 // Only the last item added will be returned.
20058 var item = menu.add(
20059     menuItem,                // add existing item by ref
20060     'Dynamic Item',          // new TextItem
20061     '-',                     // new separator
20062     { text: 'Config Item' }  // new item by config
20063 );
20064 </code></pre>
20065      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20066      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20067      */
20068     add : function(){
20069         var a = arguments, l = a.length, item;
20070         for(var i = 0; i < l; i++){
20071             var el = a[i];
20072             if ((typeof(el) == "object") && el.xtype && el.xns) {
20073                 el = Roo.factory(el, Roo.menu);
20074             }
20075             
20076             if(el.render){ // some kind of Item
20077                 item = this.addItem(el);
20078             }else if(typeof el == "string"){ // string
20079                 if(el == "separator" || el == "-"){
20080                     item = this.addSeparator();
20081                 }else{
20082                     item = this.addText(el);
20083                 }
20084             }else if(el.tagName || el.el){ // element
20085                 item = this.addElement(el);
20086             }else if(typeof el == "object"){ // must be menu item config?
20087                 item = this.addMenuItem(el);
20088             }
20089         }
20090         return item;
20091     },
20092
20093     /**
20094      * Returns this menu's underlying {@link Roo.Element} object
20095      * @return {Roo.Element} The element
20096      */
20097     getEl : function(){
20098         if(!this.el){
20099             this.render();
20100         }
20101         return this.el;
20102     },
20103
20104     /**
20105      * Adds a separator bar to the menu
20106      * @return {Roo.menu.Item} The menu item that was added
20107      */
20108     addSeparator : function(){
20109         return this.addItem(new Roo.menu.Separator());
20110     },
20111
20112     /**
20113      * Adds an {@link Roo.Element} object to the menu
20114      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20115      * @return {Roo.menu.Item} The menu item that was added
20116      */
20117     addElement : function(el){
20118         return this.addItem(new Roo.menu.BaseItem(el));
20119     },
20120
20121     /**
20122      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20123      * @param {Roo.menu.Item} item The menu item to add
20124      * @return {Roo.menu.Item} The menu item that was added
20125      */
20126     addItem : function(item){
20127         this.items.add(item);
20128         if(this.ul){
20129             var li = document.createElement("li");
20130             li.className = "x-menu-list-item";
20131             this.ul.dom.appendChild(li);
20132             item.render(li, this);
20133             this.delayAutoWidth();
20134         }
20135         return item;
20136     },
20137
20138     /**
20139      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20140      * @param {Object} config A MenuItem config object
20141      * @return {Roo.menu.Item} The menu item that was added
20142      */
20143     addMenuItem : function(config){
20144         if(!(config instanceof Roo.menu.Item)){
20145             if(typeof config.checked == "boolean"){ // must be check menu item config?
20146                 config = new Roo.menu.CheckItem(config);
20147             }else{
20148                 config = new Roo.menu.Item(config);
20149             }
20150         }
20151         return this.addItem(config);
20152     },
20153
20154     /**
20155      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20156      * @param {String} text The text to display in the menu item
20157      * @return {Roo.menu.Item} The menu item that was added
20158      */
20159     addText : function(text){
20160         return this.addItem(new Roo.menu.TextItem({ text : text }));
20161     },
20162
20163     /**
20164      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20165      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20166      * @param {Roo.menu.Item} item The menu item to add
20167      * @return {Roo.menu.Item} The menu item that was added
20168      */
20169     insert : function(index, item){
20170         this.items.insert(index, item);
20171         if(this.ul){
20172             var li = document.createElement("li");
20173             li.className = "x-menu-list-item";
20174             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20175             item.render(li, this);
20176             this.delayAutoWidth();
20177         }
20178         return item;
20179     },
20180
20181     /**
20182      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20183      * @param {Roo.menu.Item} item The menu item to remove
20184      */
20185     remove : function(item){
20186         this.items.removeKey(item.id);
20187         item.destroy();
20188     },
20189
20190     /**
20191      * Removes and destroys all items in the menu
20192      */
20193     removeAll : function(){
20194         var f;
20195         while(f = this.items.first()){
20196             this.remove(f);
20197         }
20198     }
20199 });
20200
20201 // MenuNav is a private utility class used internally by the Menu
20202 Roo.menu.MenuNav = function(menu){
20203     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20204     this.scope = this.menu = menu;
20205 };
20206
20207 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20208     doRelay : function(e, h){
20209         var k = e.getKey();
20210         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20211             this.menu.tryActivate(0, 1);
20212             return false;
20213         }
20214         return h.call(this.scope || this, e, this.menu);
20215     },
20216
20217     up : function(e, m){
20218         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20219             m.tryActivate(m.items.length-1, -1);
20220         }
20221     },
20222
20223     down : function(e, m){
20224         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20225             m.tryActivate(0, 1);
20226         }
20227     },
20228
20229     right : function(e, m){
20230         if(m.activeItem){
20231             m.activeItem.expandMenu(true);
20232         }
20233     },
20234
20235     left : function(e, m){
20236         m.hide();
20237         if(m.parentMenu && m.parentMenu.activeItem){
20238             m.parentMenu.activeItem.activate();
20239         }
20240     },
20241
20242     enter : function(e, m){
20243         if(m.activeItem){
20244             e.stopPropagation();
20245             m.activeItem.onClick(e);
20246             m.fireEvent("click", this, m.activeItem);
20247             return true;
20248         }
20249     }
20250 });/*
20251  * Based on:
20252  * Ext JS Library 1.1.1
20253  * Copyright(c) 2006-2007, Ext JS, LLC.
20254  *
20255  * Originally Released Under LGPL - original licence link has changed is not relivant.
20256  *
20257  * Fork - LGPL
20258  * <script type="text/javascript">
20259  */
20260  
20261 /**
20262  * @class Roo.menu.MenuMgr
20263  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20264  * @singleton
20265  */
20266 Roo.menu.MenuMgr = function(){
20267    var menus, active, groups = {}, attached = false, lastShow = new Date();
20268
20269    // private - called when first menu is created
20270    function init(){
20271        menus = {};
20272        active = new Roo.util.MixedCollection();
20273        Roo.get(document).addKeyListener(27, function(){
20274            if(active.length > 0){
20275                hideAll();
20276            }
20277        });
20278    }
20279
20280    // private
20281    function hideAll(){
20282        if(active && active.length > 0){
20283            var c = active.clone();
20284            c.each(function(m){
20285                m.hide();
20286            });
20287        }
20288    }
20289
20290    // private
20291    function onHide(m){
20292        active.remove(m);
20293        if(active.length < 1){
20294            Roo.get(document).un("mousedown", onMouseDown);
20295            attached = false;
20296        }
20297    }
20298
20299    // private
20300    function onShow(m){
20301        var last = active.last();
20302        lastShow = new Date();
20303        active.add(m);
20304        if(!attached){
20305            Roo.get(document).on("mousedown", onMouseDown);
20306            attached = true;
20307        }
20308        if(m.parentMenu){
20309           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20310           m.parentMenu.activeChild = m;
20311        }else if(last && last.isVisible()){
20312           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20313        }
20314    }
20315
20316    // private
20317    function onBeforeHide(m){
20318        if(m.activeChild){
20319            m.activeChild.hide();
20320        }
20321        if(m.autoHideTimer){
20322            clearTimeout(m.autoHideTimer);
20323            delete m.autoHideTimer;
20324        }
20325    }
20326
20327    // private
20328    function onBeforeShow(m){
20329        var pm = m.parentMenu;
20330        if(!pm && !m.allowOtherMenus){
20331            hideAll();
20332        }else if(pm && pm.activeChild && active != m){
20333            pm.activeChild.hide();
20334        }
20335    }
20336
20337    // private
20338    function onMouseDown(e){
20339        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20340            hideAll();
20341        }
20342    }
20343
20344    // private
20345    function onBeforeCheck(mi, state){
20346        if(state){
20347            var g = groups[mi.group];
20348            for(var i = 0, l = g.length; i < l; i++){
20349                if(g[i] != mi){
20350                    g[i].setChecked(false);
20351                }
20352            }
20353        }
20354    }
20355
20356    return {
20357
20358        /**
20359         * Hides all menus that are currently visible
20360         */
20361        hideAll : function(){
20362             hideAll();  
20363        },
20364
20365        // private
20366        register : function(menu){
20367            if(!menus){
20368                init();
20369            }
20370            menus[menu.id] = menu;
20371            menu.on("beforehide", onBeforeHide);
20372            menu.on("hide", onHide);
20373            menu.on("beforeshow", onBeforeShow);
20374            menu.on("show", onShow);
20375            var g = menu.group;
20376            if(g && menu.events["checkchange"]){
20377                if(!groups[g]){
20378                    groups[g] = [];
20379                }
20380                groups[g].push(menu);
20381                menu.on("checkchange", onCheck);
20382            }
20383        },
20384
20385         /**
20386          * Returns a {@link Roo.menu.Menu} object
20387          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20388          * be used to generate and return a new Menu instance.
20389          */
20390        get : function(menu){
20391            if(typeof menu == "string"){ // menu id
20392                return menus[menu];
20393            }else if(menu.events){  // menu instance
20394                return menu;
20395            }else if(typeof menu.length == 'number'){ // array of menu items?
20396                return new Roo.menu.Menu({items:menu});
20397            }else{ // otherwise, must be a config
20398                return new Roo.menu.Menu(menu);
20399            }
20400        },
20401
20402        // private
20403        unregister : function(menu){
20404            delete menus[menu.id];
20405            menu.un("beforehide", onBeforeHide);
20406            menu.un("hide", onHide);
20407            menu.un("beforeshow", onBeforeShow);
20408            menu.un("show", onShow);
20409            var g = menu.group;
20410            if(g && menu.events["checkchange"]){
20411                groups[g].remove(menu);
20412                menu.un("checkchange", onCheck);
20413            }
20414        },
20415
20416        // private
20417        registerCheckable : function(menuItem){
20418            var g = menuItem.group;
20419            if(g){
20420                if(!groups[g]){
20421                    groups[g] = [];
20422                }
20423                groups[g].push(menuItem);
20424                menuItem.on("beforecheckchange", onBeforeCheck);
20425            }
20426        },
20427
20428        // private
20429        unregisterCheckable : function(menuItem){
20430            var g = menuItem.group;
20431            if(g){
20432                groups[g].remove(menuItem);
20433                menuItem.un("beforecheckchange", onBeforeCheck);
20434            }
20435        }
20436    };
20437 }();/*
20438  * Based on:
20439  * Ext JS Library 1.1.1
20440  * Copyright(c) 2006-2007, Ext JS, LLC.
20441  *
20442  * Originally Released Under LGPL - original licence link has changed is not relivant.
20443  *
20444  * Fork - LGPL
20445  * <script type="text/javascript">
20446  */
20447  
20448
20449 /**
20450  * @class Roo.menu.BaseItem
20451  * @extends Roo.Component
20452  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20453  * management and base configuration options shared by all menu components.
20454  * @constructor
20455  * Creates a new BaseItem
20456  * @param {Object} config Configuration options
20457  */
20458 Roo.menu.BaseItem = function(config){
20459     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20460
20461     this.addEvents({
20462         /**
20463          * @event click
20464          * Fires when this item is clicked
20465          * @param {Roo.menu.BaseItem} this
20466          * @param {Roo.EventObject} e
20467          */
20468         click: true,
20469         /**
20470          * @event activate
20471          * Fires when this item is activated
20472          * @param {Roo.menu.BaseItem} this
20473          */
20474         activate : true,
20475         /**
20476          * @event deactivate
20477          * Fires when this item is deactivated
20478          * @param {Roo.menu.BaseItem} this
20479          */
20480         deactivate : true
20481     });
20482
20483     if(this.handler){
20484         this.on("click", this.handler, this.scope, true);
20485     }
20486 };
20487
20488 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20489     /**
20490      * @cfg {Function} handler
20491      * A function that will handle the click event of this menu item (defaults to undefined)
20492      */
20493     /**
20494      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20495      */
20496     canActivate : false,
20497     
20498      /**
20499      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20500      */
20501     hidden: false,
20502     
20503     /**
20504      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20505      */
20506     activeClass : "x-menu-item-active",
20507     /**
20508      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20509      */
20510     hideOnClick : true,
20511     /**
20512      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20513      */
20514     hideDelay : 100,
20515
20516     // private
20517     ctype: "Roo.menu.BaseItem",
20518
20519     // private
20520     actionMode : "container",
20521
20522     // private
20523     render : function(container, parentMenu){
20524         this.parentMenu = parentMenu;
20525         Roo.menu.BaseItem.superclass.render.call(this, container);
20526         this.container.menuItemId = this.id;
20527     },
20528
20529     // private
20530     onRender : function(container, position){
20531         this.el = Roo.get(this.el);
20532         container.dom.appendChild(this.el.dom);
20533     },
20534
20535     // private
20536     onClick : function(e){
20537         if(!this.disabled && this.fireEvent("click", this, e) !== false
20538                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20539             this.handleClick(e);
20540         }else{
20541             e.stopEvent();
20542         }
20543     },
20544
20545     // private
20546     activate : function(){
20547         if(this.disabled){
20548             return false;
20549         }
20550         var li = this.container;
20551         li.addClass(this.activeClass);
20552         this.region = li.getRegion().adjust(2, 2, -2, -2);
20553         this.fireEvent("activate", this);
20554         return true;
20555     },
20556
20557     // private
20558     deactivate : function(){
20559         this.container.removeClass(this.activeClass);
20560         this.fireEvent("deactivate", this);
20561     },
20562
20563     // private
20564     shouldDeactivate : function(e){
20565         return !this.region || !this.region.contains(e.getPoint());
20566     },
20567
20568     // private
20569     handleClick : function(e){
20570         if(this.hideOnClick){
20571             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20572         }
20573     },
20574
20575     // private
20576     expandMenu : function(autoActivate){
20577         // do nothing
20578     },
20579
20580     // private
20581     hideMenu : function(){
20582         // do nothing
20583     }
20584 });/*
20585  * Based on:
20586  * Ext JS Library 1.1.1
20587  * Copyright(c) 2006-2007, Ext JS, LLC.
20588  *
20589  * Originally Released Under LGPL - original licence link has changed is not relivant.
20590  *
20591  * Fork - LGPL
20592  * <script type="text/javascript">
20593  */
20594  
20595 /**
20596  * @class Roo.menu.Adapter
20597  * @extends Roo.menu.BaseItem
20598  * 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.
20599  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20600  * @constructor
20601  * Creates a new Adapter
20602  * @param {Object} config Configuration options
20603  */
20604 Roo.menu.Adapter = function(component, config){
20605     Roo.menu.Adapter.superclass.constructor.call(this, config);
20606     this.component = component;
20607 };
20608 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20609     // private
20610     canActivate : true,
20611
20612     // private
20613     onRender : function(container, position){
20614         this.component.render(container);
20615         this.el = this.component.getEl();
20616     },
20617
20618     // private
20619     activate : function(){
20620         if(this.disabled){
20621             return false;
20622         }
20623         this.component.focus();
20624         this.fireEvent("activate", this);
20625         return true;
20626     },
20627
20628     // private
20629     deactivate : function(){
20630         this.fireEvent("deactivate", this);
20631     },
20632
20633     // private
20634     disable : function(){
20635         this.component.disable();
20636         Roo.menu.Adapter.superclass.disable.call(this);
20637     },
20638
20639     // private
20640     enable : function(){
20641         this.component.enable();
20642         Roo.menu.Adapter.superclass.enable.call(this);
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.TextItem
20657  * @extends Roo.menu.BaseItem
20658  * Adds a static text string to a menu, usually used as either a heading or group separator.
20659  * Note: old style constructor with text is still supported.
20660  * 
20661  * @constructor
20662  * Creates a new TextItem
20663  * @param {Object} cfg Configuration
20664  */
20665 Roo.menu.TextItem = function(cfg){
20666     if (typeof(cfg) == 'string') {
20667         this.text = cfg;
20668     } else {
20669         Roo.apply(this,cfg);
20670     }
20671     
20672     Roo.menu.TextItem.superclass.constructor.call(this);
20673 };
20674
20675 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20676     /**
20677      * @cfg {Boolean} text Text to show on item.
20678      */
20679     text : '',
20680     
20681     /**
20682      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20683      */
20684     hideOnClick : false,
20685     /**
20686      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20687      */
20688     itemCls : "x-menu-text",
20689
20690     // private
20691     onRender : function(){
20692         var s = document.createElement("span");
20693         s.className = this.itemCls;
20694         s.innerHTML = this.text;
20695         this.el = s;
20696         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20697     }
20698 });/*
20699  * Based on:
20700  * Ext JS Library 1.1.1
20701  * Copyright(c) 2006-2007, Ext JS, LLC.
20702  *
20703  * Originally Released Under LGPL - original licence link has changed is not relivant.
20704  *
20705  * Fork - LGPL
20706  * <script type="text/javascript">
20707  */
20708
20709 /**
20710  * @class Roo.menu.Separator
20711  * @extends Roo.menu.BaseItem
20712  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20713  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20714  * @constructor
20715  * @param {Object} config Configuration options
20716  */
20717 Roo.menu.Separator = function(config){
20718     Roo.menu.Separator.superclass.constructor.call(this, config);
20719 };
20720
20721 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20722     /**
20723      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20724      */
20725     itemCls : "x-menu-sep",
20726     /**
20727      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20728      */
20729     hideOnClick : false,
20730
20731     // private
20732     onRender : function(li){
20733         var s = document.createElement("span");
20734         s.className = this.itemCls;
20735         s.innerHTML = "&#160;";
20736         this.el = s;
20737         li.addClass("x-menu-sep-li");
20738         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20739     }
20740 });/*
20741  * Based on:
20742  * Ext JS Library 1.1.1
20743  * Copyright(c) 2006-2007, Ext JS, LLC.
20744  *
20745  * Originally Released Under LGPL - original licence link has changed is not relivant.
20746  *
20747  * Fork - LGPL
20748  * <script type="text/javascript">
20749  */
20750 /**
20751  * @class Roo.menu.Item
20752  * @extends Roo.menu.BaseItem
20753  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20754  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20755  * activation and click handling.
20756  * @constructor
20757  * Creates a new Item
20758  * @param {Object} config Configuration options
20759  */
20760 Roo.menu.Item = function(config){
20761     Roo.menu.Item.superclass.constructor.call(this, config);
20762     if(this.menu){
20763         this.menu = Roo.menu.MenuMgr.get(this.menu);
20764     }
20765 };
20766 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20767     
20768     /**
20769      * @cfg {String} text
20770      * The text to show on the menu item.
20771      */
20772     text: '',
20773      /**
20774      * @cfg {String} HTML to render in menu
20775      * The text to show on the menu item (HTML version).
20776      */
20777     html: '',
20778     /**
20779      * @cfg {String} icon
20780      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20781      */
20782     icon: undefined,
20783     /**
20784      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20785      */
20786     itemCls : "x-menu-item",
20787     /**
20788      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20789      */
20790     canActivate : true,
20791     /**
20792      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20793      */
20794     showDelay: 200,
20795     // doc'd in BaseItem
20796     hideDelay: 200,
20797
20798     // private
20799     ctype: "Roo.menu.Item",
20800     
20801     // private
20802     onRender : function(container, position){
20803         var el = document.createElement("a");
20804         el.hideFocus = true;
20805         el.unselectable = "on";
20806         el.href = this.href || "#";
20807         if(this.hrefTarget){
20808             el.target = this.hrefTarget;
20809         }
20810         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20811         
20812         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20813         
20814         el.innerHTML = String.format(
20815                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20816                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20817         this.el = el;
20818         Roo.menu.Item.superclass.onRender.call(this, container, position);
20819     },
20820
20821     /**
20822      * Sets the text to display in this menu item
20823      * @param {String} text The text to display
20824      * @param {Boolean} isHTML true to indicate text is pure html.
20825      */
20826     setText : function(text, isHTML){
20827         if (isHTML) {
20828             this.html = text;
20829         } else {
20830             this.text = text;
20831             this.html = '';
20832         }
20833         if(this.rendered){
20834             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20835      
20836             this.el.update(String.format(
20837                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20838                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20839             this.parentMenu.autoWidth();
20840         }
20841     },
20842
20843     // private
20844     handleClick : function(e){
20845         if(!this.href){ // if no link defined, stop the event automatically
20846             e.stopEvent();
20847         }
20848         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20849     },
20850
20851     // private
20852     activate : function(autoExpand){
20853         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20854             this.focus();
20855             if(autoExpand){
20856                 this.expandMenu();
20857             }
20858         }
20859         return true;
20860     },
20861
20862     // private
20863     shouldDeactivate : function(e){
20864         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20865             if(this.menu && this.menu.isVisible()){
20866                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20867             }
20868             return true;
20869         }
20870         return false;
20871     },
20872
20873     // private
20874     deactivate : function(){
20875         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20876         this.hideMenu();
20877     },
20878
20879     // private
20880     expandMenu : function(autoActivate){
20881         if(!this.disabled && this.menu){
20882             clearTimeout(this.hideTimer);
20883             delete this.hideTimer;
20884             if(!this.menu.isVisible() && !this.showTimer){
20885                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20886             }else if (this.menu.isVisible() && autoActivate){
20887                 this.menu.tryActivate(0, 1);
20888             }
20889         }
20890     },
20891
20892     // private
20893     deferExpand : function(autoActivate){
20894         delete this.showTimer;
20895         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20896         if(autoActivate){
20897             this.menu.tryActivate(0, 1);
20898         }
20899     },
20900
20901     // private
20902     hideMenu : function(){
20903         clearTimeout(this.showTimer);
20904         delete this.showTimer;
20905         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20906             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20907         }
20908     },
20909
20910     // private
20911     deferHide : function(){
20912         delete this.hideTimer;
20913         this.menu.hide();
20914     }
20915 });/*
20916  * Based on:
20917  * Ext JS Library 1.1.1
20918  * Copyright(c) 2006-2007, Ext JS, LLC.
20919  *
20920  * Originally Released Under LGPL - original licence link has changed is not relivant.
20921  *
20922  * Fork - LGPL
20923  * <script type="text/javascript">
20924  */
20925  
20926 /**
20927  * @class Roo.menu.CheckItem
20928  * @extends Roo.menu.Item
20929  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20930  * @constructor
20931  * Creates a new CheckItem
20932  * @param {Object} config Configuration options
20933  */
20934 Roo.menu.CheckItem = function(config){
20935     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20936     this.addEvents({
20937         /**
20938          * @event beforecheckchange
20939          * Fires before the checked value is set, providing an opportunity to cancel if needed
20940          * @param {Roo.menu.CheckItem} this
20941          * @param {Boolean} checked The new checked value that will be set
20942          */
20943         "beforecheckchange" : true,
20944         /**
20945          * @event checkchange
20946          * Fires after the checked value has been set
20947          * @param {Roo.menu.CheckItem} this
20948          * @param {Boolean} checked The checked value that was set
20949          */
20950         "checkchange" : true
20951     });
20952     if(this.checkHandler){
20953         this.on('checkchange', this.checkHandler, this.scope);
20954     }
20955 };
20956 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20957     /**
20958      * @cfg {String} group
20959      * All check items with the same group name will automatically be grouped into a single-select
20960      * radio button group (defaults to '')
20961      */
20962     /**
20963      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20964      */
20965     itemCls : "x-menu-item x-menu-check-item",
20966     /**
20967      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20968      */
20969     groupClass : "x-menu-group-item",
20970
20971     /**
20972      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20973      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20974      * initialized with checked = true will be rendered as checked.
20975      */
20976     checked: false,
20977
20978     // private
20979     ctype: "Roo.menu.CheckItem",
20980
20981     // private
20982     onRender : function(c){
20983         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20984         if(this.group){
20985             this.el.addClass(this.groupClass);
20986         }
20987         Roo.menu.MenuMgr.registerCheckable(this);
20988         if(this.checked){
20989             this.checked = false;
20990             this.setChecked(true, true);
20991         }
20992     },
20993
20994     // private
20995     destroy : function(){
20996         if(this.rendered){
20997             Roo.menu.MenuMgr.unregisterCheckable(this);
20998         }
20999         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21000     },
21001
21002     /**
21003      * Set the checked state of this item
21004      * @param {Boolean} checked The new checked value
21005      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21006      */
21007     setChecked : function(state, suppressEvent){
21008         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21009             if(this.container){
21010                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21011             }
21012             this.checked = state;
21013             if(suppressEvent !== true){
21014                 this.fireEvent("checkchange", this, state);
21015             }
21016         }
21017     },
21018
21019     // private
21020     handleClick : function(e){
21021        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21022            this.setChecked(!this.checked);
21023        }
21024        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21025     }
21026 });/*
21027  * Based on:
21028  * Ext JS Library 1.1.1
21029  * Copyright(c) 2006-2007, Ext JS, LLC.
21030  *
21031  * Originally Released Under LGPL - original licence link has changed is not relivant.
21032  *
21033  * Fork - LGPL
21034  * <script type="text/javascript">
21035  */
21036  
21037 /**
21038  * @class Roo.menu.DateItem
21039  * @extends Roo.menu.Adapter
21040  * A menu item that wraps the {@link Roo.DatPicker} component.
21041  * @constructor
21042  * Creates a new DateItem
21043  * @param {Object} config Configuration options
21044  */
21045 Roo.menu.DateItem = function(config){
21046     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21047     /** The Roo.DatePicker object @type Roo.DatePicker */
21048     this.picker = this.component;
21049     this.addEvents({select: true});
21050     
21051     this.picker.on("render", function(picker){
21052         picker.getEl().swallowEvent("click");
21053         picker.container.addClass("x-menu-date-item");
21054     });
21055
21056     this.picker.on("select", this.onSelect, this);
21057 };
21058
21059 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21060     // private
21061     onSelect : function(picker, date){
21062         this.fireEvent("select", this, date, picker);
21063         Roo.menu.DateItem.superclass.handleClick.call(this);
21064     }
21065 });/*
21066  * Based on:
21067  * Ext JS Library 1.1.1
21068  * Copyright(c) 2006-2007, Ext JS, LLC.
21069  *
21070  * Originally Released Under LGPL - original licence link has changed is not relivant.
21071  *
21072  * Fork - LGPL
21073  * <script type="text/javascript">
21074  */
21075  
21076 /**
21077  * @class Roo.menu.ColorItem
21078  * @extends Roo.menu.Adapter
21079  * A menu item that wraps the {@link Roo.ColorPalette} component.
21080  * @constructor
21081  * Creates a new ColorItem
21082  * @param {Object} config Configuration options
21083  */
21084 Roo.menu.ColorItem = function(config){
21085     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21086     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21087     this.palette = this.component;
21088     this.relayEvents(this.palette, ["select"]);
21089     if(this.selectHandler){
21090         this.on('select', this.selectHandler, this.scope);
21091     }
21092 };
21093 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21094  * Based on:
21095  * Ext JS Library 1.1.1
21096  * Copyright(c) 2006-2007, Ext JS, LLC.
21097  *
21098  * Originally Released Under LGPL - original licence link has changed is not relivant.
21099  *
21100  * Fork - LGPL
21101  * <script type="text/javascript">
21102  */
21103  
21104
21105 /**
21106  * @class Roo.menu.DateMenu
21107  * @extends Roo.menu.Menu
21108  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21109  * @constructor
21110  * Creates a new DateMenu
21111  * @param {Object} config Configuration options
21112  */
21113 Roo.menu.DateMenu = function(config){
21114     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21115     this.plain = true;
21116     var di = new Roo.menu.DateItem(config);
21117     this.add(di);
21118     /**
21119      * The {@link Roo.DatePicker} instance for this DateMenu
21120      * @type DatePicker
21121      */
21122     this.picker = di.picker;
21123     /**
21124      * @event select
21125      * @param {DatePicker} picker
21126      * @param {Date} date
21127      */
21128     this.relayEvents(di, ["select"]);
21129     this.on('beforeshow', function(){
21130         if(this.picker){
21131             this.picker.hideMonthPicker(false);
21132         }
21133     }, this);
21134 };
21135 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21136     cls:'x-date-menu'
21137 });/*
21138  * Based on:
21139  * Ext JS Library 1.1.1
21140  * Copyright(c) 2006-2007, Ext JS, LLC.
21141  *
21142  * Originally Released Under LGPL - original licence link has changed is not relivant.
21143  *
21144  * Fork - LGPL
21145  * <script type="text/javascript">
21146  */
21147  
21148
21149 /**
21150  * @class Roo.menu.ColorMenu
21151  * @extends Roo.menu.Menu
21152  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21153  * @constructor
21154  * Creates a new ColorMenu
21155  * @param {Object} config Configuration options
21156  */
21157 Roo.menu.ColorMenu = function(config){
21158     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21159     this.plain = true;
21160     var ci = new Roo.menu.ColorItem(config);
21161     this.add(ci);
21162     /**
21163      * The {@link Roo.ColorPalette} instance for this ColorMenu
21164      * @type ColorPalette
21165      */
21166     this.palette = ci.palette;
21167     /**
21168      * @event select
21169      * @param {ColorPalette} palette
21170      * @param {String} color
21171      */
21172     this.relayEvents(ci, ["select"]);
21173 };
21174 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21175  * Based on:
21176  * Ext JS Library 1.1.1
21177  * Copyright(c) 2006-2007, Ext JS, LLC.
21178  *
21179  * Originally Released Under LGPL - original licence link has changed is not relivant.
21180  *
21181  * Fork - LGPL
21182  * <script type="text/javascript">
21183  */
21184  
21185 /**
21186  * @class Roo.form.Field
21187  * @extends Roo.BoxComponent
21188  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21189  * @constructor
21190  * Creates a new Field
21191  * @param {Object} config Configuration options
21192  */
21193 Roo.form.Field = function(config){
21194     Roo.form.Field.superclass.constructor.call(this, config);
21195 };
21196
21197 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21198     /**
21199      * @cfg {String} fieldLabel Label to use when rendering a form.
21200      */
21201        /**
21202      * @cfg {String} qtip Mouse over tip
21203      */
21204      
21205     /**
21206      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21207      */
21208     invalidClass : "x-form-invalid",
21209     /**
21210      * @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")
21211      */
21212     invalidText : "The value in this field is invalid",
21213     /**
21214      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21215      */
21216     focusClass : "x-form-focus",
21217     /**
21218      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21219       automatic validation (defaults to "keyup").
21220      */
21221     validationEvent : "keyup",
21222     /**
21223      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21224      */
21225     validateOnBlur : true,
21226     /**
21227      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21228      */
21229     validationDelay : 250,
21230     /**
21231      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21232      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21233      */
21234     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21235     /**
21236      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21237      */
21238     fieldClass : "x-form-field",
21239     /**
21240      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21241      *<pre>
21242 Value         Description
21243 -----------   ----------------------------------------------------------------------
21244 qtip          Display a quick tip when the user hovers over the field
21245 title         Display a default browser title attribute popup
21246 under         Add a block div beneath the field containing the error text
21247 side          Add an error icon to the right of the field with a popup on hover
21248 [element id]  Add the error text directly to the innerHTML of the specified element
21249 </pre>
21250      */
21251     msgTarget : 'qtip',
21252     /**
21253      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21254      */
21255     msgFx : 'normal',
21256
21257     /**
21258      * @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.
21259      */
21260     readOnly : false,
21261
21262     /**
21263      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21264      */
21265     disabled : false,
21266
21267     /**
21268      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21269      */
21270     inputType : undefined,
21271     
21272     /**
21273      * @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).
21274          */
21275         tabIndex : undefined,
21276         
21277     // private
21278     isFormField : true,
21279
21280     // private
21281     hasFocus : false,
21282     /**
21283      * @property {Roo.Element} fieldEl
21284      * Element Containing the rendered Field (with label etc.)
21285      */
21286     /**
21287      * @cfg {Mixed} value A value to initialize this field with.
21288      */
21289     value : undefined,
21290
21291     /**
21292      * @cfg {String} name The field's HTML name attribute.
21293      */
21294     /**
21295      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21296      */
21297
21298         // private ??
21299         initComponent : function(){
21300         Roo.form.Field.superclass.initComponent.call(this);
21301         this.addEvents({
21302             /**
21303              * @event focus
21304              * Fires when this field receives input focus.
21305              * @param {Roo.form.Field} this
21306              */
21307             focus : true,
21308             /**
21309              * @event blur
21310              * Fires when this field loses input focus.
21311              * @param {Roo.form.Field} this
21312              */
21313             blur : true,
21314             /**
21315              * @event specialkey
21316              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21317              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21318              * @param {Roo.form.Field} this
21319              * @param {Roo.EventObject} e The event object
21320              */
21321             specialkey : true,
21322             /**
21323              * @event change
21324              * Fires just before the field blurs if the field value has changed.
21325              * @param {Roo.form.Field} this
21326              * @param {Mixed} newValue The new value
21327              * @param {Mixed} oldValue The original value
21328              */
21329             change : true,
21330             /**
21331              * @event invalid
21332              * Fires after the field has been marked as invalid.
21333              * @param {Roo.form.Field} this
21334              * @param {String} msg The validation message
21335              */
21336             invalid : true,
21337             /**
21338              * @event valid
21339              * Fires after the field has been validated with no errors.
21340              * @param {Roo.form.Field} this
21341              */
21342             valid : true,
21343              /**
21344              * @event keyup
21345              * Fires after the key up
21346              * @param {Roo.form.Field} this
21347              * @param {Roo.EventObject}  e The event Object
21348              */
21349             keyup : true
21350         });
21351     },
21352
21353     /**
21354      * Returns the name attribute of the field if available
21355      * @return {String} name The field name
21356      */
21357     getName: function(){
21358          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21359     },
21360
21361     // private
21362     onRender : function(ct, position){
21363         Roo.form.Field.superclass.onRender.call(this, ct, position);
21364         if(!this.el){
21365             var cfg = this.getAutoCreate();
21366             if(!cfg.name){
21367                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21368             }
21369             if (!cfg.name.length) {
21370                 delete cfg.name;
21371             }
21372             if(this.inputType){
21373                 cfg.type = this.inputType;
21374             }
21375             this.el = ct.createChild(cfg, position);
21376         }
21377         var type = this.el.dom.type;
21378         if(type){
21379             if(type == 'password'){
21380                 type = 'text';
21381             }
21382             this.el.addClass('x-form-'+type);
21383         }
21384         if(this.readOnly){
21385             this.el.dom.readOnly = true;
21386         }
21387         if(this.tabIndex !== undefined){
21388             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21389         }
21390
21391         this.el.addClass([this.fieldClass, this.cls]);
21392         this.initValue();
21393     },
21394
21395     /**
21396      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21397      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21398      * @return {Roo.form.Field} this
21399      */
21400     applyTo : function(target){
21401         this.allowDomMove = false;
21402         this.el = Roo.get(target);
21403         this.render(this.el.dom.parentNode);
21404         return this;
21405     },
21406
21407     // private
21408     initValue : function(){
21409         if(this.value !== undefined){
21410             this.setValue(this.value);
21411         }else if(this.el.dom.value.length > 0){
21412             this.setValue(this.el.dom.value);
21413         }
21414     },
21415
21416     /**
21417      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21418      */
21419     isDirty : function() {
21420         if(this.disabled) {
21421             return false;
21422         }
21423         return String(this.getValue()) !== String(this.originalValue);
21424     },
21425
21426     // private
21427     afterRender : function(){
21428         Roo.form.Field.superclass.afterRender.call(this);
21429         this.initEvents();
21430     },
21431
21432     // private
21433     fireKey : function(e){
21434         //Roo.log('field ' + e.getKey());
21435         if(e.isNavKeyPress()){
21436             this.fireEvent("specialkey", this, e);
21437         }
21438     },
21439
21440     /**
21441      * Resets the current field value to the originally loaded value and clears any validation messages
21442      */
21443     reset : function(){
21444         this.setValue(this.originalValue);
21445         this.clearInvalid();
21446     },
21447
21448     // private
21449     initEvents : function(){
21450         // safari killled keypress - so keydown is now used..
21451         this.el.on("keydown" , this.fireKey,  this);
21452         this.el.on("focus", this.onFocus,  this);
21453         this.el.on("blur", this.onBlur,  this);
21454         this.el.relayEvent('keyup', this);
21455
21456         // reference to original value for reset
21457         this.originalValue = this.getValue();
21458     },
21459
21460     // private
21461     onFocus : function(){
21462         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21463             this.el.addClass(this.focusClass);
21464         }
21465         if(!this.hasFocus){
21466             this.hasFocus = true;
21467             this.startValue = this.getValue();
21468             this.fireEvent("focus", this);
21469         }
21470     },
21471
21472     beforeBlur : Roo.emptyFn,
21473
21474     // private
21475     onBlur : function(){
21476         this.beforeBlur();
21477         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21478             this.el.removeClass(this.focusClass);
21479         }
21480         this.hasFocus = false;
21481         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21482             this.validate();
21483         }
21484         var v = this.getValue();
21485         if(String(v) !== String(this.startValue)){
21486             this.fireEvent('change', this, v, this.startValue);
21487         }
21488         this.fireEvent("blur", this);
21489     },
21490
21491     /**
21492      * Returns whether or not the field value is currently valid
21493      * @param {Boolean} preventMark True to disable marking the field invalid
21494      * @return {Boolean} True if the value is valid, else false
21495      */
21496     isValid : function(preventMark){
21497         if(this.disabled){
21498             return true;
21499         }
21500         var restore = this.preventMark;
21501         this.preventMark = preventMark === true;
21502         var v = this.validateValue(this.processValue(this.getRawValue()));
21503         this.preventMark = restore;
21504         return v;
21505     },
21506
21507     /**
21508      * Validates the field value
21509      * @return {Boolean} True if the value is valid, else false
21510      */
21511     validate : function(){
21512         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21513             this.clearInvalid();
21514             return true;
21515         }
21516         return false;
21517     },
21518
21519     processValue : function(value){
21520         return value;
21521     },
21522
21523     // private
21524     // Subclasses should provide the validation implementation by overriding this
21525     validateValue : function(value){
21526         return true;
21527     },
21528
21529     /**
21530      * Mark this field as invalid
21531      * @param {String} msg The validation message
21532      */
21533     markInvalid : function(msg){
21534         if(!this.rendered || this.preventMark){ // not rendered
21535             return;
21536         }
21537         this.el.addClass(this.invalidClass);
21538         msg = msg || this.invalidText;
21539         switch(this.msgTarget){
21540             case 'qtip':
21541                 this.el.dom.qtip = msg;
21542                 this.el.dom.qclass = 'x-form-invalid-tip';
21543                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21544                     Roo.QuickTips.enable();
21545                 }
21546                 break;
21547             case 'title':
21548                 this.el.dom.title = msg;
21549                 break;
21550             case 'under':
21551                 if(!this.errorEl){
21552                     var elp = this.el.findParent('.x-form-element', 5, true);
21553                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21554                     this.errorEl.setWidth(elp.getWidth(true)-20);
21555                 }
21556                 this.errorEl.update(msg);
21557                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21558                 break;
21559             case 'side':
21560                 if(!this.errorIcon){
21561                     var elp = this.el.findParent('.x-form-element', 5, true);
21562                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21563                 }
21564                 this.alignErrorIcon();
21565                 this.errorIcon.dom.qtip = msg;
21566                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21567                 this.errorIcon.show();
21568                 this.on('resize', this.alignErrorIcon, this);
21569                 break;
21570             default:
21571                 var t = Roo.getDom(this.msgTarget);
21572                 t.innerHTML = msg;
21573                 t.style.display = this.msgDisplay;
21574                 break;
21575         }
21576         this.fireEvent('invalid', this, msg);
21577     },
21578
21579     // private
21580     alignErrorIcon : function(){
21581         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21582     },
21583
21584     /**
21585      * Clear any invalid styles/messages for this field
21586      */
21587     clearInvalid : function(){
21588         if(!this.rendered || this.preventMark){ // not rendered
21589             return;
21590         }
21591         this.el.removeClass(this.invalidClass);
21592         switch(this.msgTarget){
21593             case 'qtip':
21594                 this.el.dom.qtip = '';
21595                 break;
21596             case 'title':
21597                 this.el.dom.title = '';
21598                 break;
21599             case 'under':
21600                 if(this.errorEl){
21601                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21602                 }
21603                 break;
21604             case 'side':
21605                 if(this.errorIcon){
21606                     this.errorIcon.dom.qtip = '';
21607                     this.errorIcon.hide();
21608                     this.un('resize', this.alignErrorIcon, this);
21609                 }
21610                 break;
21611             default:
21612                 var t = Roo.getDom(this.msgTarget);
21613                 t.innerHTML = '';
21614                 t.style.display = 'none';
21615                 break;
21616         }
21617         this.fireEvent('valid', this);
21618     },
21619
21620     /**
21621      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21622      * @return {Mixed} value The field value
21623      */
21624     getRawValue : function(){
21625         var v = this.el.getValue();
21626         
21627         return v;
21628     },
21629
21630     /**
21631      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21632      * @return {Mixed} value The field value
21633      */
21634     getValue : function(){
21635         var v = this.el.getValue();
21636          
21637         return v;
21638     },
21639
21640     /**
21641      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21642      * @param {Mixed} value The value to set
21643      */
21644     setRawValue : function(v){
21645         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21646     },
21647
21648     /**
21649      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21650      * @param {Mixed} value The value to set
21651      */
21652     setValue : function(v){
21653         this.value = v;
21654         if(this.rendered){
21655             this.el.dom.value = (v === null || v === undefined ? '' : v);
21656              this.validate();
21657         }
21658     },
21659
21660     adjustSize : function(w, h){
21661         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21662         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21663         return s;
21664     },
21665
21666     adjustWidth : function(tag, w){
21667         tag = tag.toLowerCase();
21668         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21669             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21670                 if(tag == 'input'){
21671                     return w + 2;
21672                 }
21673                 if(tag = 'textarea'){
21674                     return w-2;
21675                 }
21676             }else if(Roo.isOpera){
21677                 if(tag == 'input'){
21678                     return w + 2;
21679                 }
21680                 if(tag = 'textarea'){
21681                     return w-2;
21682                 }
21683             }
21684         }
21685         return w;
21686     }
21687 });
21688
21689
21690 // anything other than normal should be considered experimental
21691 Roo.form.Field.msgFx = {
21692     normal : {
21693         show: function(msgEl, f){
21694             msgEl.setDisplayed('block');
21695         },
21696
21697         hide : function(msgEl, f){
21698             msgEl.setDisplayed(false).update('');
21699         }
21700     },
21701
21702     slide : {
21703         show: function(msgEl, f){
21704             msgEl.slideIn('t', {stopFx:true});
21705         },
21706
21707         hide : function(msgEl, f){
21708             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21709         }
21710     },
21711
21712     slideRight : {
21713         show: function(msgEl, f){
21714             msgEl.fixDisplay();
21715             msgEl.alignTo(f.el, 'tl-tr');
21716             msgEl.slideIn('l', {stopFx:true});
21717         },
21718
21719         hide : function(msgEl, f){
21720             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21721         }
21722     }
21723 };/*
21724  * Based on:
21725  * Ext JS Library 1.1.1
21726  * Copyright(c) 2006-2007, Ext JS, LLC.
21727  *
21728  * Originally Released Under LGPL - original licence link has changed is not relivant.
21729  *
21730  * Fork - LGPL
21731  * <script type="text/javascript">
21732  */
21733  
21734
21735 /**
21736  * @class Roo.form.TextField
21737  * @extends Roo.form.Field
21738  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21739  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21740  * @constructor
21741  * Creates a new TextField
21742  * @param {Object} config Configuration options
21743  */
21744 Roo.form.TextField = function(config){
21745     Roo.form.TextField.superclass.constructor.call(this, config);
21746     this.addEvents({
21747         /**
21748          * @event autosize
21749          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21750          * according to the default logic, but this event provides a hook for the developer to apply additional
21751          * logic at runtime to resize the field if needed.
21752              * @param {Roo.form.Field} this This text field
21753              * @param {Number} width The new field width
21754              */
21755         autosize : true
21756     });
21757 };
21758
21759 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21760     /**
21761      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21762      */
21763     grow : false,
21764     /**
21765      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21766      */
21767     growMin : 30,
21768     /**
21769      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21770      */
21771     growMax : 800,
21772     /**
21773      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21774      */
21775     vtype : null,
21776     /**
21777      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21778      */
21779     maskRe : null,
21780     /**
21781      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21782      */
21783     disableKeyFilter : false,
21784     /**
21785      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21786      */
21787     allowBlank : true,
21788     /**
21789      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21790      */
21791     minLength : 0,
21792     /**
21793      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21794      */
21795     maxLength : Number.MAX_VALUE,
21796     /**
21797      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21798      */
21799     minLengthText : "The minimum length for this field is {0}",
21800     /**
21801      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21802      */
21803     maxLengthText : "The maximum length for this field is {0}",
21804     /**
21805      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21806      */
21807     selectOnFocus : false,
21808     /**
21809      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21810      */
21811     blankText : "This field is required",
21812     /**
21813      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21814      * If available, this function will be called only after the basic validators all return true, and will be passed the
21815      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21816      */
21817     validator : null,
21818     /**
21819      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21820      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21821      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21822      */
21823     regex : null,
21824     /**
21825      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21826      */
21827     regexText : "",
21828     /**
21829      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21830      */
21831     emptyText : null,
21832    
21833
21834     // private
21835     initEvents : function()
21836     {
21837         if (this.emptyText) {
21838             this.el.attr('placeholder', this.emptyText);
21839         }
21840         
21841         Roo.form.TextField.superclass.initEvents.call(this);
21842         if(this.validationEvent == 'keyup'){
21843             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21844             this.el.on('keyup', this.filterValidation, this);
21845         }
21846         else if(this.validationEvent !== false){
21847             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21848         }
21849         
21850         if(this.selectOnFocus){
21851             this.on("focus", this.preFocus, this);
21852             
21853         }
21854         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21855             this.el.on("keypress", this.filterKeys, this);
21856         }
21857         if(this.grow){
21858             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21859             this.el.on("click", this.autoSize,  this);
21860         }
21861         if(this.el.is('input[type=password]') && Roo.isSafari){
21862             this.el.on('keydown', this.SafariOnKeyDown, this);
21863         }
21864     },
21865
21866     processValue : function(value){
21867         if(this.stripCharsRe){
21868             var newValue = value.replace(this.stripCharsRe, '');
21869             if(newValue !== value){
21870                 this.setRawValue(newValue);
21871                 return newValue;
21872             }
21873         }
21874         return value;
21875     },
21876
21877     filterValidation : function(e){
21878         if(!e.isNavKeyPress()){
21879             this.validationTask.delay(this.validationDelay);
21880         }
21881     },
21882
21883     // private
21884     onKeyUp : function(e){
21885         if(!e.isNavKeyPress()){
21886             this.autoSize();
21887         }
21888     },
21889
21890     /**
21891      * Resets the current field value to the originally-loaded value and clears any validation messages.
21892      *  
21893      */
21894     reset : function(){
21895         Roo.form.TextField.superclass.reset.call(this);
21896        
21897     },
21898
21899     
21900     // private
21901     preFocus : function(){
21902         
21903         if(this.selectOnFocus){
21904             this.el.dom.select();
21905         }
21906     },
21907
21908     
21909     // private
21910     filterKeys : function(e){
21911         var k = e.getKey();
21912         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21913             return;
21914         }
21915         var c = e.getCharCode(), cc = String.fromCharCode(c);
21916         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21917             return;
21918         }
21919         if(!this.maskRe.test(cc)){
21920             e.stopEvent();
21921         }
21922     },
21923
21924     setValue : function(v){
21925         
21926         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21927         
21928         this.autoSize();
21929     },
21930
21931     /**
21932      * Validates a value according to the field's validation rules and marks the field as invalid
21933      * if the validation fails
21934      * @param {Mixed} value The value to validate
21935      * @return {Boolean} True if the value is valid, else false
21936      */
21937     validateValue : function(value){
21938         if(value.length < 1)  { // if it's blank
21939              if(this.allowBlank){
21940                 this.clearInvalid();
21941                 return true;
21942              }else{
21943                 this.markInvalid(this.blankText);
21944                 return false;
21945              }
21946         }
21947         if(value.length < this.minLength){
21948             this.markInvalid(String.format(this.minLengthText, this.minLength));
21949             return false;
21950         }
21951         if(value.length > this.maxLength){
21952             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21953             return false;
21954         }
21955         if(this.vtype){
21956             var vt = Roo.form.VTypes;
21957             if(!vt[this.vtype](value, this)){
21958                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21959                 return false;
21960             }
21961         }
21962         if(typeof this.validator == "function"){
21963             var msg = this.validator(value);
21964             if(msg !== true){
21965                 this.markInvalid(msg);
21966                 return false;
21967             }
21968         }
21969         if(this.regex && !this.regex.test(value)){
21970             this.markInvalid(this.regexText);
21971             return false;
21972         }
21973         return true;
21974     },
21975
21976     /**
21977      * Selects text in this field
21978      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21979      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21980      */
21981     selectText : function(start, end){
21982         var v = this.getRawValue();
21983         if(v.length > 0){
21984             start = start === undefined ? 0 : start;
21985             end = end === undefined ? v.length : end;
21986             var d = this.el.dom;
21987             if(d.setSelectionRange){
21988                 d.setSelectionRange(start, end);
21989             }else if(d.createTextRange){
21990                 var range = d.createTextRange();
21991                 range.moveStart("character", start);
21992                 range.moveEnd("character", v.length-end);
21993                 range.select();
21994             }
21995         }
21996     },
21997
21998     /**
21999      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22000      * This only takes effect if grow = true, and fires the autosize event.
22001      */
22002     autoSize : function(){
22003         if(!this.grow || !this.rendered){
22004             return;
22005         }
22006         if(!this.metrics){
22007             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22008         }
22009         var el = this.el;
22010         var v = el.dom.value;
22011         var d = document.createElement('div');
22012         d.appendChild(document.createTextNode(v));
22013         v = d.innerHTML;
22014         d = null;
22015         v += "&#160;";
22016         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22017         this.el.setWidth(w);
22018         this.fireEvent("autosize", this, w);
22019     },
22020     
22021     // private
22022     SafariOnKeyDown : function(event)
22023     {
22024         // this is a workaround for a password hang bug on chrome/ webkit.
22025         
22026         var isSelectAll = false;
22027         
22028         if(this.el.dom.selectionEnd > 0){
22029             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22030         }
22031         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22032             event.preventDefault();
22033             this.setValue('');
22034             return;
22035         }
22036         
22037         if(isSelectAll){ // backspace and delete key
22038             
22039             event.preventDefault();
22040             // this is very hacky as keydown always get's upper case.
22041             //
22042             var cc = String.fromCharCode(event.getCharCode());
22043             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22044             
22045         }
22046         
22047         
22048     }
22049 });/*
22050  * Based on:
22051  * Ext JS Library 1.1.1
22052  * Copyright(c) 2006-2007, Ext JS, LLC.
22053  *
22054  * Originally Released Under LGPL - original licence link has changed is not relivant.
22055  *
22056  * Fork - LGPL
22057  * <script type="text/javascript">
22058  */
22059  
22060 /**
22061  * @class Roo.form.Hidden
22062  * @extends Roo.form.TextField
22063  * Simple Hidden element used on forms 
22064  * 
22065  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22066  * 
22067  * @constructor
22068  * Creates a new Hidden form element.
22069  * @param {Object} config Configuration options
22070  */
22071
22072
22073
22074 // easy hidden field...
22075 Roo.form.Hidden = function(config){
22076     Roo.form.Hidden.superclass.constructor.call(this, config);
22077 };
22078   
22079 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22080     fieldLabel:      '',
22081     inputType:      'hidden',
22082     width:          50,
22083     allowBlank:     true,
22084     labelSeparator: '',
22085     hidden:         true,
22086     itemCls :       'x-form-item-display-none'
22087
22088
22089 });
22090
22091
22092 /*
22093  * Based on:
22094  * Ext JS Library 1.1.1
22095  * Copyright(c) 2006-2007, Ext JS, LLC.
22096  *
22097  * Originally Released Under LGPL - original licence link has changed is not relivant.
22098  *
22099  * Fork - LGPL
22100  * <script type="text/javascript">
22101  */
22102  
22103 /**
22104  * @class Roo.form.TriggerField
22105  * @extends Roo.form.TextField
22106  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22107  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22108  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22109  * for which you can provide a custom implementation.  For example:
22110  * <pre><code>
22111 var trigger = new Roo.form.TriggerField();
22112 trigger.onTriggerClick = myTriggerFn;
22113 trigger.applyTo('my-field');
22114 </code></pre>
22115  *
22116  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22117  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22118  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22119  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22120  * @constructor
22121  * Create a new TriggerField.
22122  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22123  * to the base TextField)
22124  */
22125 Roo.form.TriggerField = function(config){
22126     this.mimicing = false;
22127     Roo.form.TriggerField.superclass.constructor.call(this, config);
22128 };
22129
22130 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22131     /**
22132      * @cfg {String} triggerClass A CSS class to apply to the trigger
22133      */
22134     /**
22135      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22136      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22137      */
22138     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22139     /**
22140      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22141      */
22142     hideTrigger:false,
22143
22144     /** @cfg {Boolean} grow @hide */
22145     /** @cfg {Number} growMin @hide */
22146     /** @cfg {Number} growMax @hide */
22147
22148     /**
22149      * @hide 
22150      * @method
22151      */
22152     autoSize: Roo.emptyFn,
22153     // private
22154     monitorTab : true,
22155     // private
22156     deferHeight : true,
22157
22158     
22159     actionMode : 'wrap',
22160     // private
22161     onResize : function(w, h){
22162         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22163         if(typeof w == 'number'){
22164             var x = w - this.trigger.getWidth();
22165             this.el.setWidth(this.adjustWidth('input', x));
22166             this.trigger.setStyle('left', x+'px');
22167         }
22168     },
22169
22170     // private
22171     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22172
22173     // private
22174     getResizeEl : function(){
22175         return this.wrap;
22176     },
22177
22178     // private
22179     getPositionEl : function(){
22180         return this.wrap;
22181     },
22182
22183     // private
22184     alignErrorIcon : function(){
22185         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22186     },
22187
22188     // private
22189     onRender : function(ct, position){
22190         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22191         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22192         this.trigger = this.wrap.createChild(this.triggerConfig ||
22193                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22194         if(this.hideTrigger){
22195             this.trigger.setDisplayed(false);
22196         }
22197         this.initTrigger();
22198         if(!this.width){
22199             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22200         }
22201     },
22202
22203     // private
22204     initTrigger : function(){
22205         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22206         this.trigger.addClassOnOver('x-form-trigger-over');
22207         this.trigger.addClassOnClick('x-form-trigger-click');
22208     },
22209
22210     // private
22211     onDestroy : function(){
22212         if(this.trigger){
22213             this.trigger.removeAllListeners();
22214             this.trigger.remove();
22215         }
22216         if(this.wrap){
22217             this.wrap.remove();
22218         }
22219         Roo.form.TriggerField.superclass.onDestroy.call(this);
22220     },
22221
22222     // private
22223     onFocus : function(){
22224         Roo.form.TriggerField.superclass.onFocus.call(this);
22225         if(!this.mimicing){
22226             this.wrap.addClass('x-trigger-wrap-focus');
22227             this.mimicing = true;
22228             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22229             if(this.monitorTab){
22230                 this.el.on("keydown", this.checkTab, this);
22231             }
22232         }
22233     },
22234
22235     // private
22236     checkTab : function(e){
22237         if(e.getKey() == e.TAB){
22238             this.triggerBlur();
22239         }
22240     },
22241
22242     // private
22243     onBlur : function(){
22244         // do nothing
22245     },
22246
22247     // private
22248     mimicBlur : function(e, t){
22249         if(!this.wrap.contains(t) && this.validateBlur()){
22250             this.triggerBlur();
22251         }
22252     },
22253
22254     // private
22255     triggerBlur : function(){
22256         this.mimicing = false;
22257         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22258         if(this.monitorTab){
22259             this.el.un("keydown", this.checkTab, this);
22260         }
22261         this.wrap.removeClass('x-trigger-wrap-focus');
22262         Roo.form.TriggerField.superclass.onBlur.call(this);
22263     },
22264
22265     // private
22266     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22267     validateBlur : function(e, t){
22268         return true;
22269     },
22270
22271     // private
22272     onDisable : function(){
22273         Roo.form.TriggerField.superclass.onDisable.call(this);
22274         if(this.wrap){
22275             this.wrap.addClass('x-item-disabled');
22276         }
22277     },
22278
22279     // private
22280     onEnable : function(){
22281         Roo.form.TriggerField.superclass.onEnable.call(this);
22282         if(this.wrap){
22283             this.wrap.removeClass('x-item-disabled');
22284         }
22285     },
22286
22287     // private
22288     onShow : function(){
22289         var ae = this.getActionEl();
22290         
22291         if(ae){
22292             ae.dom.style.display = '';
22293             ae.dom.style.visibility = 'visible';
22294         }
22295     },
22296
22297     // private
22298     
22299     onHide : function(){
22300         var ae = this.getActionEl();
22301         ae.dom.style.display = 'none';
22302     },
22303
22304     /**
22305      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22306      * by an implementing function.
22307      * @method
22308      * @param {EventObject} e
22309      */
22310     onTriggerClick : Roo.emptyFn
22311 });
22312
22313 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22314 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22315 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22316 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22317     initComponent : function(){
22318         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22319
22320         this.triggerConfig = {
22321             tag:'span', cls:'x-form-twin-triggers', cn:[
22322             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22323             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22324         ]};
22325     },
22326
22327     getTrigger : function(index){
22328         return this.triggers[index];
22329     },
22330
22331     initTrigger : function(){
22332         var ts = this.trigger.select('.x-form-trigger', true);
22333         this.wrap.setStyle('overflow', 'hidden');
22334         var triggerField = this;
22335         ts.each(function(t, all, index){
22336             t.hide = function(){
22337                 var w = triggerField.wrap.getWidth();
22338                 this.dom.style.display = 'none';
22339                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22340             };
22341             t.show = function(){
22342                 var w = triggerField.wrap.getWidth();
22343                 this.dom.style.display = '';
22344                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22345             };
22346             var triggerIndex = 'Trigger'+(index+1);
22347
22348             if(this['hide'+triggerIndex]){
22349                 t.dom.style.display = 'none';
22350             }
22351             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22352             t.addClassOnOver('x-form-trigger-over');
22353             t.addClassOnClick('x-form-trigger-click');
22354         }, this);
22355         this.triggers = ts.elements;
22356     },
22357
22358     onTrigger1Click : Roo.emptyFn,
22359     onTrigger2Click : Roo.emptyFn
22360 });/*
22361  * Based on:
22362  * Ext JS Library 1.1.1
22363  * Copyright(c) 2006-2007, Ext JS, LLC.
22364  *
22365  * Originally Released Under LGPL - original licence link has changed is not relivant.
22366  *
22367  * Fork - LGPL
22368  * <script type="text/javascript">
22369  */
22370  
22371 /**
22372  * @class Roo.form.TextArea
22373  * @extends Roo.form.TextField
22374  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22375  * support for auto-sizing.
22376  * @constructor
22377  * Creates a new TextArea
22378  * @param {Object} config Configuration options
22379  */
22380 Roo.form.TextArea = function(config){
22381     Roo.form.TextArea.superclass.constructor.call(this, config);
22382     // these are provided exchanges for backwards compat
22383     // minHeight/maxHeight were replaced by growMin/growMax to be
22384     // compatible with TextField growing config values
22385     if(this.minHeight !== undefined){
22386         this.growMin = this.minHeight;
22387     }
22388     if(this.maxHeight !== undefined){
22389         this.growMax = this.maxHeight;
22390     }
22391 };
22392
22393 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22394     /**
22395      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22396      */
22397     growMin : 60,
22398     /**
22399      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22400      */
22401     growMax: 1000,
22402     /**
22403      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22404      * in the field (equivalent to setting overflow: hidden, defaults to false)
22405      */
22406     preventScrollbars: false,
22407     /**
22408      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22409      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22410      */
22411
22412     // private
22413     onRender : function(ct, position){
22414         if(!this.el){
22415             this.defaultAutoCreate = {
22416                 tag: "textarea",
22417                 style:"width:300px;height:60px;",
22418                 autocomplete: "off"
22419             };
22420         }
22421         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22422         if(this.grow){
22423             this.textSizeEl = Roo.DomHelper.append(document.body, {
22424                 tag: "pre", cls: "x-form-grow-sizer"
22425             });
22426             if(this.preventScrollbars){
22427                 this.el.setStyle("overflow", "hidden");
22428             }
22429             this.el.setHeight(this.growMin);
22430         }
22431     },
22432
22433     onDestroy : function(){
22434         if(this.textSizeEl){
22435             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22436         }
22437         Roo.form.TextArea.superclass.onDestroy.call(this);
22438     },
22439
22440     // private
22441     onKeyUp : function(e){
22442         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22443             this.autoSize();
22444         }
22445     },
22446
22447     /**
22448      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22449      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22450      */
22451     autoSize : function(){
22452         if(!this.grow || !this.textSizeEl){
22453             return;
22454         }
22455         var el = this.el;
22456         var v = el.dom.value;
22457         var ts = this.textSizeEl;
22458
22459         ts.innerHTML = '';
22460         ts.appendChild(document.createTextNode(v));
22461         v = ts.innerHTML;
22462
22463         Roo.fly(ts).setWidth(this.el.getWidth());
22464         if(v.length < 1){
22465             v = "&#160;&#160;";
22466         }else{
22467             if(Roo.isIE){
22468                 v = v.replace(/\n/g, '<p>&#160;</p>');
22469             }
22470             v += "&#160;\n&#160;";
22471         }
22472         ts.innerHTML = v;
22473         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22474         if(h != this.lastHeight){
22475             this.lastHeight = h;
22476             this.el.setHeight(h);
22477             this.fireEvent("autosize", this, h);
22478         }
22479     }
22480 });/*
22481  * Based on:
22482  * Ext JS Library 1.1.1
22483  * Copyright(c) 2006-2007, Ext JS, LLC.
22484  *
22485  * Originally Released Under LGPL - original licence link has changed is not relivant.
22486  *
22487  * Fork - LGPL
22488  * <script type="text/javascript">
22489  */
22490  
22491
22492 /**
22493  * @class Roo.form.NumberField
22494  * @extends Roo.form.TextField
22495  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22496  * @constructor
22497  * Creates a new NumberField
22498  * @param {Object} config Configuration options
22499  */
22500 Roo.form.NumberField = function(config){
22501     Roo.form.NumberField.superclass.constructor.call(this, config);
22502 };
22503
22504 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22505     /**
22506      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22507      */
22508     fieldClass: "x-form-field x-form-num-field",
22509     /**
22510      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22511      */
22512     allowDecimals : true,
22513     /**
22514      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22515      */
22516     decimalSeparator : ".",
22517     /**
22518      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22519      */
22520     decimalPrecision : 2,
22521     /**
22522      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22523      */
22524     allowNegative : true,
22525     /**
22526      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22527      */
22528     minValue : Number.NEGATIVE_INFINITY,
22529     /**
22530      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22531      */
22532     maxValue : Number.MAX_VALUE,
22533     /**
22534      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22535      */
22536     minText : "The minimum value for this field is {0}",
22537     /**
22538      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22539      */
22540     maxText : "The maximum value for this field is {0}",
22541     /**
22542      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22543      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22544      */
22545     nanText : "{0} is not a valid number",
22546
22547     // private
22548     initEvents : function(){
22549         Roo.form.NumberField.superclass.initEvents.call(this);
22550         var allowed = "0123456789";
22551         if(this.allowDecimals){
22552             allowed += this.decimalSeparator;
22553         }
22554         if(this.allowNegative){
22555             allowed += "-";
22556         }
22557         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22558         var keyPress = function(e){
22559             var k = e.getKey();
22560             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22561                 return;
22562             }
22563             var c = e.getCharCode();
22564             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22565                 e.stopEvent();
22566             }
22567         };
22568         this.el.on("keypress", keyPress, this);
22569     },
22570
22571     // private
22572     validateValue : function(value){
22573         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22574             return false;
22575         }
22576         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22577              return true;
22578         }
22579         var num = this.parseValue(value);
22580         if(isNaN(num)){
22581             this.markInvalid(String.format(this.nanText, value));
22582             return false;
22583         }
22584         if(num < this.minValue){
22585             this.markInvalid(String.format(this.minText, this.minValue));
22586             return false;
22587         }
22588         if(num > this.maxValue){
22589             this.markInvalid(String.format(this.maxText, this.maxValue));
22590             return false;
22591         }
22592         return true;
22593     },
22594
22595     getValue : function(){
22596         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22597     },
22598
22599     // private
22600     parseValue : function(value){
22601         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22602         return isNaN(value) ? '' : value;
22603     },
22604
22605     // private
22606     fixPrecision : function(value){
22607         var nan = isNaN(value);
22608         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22609             return nan ? '' : value;
22610         }
22611         return parseFloat(value).toFixed(this.decimalPrecision);
22612     },
22613
22614     setValue : function(v){
22615         v = this.fixPrecision(v);
22616         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22617     },
22618
22619     // private
22620     decimalPrecisionFcn : function(v){
22621         return Math.floor(v);
22622     },
22623
22624     beforeBlur : function(){
22625         var v = this.parseValue(this.getRawValue());
22626         if(v){
22627             this.setValue(v);
22628         }
22629     }
22630 });/*
22631  * Based on:
22632  * Ext JS Library 1.1.1
22633  * Copyright(c) 2006-2007, Ext JS, LLC.
22634  *
22635  * Originally Released Under LGPL - original licence link has changed is not relivant.
22636  *
22637  * Fork - LGPL
22638  * <script type="text/javascript">
22639  */
22640  
22641 /**
22642  * @class Roo.form.DateField
22643  * @extends Roo.form.TriggerField
22644  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22645 * @constructor
22646 * Create a new DateField
22647 * @param {Object} config
22648  */
22649 Roo.form.DateField = function(config){
22650     Roo.form.DateField.superclass.constructor.call(this, config);
22651     
22652       this.addEvents({
22653          
22654         /**
22655          * @event select
22656          * Fires when a date is selected
22657              * @param {Roo.form.DateField} combo This combo box
22658              * @param {Date} date The date selected
22659              */
22660         'select' : true
22661          
22662     });
22663     
22664     
22665     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22666     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22667     this.ddMatch = null;
22668     if(this.disabledDates){
22669         var dd = this.disabledDates;
22670         var re = "(?:";
22671         for(var i = 0; i < dd.length; i++){
22672             re += dd[i];
22673             if(i != dd.length-1) re += "|";
22674         }
22675         this.ddMatch = new RegExp(re + ")");
22676     }
22677 };
22678
22679 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22680     /**
22681      * @cfg {String} format
22682      * The default date format string which can be overriden for localization support.  The format must be
22683      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22684      */
22685     format : "m/d/y",
22686     /**
22687      * @cfg {String} altFormats
22688      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22689      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22690      */
22691     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22692     /**
22693      * @cfg {Array} disabledDays
22694      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22695      */
22696     disabledDays : null,
22697     /**
22698      * @cfg {String} disabledDaysText
22699      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22700      */
22701     disabledDaysText : "Disabled",
22702     /**
22703      * @cfg {Array} disabledDates
22704      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22705      * expression so they are very powerful. Some examples:
22706      * <ul>
22707      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22708      * <li>["03/08", "09/16"] would disable those days for every year</li>
22709      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22710      * <li>["03/../2006"] would disable every day in March 2006</li>
22711      * <li>["^03"] would disable every day in every March</li>
22712      * </ul>
22713      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22714      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22715      */
22716     disabledDates : null,
22717     /**
22718      * @cfg {String} disabledDatesText
22719      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22720      */
22721     disabledDatesText : "Disabled",
22722     /**
22723      * @cfg {Date/String} minValue
22724      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22725      * valid format (defaults to null).
22726      */
22727     minValue : null,
22728     /**
22729      * @cfg {Date/String} maxValue
22730      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22731      * valid format (defaults to null).
22732      */
22733     maxValue : null,
22734     /**
22735      * @cfg {String} minText
22736      * The error text to display when the date in the cell is before minValue (defaults to
22737      * 'The date in this field must be after {minValue}').
22738      */
22739     minText : "The date in this field must be equal to or after {0}",
22740     /**
22741      * @cfg {String} maxText
22742      * The error text to display when the date in the cell is after maxValue (defaults to
22743      * 'The date in this field must be before {maxValue}').
22744      */
22745     maxText : "The date in this field must be equal to or before {0}",
22746     /**
22747      * @cfg {String} invalidText
22748      * The error text to display when the date in the field is invalid (defaults to
22749      * '{value} is not a valid date - it must be in the format {format}').
22750      */
22751     invalidText : "{0} is not a valid date - it must be in the format {1}",
22752     /**
22753      * @cfg {String} triggerClass
22754      * An additional CSS class used to style the trigger button.  The trigger will always get the
22755      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22756      * which displays a calendar icon).
22757      */
22758     triggerClass : 'x-form-date-trigger',
22759     
22760
22761     /**
22762      * @cfg {Boolean} useIso
22763      * if enabled, then the date field will use a hidden field to store the 
22764      * real value as iso formated date. default (false)
22765      */ 
22766     useIso : false,
22767     /**
22768      * @cfg {String/Object} autoCreate
22769      * A DomHelper element spec, or true for a default element spec (defaults to
22770      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22771      */ 
22772     // private
22773     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22774     
22775     // private
22776     hiddenField: false,
22777     
22778     onRender : function(ct, position)
22779     {
22780         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22781         if (this.useIso) {
22782             //this.el.dom.removeAttribute('name'); 
22783             Roo.log("Changing name?");
22784             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22785             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22786                     'before', true);
22787             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22788             // prevent input submission
22789             this.hiddenName = this.name;
22790         }
22791             
22792             
22793     },
22794     
22795     // private
22796     validateValue : function(value)
22797     {
22798         value = this.formatDate(value);
22799         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22800             Roo.log('super failed');
22801             return false;
22802         }
22803         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22804              return true;
22805         }
22806         var svalue = value;
22807         value = this.parseDate(value);
22808         if(!value){
22809             Roo.log('parse date failed' + svalue);
22810             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22811             return false;
22812         }
22813         var time = value.getTime();
22814         if(this.minValue && time < this.minValue.getTime()){
22815             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22816             return false;
22817         }
22818         if(this.maxValue && time > this.maxValue.getTime()){
22819             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22820             return false;
22821         }
22822         if(this.disabledDays){
22823             var day = value.getDay();
22824             for(var i = 0; i < this.disabledDays.length; i++) {
22825                 if(day === this.disabledDays[i]){
22826                     this.markInvalid(this.disabledDaysText);
22827                     return false;
22828                 }
22829             }
22830         }
22831         var fvalue = this.formatDate(value);
22832         if(this.ddMatch && this.ddMatch.test(fvalue)){
22833             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22834             return false;
22835         }
22836         return true;
22837     },
22838
22839     // private
22840     // Provides logic to override the default TriggerField.validateBlur which just returns true
22841     validateBlur : function(){
22842         return !this.menu || !this.menu.isVisible();
22843     },
22844     
22845     getName: function()
22846     {
22847         // returns hidden if it's set..
22848         if (!this.rendered) {return ''};
22849         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22850         
22851     },
22852
22853     /**
22854      * Returns the current date value of the date field.
22855      * @return {Date} The date value
22856      */
22857     getValue : function(){
22858         
22859         return  this.hiddenField ?
22860                 this.hiddenField.value :
22861                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22862     },
22863
22864     /**
22865      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22866      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22867      * (the default format used is "m/d/y").
22868      * <br />Usage:
22869      * <pre><code>
22870 //All of these calls set the same date value (May 4, 2006)
22871
22872 //Pass a date object:
22873 var dt = new Date('5/4/06');
22874 dateField.setValue(dt);
22875
22876 //Pass a date string (default format):
22877 dateField.setValue('5/4/06');
22878
22879 //Pass a date string (custom format):
22880 dateField.format = 'Y-m-d';
22881 dateField.setValue('2006-5-4');
22882 </code></pre>
22883      * @param {String/Date} date The date or valid date string
22884      */
22885     setValue : function(date){
22886         if (this.hiddenField) {
22887             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22888         }
22889         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22890         // make sure the value field is always stored as a date..
22891         this.value = this.parseDate(date);
22892         
22893         
22894     },
22895
22896     // private
22897     parseDate : function(value){
22898         if(!value || value instanceof Date){
22899             return value;
22900         }
22901         var v = Date.parseDate(value, this.format);
22902          if (!v && this.useIso) {
22903             v = Date.parseDate(value, 'Y-m-d');
22904         }
22905         if(!v && this.altFormats){
22906             if(!this.altFormatsArray){
22907                 this.altFormatsArray = this.altFormats.split("|");
22908             }
22909             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22910                 v = Date.parseDate(value, this.altFormatsArray[i]);
22911             }
22912         }
22913         return v;
22914     },
22915
22916     // private
22917     formatDate : function(date, fmt){
22918         return (!date || !(date instanceof Date)) ?
22919                date : date.dateFormat(fmt || this.format);
22920     },
22921
22922     // private
22923     menuListeners : {
22924         select: function(m, d){
22925             
22926             this.setValue(d);
22927             this.fireEvent('select', this, d);
22928         },
22929         show : function(){ // retain focus styling
22930             this.onFocus();
22931         },
22932         hide : function(){
22933             this.focus.defer(10, this);
22934             var ml = this.menuListeners;
22935             this.menu.un("select", ml.select,  this);
22936             this.menu.un("show", ml.show,  this);
22937             this.menu.un("hide", ml.hide,  this);
22938         }
22939     },
22940
22941     // private
22942     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22943     onTriggerClick : function(){
22944         if(this.disabled){
22945             return;
22946         }
22947         if(this.menu == null){
22948             this.menu = new Roo.menu.DateMenu();
22949         }
22950         Roo.apply(this.menu.picker,  {
22951             showClear: this.allowBlank,
22952             minDate : this.minValue,
22953             maxDate : this.maxValue,
22954             disabledDatesRE : this.ddMatch,
22955             disabledDatesText : this.disabledDatesText,
22956             disabledDays : this.disabledDays,
22957             disabledDaysText : this.disabledDaysText,
22958             format : this.useIso ? 'Y-m-d' : this.format,
22959             minText : String.format(this.minText, this.formatDate(this.minValue)),
22960             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22961         });
22962         this.menu.on(Roo.apply({}, this.menuListeners, {
22963             scope:this
22964         }));
22965         this.menu.picker.setValue(this.getValue() || new Date());
22966         this.menu.show(this.el, "tl-bl?");
22967     },
22968
22969     beforeBlur : function(){
22970         var v = this.parseDate(this.getRawValue());
22971         if(v){
22972             this.setValue(v);
22973         }
22974     }
22975
22976     /** @cfg {Boolean} grow @hide */
22977     /** @cfg {Number} growMin @hide */
22978     /** @cfg {Number} growMax @hide */
22979     /**
22980      * @hide
22981      * @method autoSize
22982      */
22983 });/*
22984  * Based on:
22985  * Ext JS Library 1.1.1
22986  * Copyright(c) 2006-2007, Ext JS, LLC.
22987  *
22988  * Originally Released Under LGPL - original licence link has changed is not relivant.
22989  *
22990  * Fork - LGPL
22991  * <script type="text/javascript">
22992  */
22993  
22994 /**
22995  * @class Roo.form.MonthField
22996  * @extends Roo.form.TriggerField
22997  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22998 * @constructor
22999 * Create a new MonthField
23000 * @param {Object} config
23001  */
23002 Roo.form.MonthField = function(config){
23003     
23004     Roo.form.MonthField.superclass.constructor.call(this, config);
23005     
23006       this.addEvents({
23007          
23008         /**
23009          * @event select
23010          * Fires when a date is selected
23011              * @param {Roo.form.MonthFieeld} combo This combo box
23012              * @param {Date} date The date selected
23013              */
23014         'select' : true
23015          
23016     });
23017     
23018     
23019     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23020     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23021     this.ddMatch = null;
23022     if(this.disabledDates){
23023         var dd = this.disabledDates;
23024         var re = "(?:";
23025         for(var i = 0; i < dd.length; i++){
23026             re += dd[i];
23027             if(i != dd.length-1) re += "|";
23028         }
23029         this.ddMatch = new RegExp(re + ")");
23030     }
23031 };
23032
23033 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23034     /**
23035      * @cfg {String} format
23036      * The default date format string which can be overriden for localization support.  The format must be
23037      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23038      */
23039     format : "M Y",
23040     /**
23041      * @cfg {String} altFormats
23042      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23043      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23044      */
23045     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23046     /**
23047      * @cfg {Array} disabledDays
23048      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23049      */
23050     disabledDays : [0,1,2,3,4,5,6],
23051     /**
23052      * @cfg {String} disabledDaysText
23053      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23054      */
23055     disabledDaysText : "Disabled",
23056     /**
23057      * @cfg {Array} disabledDates
23058      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23059      * expression so they are very powerful. Some examples:
23060      * <ul>
23061      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23062      * <li>["03/08", "09/16"] would disable those days for every year</li>
23063      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23064      * <li>["03/../2006"] would disable every day in March 2006</li>
23065      * <li>["^03"] would disable every day in every March</li>
23066      * </ul>
23067      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23068      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23069      */
23070     disabledDates : null,
23071     /**
23072      * @cfg {String} disabledDatesText
23073      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23074      */
23075     disabledDatesText : "Disabled",
23076     /**
23077      * @cfg {Date/String} minValue
23078      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23079      * valid format (defaults to null).
23080      */
23081     minValue : null,
23082     /**
23083      * @cfg {Date/String} maxValue
23084      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23085      * valid format (defaults to null).
23086      */
23087     maxValue : null,
23088     /**
23089      * @cfg {String} minText
23090      * The error text to display when the date in the cell is before minValue (defaults to
23091      * 'The date in this field must be after {minValue}').
23092      */
23093     minText : "The date in this field must be equal to or after {0}",
23094     /**
23095      * @cfg {String} maxTextf
23096      * The error text to display when the date in the cell is after maxValue (defaults to
23097      * 'The date in this field must be before {maxValue}').
23098      */
23099     maxText : "The date in this field must be equal to or before {0}",
23100     /**
23101      * @cfg {String} invalidText
23102      * The error text to display when the date in the field is invalid (defaults to
23103      * '{value} is not a valid date - it must be in the format {format}').
23104      */
23105     invalidText : "{0} is not a valid date - it must be in the format {1}",
23106     /**
23107      * @cfg {String} triggerClass
23108      * An additional CSS class used to style the trigger button.  The trigger will always get the
23109      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23110      * which displays a calendar icon).
23111      */
23112     triggerClass : 'x-form-date-trigger',
23113     
23114
23115     /**
23116      * @cfg {Boolean} useIso
23117      * if enabled, then the date field will use a hidden field to store the 
23118      * real value as iso formated date. default (true)
23119      */ 
23120     useIso : true,
23121     /**
23122      * @cfg {String/Object} autoCreate
23123      * A DomHelper element spec, or true for a default element spec (defaults to
23124      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23125      */ 
23126     // private
23127     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23128     
23129     // private
23130     hiddenField: false,
23131     
23132     hideMonthPicker : false,
23133     
23134     onRender : function(ct, position)
23135     {
23136         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23137         if (this.useIso) {
23138             this.el.dom.removeAttribute('name'); 
23139             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23140                     'before', true);
23141             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23142             // prevent input submission
23143             this.hiddenName = this.name;
23144         }
23145             
23146             
23147     },
23148     
23149     // private
23150     validateValue : function(value)
23151     {
23152         value = this.formatDate(value);
23153         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23154             return false;
23155         }
23156         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23157              return true;
23158         }
23159         var svalue = value;
23160         value = this.parseDate(value);
23161         if(!value){
23162             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23163             return false;
23164         }
23165         var time = value.getTime();
23166         if(this.minValue && time < this.minValue.getTime()){
23167             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23168             return false;
23169         }
23170         if(this.maxValue && time > this.maxValue.getTime()){
23171             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23172             return false;
23173         }
23174         /*if(this.disabledDays){
23175             var day = value.getDay();
23176             for(var i = 0; i < this.disabledDays.length; i++) {
23177                 if(day === this.disabledDays[i]){
23178                     this.markInvalid(this.disabledDaysText);
23179                     return false;
23180                 }
23181             }
23182         }
23183         */
23184         var fvalue = this.formatDate(value);
23185         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23186             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23187             return false;
23188         }
23189         */
23190         return true;
23191     },
23192
23193     // private
23194     // Provides logic to override the default TriggerField.validateBlur which just returns true
23195     validateBlur : function(){
23196         return !this.menu || !this.menu.isVisible();
23197     },
23198
23199     /**
23200      * Returns the current date value of the date field.
23201      * @return {Date} The date value
23202      */
23203     getValue : function(){
23204         
23205         
23206         
23207         return  this.hiddenField ?
23208                 this.hiddenField.value :
23209                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23210     },
23211
23212     /**
23213      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23214      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23215      * (the default format used is "m/d/y").
23216      * <br />Usage:
23217      * <pre><code>
23218 //All of these calls set the same date value (May 4, 2006)
23219
23220 //Pass a date object:
23221 var dt = new Date('5/4/06');
23222 monthField.setValue(dt);
23223
23224 //Pass a date string (default format):
23225 monthField.setValue('5/4/06');
23226
23227 //Pass a date string (custom format):
23228 monthField.format = 'Y-m-d';
23229 monthField.setValue('2006-5-4');
23230 </code></pre>
23231      * @param {String/Date} date The date or valid date string
23232      */
23233     setValue : function(date){
23234         Roo.log('month setValue' + date);
23235         // can only be first of month..
23236         
23237         var val = this.parseDate(date);
23238         
23239         if (this.hiddenField) {
23240             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23241         }
23242         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23243         this.value = this.parseDate(date);
23244     },
23245
23246     // private
23247     parseDate : function(value){
23248         if(!value || value instanceof Date){
23249             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23250             return value;
23251         }
23252         var v = Date.parseDate(value, this.format);
23253         if (!v && this.useIso) {
23254             v = Date.parseDate(value, 'Y-m-d');
23255         }
23256         if (v) {
23257             // 
23258             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23259         }
23260         
23261         
23262         if(!v && this.altFormats){
23263             if(!this.altFormatsArray){
23264                 this.altFormatsArray = this.altFormats.split("|");
23265             }
23266             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23267                 v = Date.parseDate(value, this.altFormatsArray[i]);
23268             }
23269         }
23270         return v;
23271     },
23272
23273     // private
23274     formatDate : function(date, fmt){
23275         return (!date || !(date instanceof Date)) ?
23276                date : date.dateFormat(fmt || this.format);
23277     },
23278
23279     // private
23280     menuListeners : {
23281         select: function(m, d){
23282             this.setValue(d);
23283             this.fireEvent('select', this, d);
23284         },
23285         show : function(){ // retain focus styling
23286             this.onFocus();
23287         },
23288         hide : function(){
23289             this.focus.defer(10, this);
23290             var ml = this.menuListeners;
23291             this.menu.un("select", ml.select,  this);
23292             this.menu.un("show", ml.show,  this);
23293             this.menu.un("hide", ml.hide,  this);
23294         }
23295     },
23296     // private
23297     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23298     onTriggerClick : function(){
23299         if(this.disabled){
23300             return;
23301         }
23302         if(this.menu == null){
23303             this.menu = new Roo.menu.DateMenu();
23304            
23305         }
23306         
23307         Roo.apply(this.menu.picker,  {
23308             
23309             showClear: this.allowBlank,
23310             minDate : this.minValue,
23311             maxDate : this.maxValue,
23312             disabledDatesRE : this.ddMatch,
23313             disabledDatesText : this.disabledDatesText,
23314             
23315             format : this.useIso ? 'Y-m-d' : this.format,
23316             minText : String.format(this.minText, this.formatDate(this.minValue)),
23317             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23318             
23319         });
23320          this.menu.on(Roo.apply({}, this.menuListeners, {
23321             scope:this
23322         }));
23323        
23324         
23325         var m = this.menu;
23326         var p = m.picker;
23327         
23328         // hide month picker get's called when we called by 'before hide';
23329         
23330         var ignorehide = true;
23331         p.hideMonthPicker  = function(disableAnim){
23332             if (ignorehide) {
23333                 return;
23334             }
23335              if(this.monthPicker){
23336                 Roo.log("hideMonthPicker called");
23337                 if(disableAnim === true){
23338                     this.monthPicker.hide();
23339                 }else{
23340                     this.monthPicker.slideOut('t', {duration:.2});
23341                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23342                     p.fireEvent("select", this, this.value);
23343                     m.hide();
23344                 }
23345             }
23346         }
23347         
23348         Roo.log('picker set value');
23349         Roo.log(this.getValue());
23350         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23351         m.show(this.el, 'tl-bl?');
23352         ignorehide  = false;
23353         // this will trigger hideMonthPicker..
23354         
23355         
23356         // hidden the day picker
23357         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23358         
23359         
23360         
23361       
23362         
23363         p.showMonthPicker.defer(100, p);
23364     
23365         
23366        
23367     },
23368
23369     beforeBlur : function(){
23370         var v = this.parseDate(this.getRawValue());
23371         if(v){
23372             this.setValue(v);
23373         }
23374     }
23375
23376     /** @cfg {Boolean} grow @hide */
23377     /** @cfg {Number} growMin @hide */
23378     /** @cfg {Number} growMax @hide */
23379     /**
23380      * @hide
23381      * @method autoSize
23382      */
23383 });/*
23384  * Based on:
23385  * Ext JS Library 1.1.1
23386  * Copyright(c) 2006-2007, Ext JS, LLC.
23387  *
23388  * Originally Released Under LGPL - original licence link has changed is not relivant.
23389  *
23390  * Fork - LGPL
23391  * <script type="text/javascript">
23392  */
23393  
23394
23395 /**
23396  * @class Roo.form.ComboBox
23397  * @extends Roo.form.TriggerField
23398  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23399  * @constructor
23400  * Create a new ComboBox.
23401  * @param {Object} config Configuration options
23402  */
23403 Roo.form.ComboBox = function(config){
23404     Roo.form.ComboBox.superclass.constructor.call(this, config);
23405     this.addEvents({
23406         /**
23407          * @event expand
23408          * Fires when the dropdown list is expanded
23409              * @param {Roo.form.ComboBox} combo This combo box
23410              */
23411         'expand' : true,
23412         /**
23413          * @event collapse
23414          * Fires when the dropdown list is collapsed
23415              * @param {Roo.form.ComboBox} combo This combo box
23416              */
23417         'collapse' : true,
23418         /**
23419          * @event beforeselect
23420          * Fires before a list item is selected. Return false to cancel the selection.
23421              * @param {Roo.form.ComboBox} combo This combo box
23422              * @param {Roo.data.Record} record The data record returned from the underlying store
23423              * @param {Number} index The index of the selected item in the dropdown list
23424              */
23425         'beforeselect' : true,
23426         /**
23427          * @event select
23428          * Fires when a list item is selected
23429              * @param {Roo.form.ComboBox} combo This combo box
23430              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23431              * @param {Number} index The index of the selected item in the dropdown list
23432              */
23433         'select' : true,
23434         /**
23435          * @event beforequery
23436          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23437          * The event object passed has these properties:
23438              * @param {Roo.form.ComboBox} combo This combo box
23439              * @param {String} query The query
23440              * @param {Boolean} forceAll true to force "all" query
23441              * @param {Boolean} cancel true to cancel the query
23442              * @param {Object} e The query event object
23443              */
23444         'beforequery': true,
23445          /**
23446          * @event add
23447          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23448              * @param {Roo.form.ComboBox} combo This combo box
23449              */
23450         'add' : true,
23451         /**
23452          * @event edit
23453          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23454              * @param {Roo.form.ComboBox} combo This combo box
23455              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23456              */
23457         'edit' : true
23458         
23459         
23460     });
23461     if(this.transform){
23462         this.allowDomMove = false;
23463         var s = Roo.getDom(this.transform);
23464         if(!this.hiddenName){
23465             this.hiddenName = s.name;
23466         }
23467         if(!this.store){
23468             this.mode = 'local';
23469             var d = [], opts = s.options;
23470             for(var i = 0, len = opts.length;i < len; i++){
23471                 var o = opts[i];
23472                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23473                 if(o.selected) {
23474                     this.value = value;
23475                 }
23476                 d.push([value, o.text]);
23477             }
23478             this.store = new Roo.data.SimpleStore({
23479                 'id': 0,
23480                 fields: ['value', 'text'],
23481                 data : d
23482             });
23483             this.valueField = 'value';
23484             this.displayField = 'text';
23485         }
23486         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23487         if(!this.lazyRender){
23488             this.target = true;
23489             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23490             s.parentNode.removeChild(s); // remove it
23491             this.render(this.el.parentNode);
23492         }else{
23493             s.parentNode.removeChild(s); // remove it
23494         }
23495
23496     }
23497     if (this.store) {
23498         this.store = Roo.factory(this.store, Roo.data);
23499     }
23500     
23501     this.selectedIndex = -1;
23502     if(this.mode == 'local'){
23503         if(config.queryDelay === undefined){
23504             this.queryDelay = 10;
23505         }
23506         if(config.minChars === undefined){
23507             this.minChars = 0;
23508         }
23509     }
23510 };
23511
23512 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23513     /**
23514      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23515      */
23516     /**
23517      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23518      * rendering into an Roo.Editor, defaults to false)
23519      */
23520     /**
23521      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23522      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23523      */
23524     /**
23525      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23526      */
23527     /**
23528      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23529      * the dropdown list (defaults to undefined, with no header element)
23530      */
23531
23532      /**
23533      * @cfg {String/Roo.Template} tpl The template to use to render the output
23534      */
23535      
23536     // private
23537     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23538     /**
23539      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23540      */
23541     listWidth: undefined,
23542     /**
23543      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23544      * mode = 'remote' or 'text' if mode = 'local')
23545      */
23546     displayField: undefined,
23547     /**
23548      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23549      * mode = 'remote' or 'value' if mode = 'local'). 
23550      * Note: use of a valueField requires the user make a selection
23551      * in order for a value to be mapped.
23552      */
23553     valueField: undefined,
23554     
23555     
23556     /**
23557      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23558      * field's data value (defaults to the underlying DOM element's name)
23559      */
23560     hiddenName: undefined,
23561     /**
23562      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23563      */
23564     listClass: '',
23565     /**
23566      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23567      */
23568     selectedClass: 'x-combo-selected',
23569     /**
23570      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23571      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23572      * which displays a downward arrow icon).
23573      */
23574     triggerClass : 'x-form-arrow-trigger',
23575     /**
23576      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23577      */
23578     shadow:'sides',
23579     /**
23580      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23581      * anchor positions (defaults to 'tl-bl')
23582      */
23583     listAlign: 'tl-bl?',
23584     /**
23585      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23586      */
23587     maxHeight: 300,
23588     /**
23589      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23590      * query specified by the allQuery config option (defaults to 'query')
23591      */
23592     triggerAction: 'query',
23593     /**
23594      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23595      * (defaults to 4, does not apply if editable = false)
23596      */
23597     minChars : 4,
23598     /**
23599      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23600      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23601      */
23602     typeAhead: false,
23603     /**
23604      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23605      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23606      */
23607     queryDelay: 500,
23608     /**
23609      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23610      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23611      */
23612     pageSize: 0,
23613     /**
23614      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23615      * when editable = true (defaults to false)
23616      */
23617     selectOnFocus:false,
23618     /**
23619      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23620      */
23621     queryParam: 'query',
23622     /**
23623      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23624      * when mode = 'remote' (defaults to 'Loading...')
23625      */
23626     loadingText: 'Loading...',
23627     /**
23628      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23629      */
23630     resizable: false,
23631     /**
23632      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23633      */
23634     handleHeight : 8,
23635     /**
23636      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23637      * traditional select (defaults to true)
23638      */
23639     editable: true,
23640     /**
23641      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23642      */
23643     allQuery: '',
23644     /**
23645      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23646      */
23647     mode: 'remote',
23648     /**
23649      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23650      * listWidth has a higher value)
23651      */
23652     minListWidth : 70,
23653     /**
23654      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23655      * allow the user to set arbitrary text into the field (defaults to false)
23656      */
23657     forceSelection:false,
23658     /**
23659      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23660      * if typeAhead = true (defaults to 250)
23661      */
23662     typeAheadDelay : 250,
23663     /**
23664      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23665      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23666      */
23667     valueNotFoundText : undefined,
23668     /**
23669      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23670      */
23671     blockFocus : false,
23672     
23673     /**
23674      * @cfg {Boolean} disableClear Disable showing of clear button.
23675      */
23676     disableClear : false,
23677     /**
23678      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23679      */
23680     alwaysQuery : false,
23681     
23682     //private
23683     addicon : false,
23684     editicon: false,
23685     
23686     // element that contains real text value.. (when hidden is used..)
23687      
23688     // private
23689     onRender : function(ct, position){
23690         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23691         if(this.hiddenName){
23692             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23693                     'before', true);
23694             this.hiddenField.value =
23695                 this.hiddenValue !== undefined ? this.hiddenValue :
23696                 this.value !== undefined ? this.value : '';
23697
23698             // prevent input submission
23699             this.el.dom.removeAttribute('name');
23700              
23701              
23702         }
23703         if(Roo.isGecko){
23704             this.el.dom.setAttribute('autocomplete', 'off');
23705         }
23706
23707         var cls = 'x-combo-list';
23708
23709         this.list = new Roo.Layer({
23710             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23711         });
23712
23713         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23714         this.list.setWidth(lw);
23715         this.list.swallowEvent('mousewheel');
23716         this.assetHeight = 0;
23717
23718         if(this.title){
23719             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23720             this.assetHeight += this.header.getHeight();
23721         }
23722
23723         this.innerList = this.list.createChild({cls:cls+'-inner'});
23724         this.innerList.on('mouseover', this.onViewOver, this);
23725         this.innerList.on('mousemove', this.onViewMove, this);
23726         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23727         
23728         if(this.allowBlank && !this.pageSize && !this.disableClear){
23729             this.footer = this.list.createChild({cls:cls+'-ft'});
23730             this.pageTb = new Roo.Toolbar(this.footer);
23731            
23732         }
23733         if(this.pageSize){
23734             this.footer = this.list.createChild({cls:cls+'-ft'});
23735             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23736                     {pageSize: this.pageSize});
23737             
23738         }
23739         
23740         if (this.pageTb && this.allowBlank && !this.disableClear) {
23741             var _this = this;
23742             this.pageTb.add(new Roo.Toolbar.Fill(), {
23743                 cls: 'x-btn-icon x-btn-clear',
23744                 text: '&#160;',
23745                 handler: function()
23746                 {
23747                     _this.collapse();
23748                     _this.clearValue();
23749                     _this.onSelect(false, -1);
23750                 }
23751             });
23752         }
23753         if (this.footer) {
23754             this.assetHeight += this.footer.getHeight();
23755         }
23756         
23757
23758         if(!this.tpl){
23759             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23760         }
23761
23762         this.view = new Roo.View(this.innerList, this.tpl, {
23763             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23764         });
23765
23766         this.view.on('click', this.onViewClick, this);
23767
23768         this.store.on('beforeload', this.onBeforeLoad, this);
23769         this.store.on('load', this.onLoad, this);
23770         this.store.on('loadexception', this.onLoadException, this);
23771
23772         if(this.resizable){
23773             this.resizer = new Roo.Resizable(this.list,  {
23774                pinned:true, handles:'se'
23775             });
23776             this.resizer.on('resize', function(r, w, h){
23777                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23778                 this.listWidth = w;
23779                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23780                 this.restrictHeight();
23781             }, this);
23782             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23783         }
23784         if(!this.editable){
23785             this.editable = true;
23786             this.setEditable(false);
23787         }  
23788         
23789         
23790         if (typeof(this.events.add.listeners) != 'undefined') {
23791             
23792             this.addicon = this.wrap.createChild(
23793                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23794        
23795             this.addicon.on('click', function(e) {
23796                 this.fireEvent('add', this);
23797             }, this);
23798         }
23799         if (typeof(this.events.edit.listeners) != 'undefined') {
23800             
23801             this.editicon = this.wrap.createChild(
23802                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23803             if (this.addicon) {
23804                 this.editicon.setStyle('margin-left', '40px');
23805             }
23806             this.editicon.on('click', function(e) {
23807                 
23808                 // we fire even  if inothing is selected..
23809                 this.fireEvent('edit', this, this.lastData );
23810                 
23811             }, this);
23812         }
23813         
23814         
23815         
23816     },
23817
23818     // private
23819     initEvents : function(){
23820         Roo.form.ComboBox.superclass.initEvents.call(this);
23821
23822         this.keyNav = new Roo.KeyNav(this.el, {
23823             "up" : function(e){
23824                 this.inKeyMode = true;
23825                 this.selectPrev();
23826             },
23827
23828             "down" : function(e){
23829                 if(!this.isExpanded()){
23830                     this.onTriggerClick();
23831                 }else{
23832                     this.inKeyMode = true;
23833                     this.selectNext();
23834                 }
23835             },
23836
23837             "enter" : function(e){
23838                 this.onViewClick();
23839                 //return true;
23840             },
23841
23842             "esc" : function(e){
23843                 this.collapse();
23844             },
23845
23846             "tab" : function(e){
23847                 this.onViewClick(false);
23848                 this.fireEvent("specialkey", this, e);
23849                 return true;
23850             },
23851
23852             scope : this,
23853
23854             doRelay : function(foo, bar, hname){
23855                 if(hname == 'down' || this.scope.isExpanded()){
23856                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23857                 }
23858                 return true;
23859             },
23860
23861             forceKeyDown: true
23862         });
23863         this.queryDelay = Math.max(this.queryDelay || 10,
23864                 this.mode == 'local' ? 10 : 250);
23865         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23866         if(this.typeAhead){
23867             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23868         }
23869         if(this.editable !== false){
23870             this.el.on("keyup", this.onKeyUp, this);
23871         }
23872         if(this.forceSelection){
23873             this.on('blur', this.doForce, this);
23874         }
23875     },
23876
23877     onDestroy : function(){
23878         if(this.view){
23879             this.view.setStore(null);
23880             this.view.el.removeAllListeners();
23881             this.view.el.remove();
23882             this.view.purgeListeners();
23883         }
23884         if(this.list){
23885             this.list.destroy();
23886         }
23887         if(this.store){
23888             this.store.un('beforeload', this.onBeforeLoad, this);
23889             this.store.un('load', this.onLoad, this);
23890             this.store.un('loadexception', this.onLoadException, this);
23891         }
23892         Roo.form.ComboBox.superclass.onDestroy.call(this);
23893     },
23894
23895     // private
23896     fireKey : function(e){
23897         if(e.isNavKeyPress() && !this.list.isVisible()){
23898             this.fireEvent("specialkey", this, e);
23899         }
23900     },
23901
23902     // private
23903     onResize: function(w, h){
23904         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23905         
23906         if(typeof w != 'number'){
23907             // we do not handle it!?!?
23908             return;
23909         }
23910         var tw = this.trigger.getWidth();
23911         tw += this.addicon ? this.addicon.getWidth() : 0;
23912         tw += this.editicon ? this.editicon.getWidth() : 0;
23913         var x = w - tw;
23914         this.el.setWidth( this.adjustWidth('input', x));
23915             
23916         this.trigger.setStyle('left', x+'px');
23917         
23918         if(this.list && this.listWidth === undefined){
23919             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23920             this.list.setWidth(lw);
23921             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23922         }
23923         
23924     
23925         
23926     },
23927
23928     /**
23929      * Allow or prevent the user from directly editing the field text.  If false is passed,
23930      * the user will only be able to select from the items defined in the dropdown list.  This method
23931      * is the runtime equivalent of setting the 'editable' config option at config time.
23932      * @param {Boolean} value True to allow the user to directly edit the field text
23933      */
23934     setEditable : function(value){
23935         if(value == this.editable){
23936             return;
23937         }
23938         this.editable = value;
23939         if(!value){
23940             this.el.dom.setAttribute('readOnly', true);
23941             this.el.on('mousedown', this.onTriggerClick,  this);
23942             this.el.addClass('x-combo-noedit');
23943         }else{
23944             this.el.dom.setAttribute('readOnly', false);
23945             this.el.un('mousedown', this.onTriggerClick,  this);
23946             this.el.removeClass('x-combo-noedit');
23947         }
23948     },
23949
23950     // private
23951     onBeforeLoad : function(){
23952         if(!this.hasFocus){
23953             return;
23954         }
23955         this.innerList.update(this.loadingText ?
23956                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23957         this.restrictHeight();
23958         this.selectedIndex = -1;
23959     },
23960
23961     // private
23962     onLoad : function(){
23963         if(!this.hasFocus){
23964             return;
23965         }
23966         if(this.store.getCount() > 0){
23967             this.expand();
23968             this.restrictHeight();
23969             if(this.lastQuery == this.allQuery){
23970                 if(this.editable){
23971                     this.el.dom.select();
23972                 }
23973                 if(!this.selectByValue(this.value, true)){
23974                     this.select(0, true);
23975                 }
23976             }else{
23977                 this.selectNext();
23978                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23979                     this.taTask.delay(this.typeAheadDelay);
23980                 }
23981             }
23982         }else{
23983             this.onEmptyResults();
23984         }
23985         //this.el.focus();
23986     },
23987     // private
23988     onLoadException : function()
23989     {
23990         this.collapse();
23991         Roo.log(this.store.reader.jsonData);
23992         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23993             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23994         }
23995         
23996         
23997     },
23998     // private
23999     onTypeAhead : function(){
24000         if(this.store.getCount() > 0){
24001             var r = this.store.getAt(0);
24002             var newValue = r.data[this.displayField];
24003             var len = newValue.length;
24004             var selStart = this.getRawValue().length;
24005             if(selStart != len){
24006                 this.setRawValue(newValue);
24007                 this.selectText(selStart, newValue.length);
24008             }
24009         }
24010     },
24011
24012     // private
24013     onSelect : function(record, index){
24014         if(this.fireEvent('beforeselect', this, record, index) !== false){
24015             this.setFromData(index > -1 ? record.data : false);
24016             this.collapse();
24017             this.fireEvent('select', this, record, index);
24018         }
24019     },
24020
24021     /**
24022      * Returns the currently selected field value or empty string if no value is set.
24023      * @return {String} value The selected value
24024      */
24025     getValue : function(){
24026         if(this.valueField){
24027             return typeof this.value != 'undefined' ? this.value : '';
24028         }else{
24029             return Roo.form.ComboBox.superclass.getValue.call(this);
24030         }
24031     },
24032
24033     /**
24034      * Clears any text/value currently set in the field
24035      */
24036     clearValue : function(){
24037         if(this.hiddenField){
24038             this.hiddenField.value = '';
24039         }
24040         this.value = '';
24041         this.setRawValue('');
24042         this.lastSelectionText = '';
24043         
24044     },
24045
24046     /**
24047      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24048      * will be displayed in the field.  If the value does not match the data value of an existing item,
24049      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24050      * Otherwise the field will be blank (although the value will still be set).
24051      * @param {String} value The value to match
24052      */
24053     setValue : function(v){
24054         var text = v;
24055         if(this.valueField){
24056             var r = this.findRecord(this.valueField, v);
24057             if(r){
24058                 text = r.data[this.displayField];
24059             }else if(this.valueNotFoundText !== undefined){
24060                 text = this.valueNotFoundText;
24061             }
24062         }
24063         this.lastSelectionText = text;
24064         if(this.hiddenField){
24065             this.hiddenField.value = v;
24066         }
24067         Roo.form.ComboBox.superclass.setValue.call(this, text);
24068         this.value = v;
24069     },
24070     /**
24071      * @property {Object} the last set data for the element
24072      */
24073     
24074     lastData : false,
24075     /**
24076      * Sets the value of the field based on a object which is related to the record format for the store.
24077      * @param {Object} value the value to set as. or false on reset?
24078      */
24079     setFromData : function(o){
24080         var dv = ''; // display value
24081         var vv = ''; // value value..
24082         this.lastData = o;
24083         if (this.displayField) {
24084             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24085         } else {
24086             // this is an error condition!!!
24087             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24088         }
24089         
24090         if(this.valueField){
24091             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24092         }
24093         if(this.hiddenField){
24094             this.hiddenField.value = vv;
24095             
24096             this.lastSelectionText = dv;
24097             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24098             this.value = vv;
24099             return;
24100         }
24101         // no hidden field.. - we store the value in 'value', but still display
24102         // display field!!!!
24103         this.lastSelectionText = dv;
24104         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24105         this.value = vv;
24106         
24107         
24108     },
24109     // private
24110     reset : function(){
24111         // overridden so that last data is reset..
24112         this.setValue(this.originalValue);
24113         this.clearInvalid();
24114         this.lastData = false;
24115         if (this.view) {
24116             this.view.clearSelections();
24117         }
24118     },
24119     // private
24120     findRecord : function(prop, value){
24121         var record;
24122         if(this.store.getCount() > 0){
24123             this.store.each(function(r){
24124                 if(r.data[prop] == value){
24125                     record = r;
24126                     return false;
24127                 }
24128                 return true;
24129             });
24130         }
24131         return record;
24132     },
24133     
24134     getName: function()
24135     {
24136         // returns hidden if it's set..
24137         if (!this.rendered) {return ''};
24138         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24139         
24140     },
24141     // private
24142     onViewMove : function(e, t){
24143         this.inKeyMode = false;
24144     },
24145
24146     // private
24147     onViewOver : function(e, t){
24148         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24149             return;
24150         }
24151         var item = this.view.findItemFromChild(t);
24152         if(item){
24153             var index = this.view.indexOf(item);
24154             this.select(index, false);
24155         }
24156     },
24157
24158     // private
24159     onViewClick : function(doFocus)
24160     {
24161         var index = this.view.getSelectedIndexes()[0];
24162         var r = this.store.getAt(index);
24163         if(r){
24164             this.onSelect(r, index);
24165         }
24166         if(doFocus !== false && !this.blockFocus){
24167             this.el.focus();
24168         }
24169     },
24170
24171     // private
24172     restrictHeight : function(){
24173         this.innerList.dom.style.height = '';
24174         var inner = this.innerList.dom;
24175         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24176         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24177         this.list.beginUpdate();
24178         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24179         this.list.alignTo(this.el, this.listAlign);
24180         this.list.endUpdate();
24181     },
24182
24183     // private
24184     onEmptyResults : function(){
24185         this.collapse();
24186     },
24187
24188     /**
24189      * Returns true if the dropdown list is expanded, else false.
24190      */
24191     isExpanded : function(){
24192         return this.list.isVisible();
24193     },
24194
24195     /**
24196      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24197      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24198      * @param {String} value The data value of the item to select
24199      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24200      * selected item if it is not currently in view (defaults to true)
24201      * @return {Boolean} True if the value matched an item in the list, else false
24202      */
24203     selectByValue : function(v, scrollIntoView){
24204         if(v !== undefined && v !== null){
24205             var r = this.findRecord(this.valueField || this.displayField, v);
24206             if(r){
24207                 this.select(this.store.indexOf(r), scrollIntoView);
24208                 return true;
24209             }
24210         }
24211         return false;
24212     },
24213
24214     /**
24215      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24216      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24217      * @param {Number} index The zero-based index of the list item to select
24218      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24219      * selected item if it is not currently in view (defaults to true)
24220      */
24221     select : function(index, scrollIntoView){
24222         this.selectedIndex = index;
24223         this.view.select(index);
24224         if(scrollIntoView !== false){
24225             var el = this.view.getNode(index);
24226             if(el){
24227                 this.innerList.scrollChildIntoView(el, false);
24228             }
24229         }
24230     },
24231
24232     // private
24233     selectNext : function(){
24234         var ct = this.store.getCount();
24235         if(ct > 0){
24236             if(this.selectedIndex == -1){
24237                 this.select(0);
24238             }else if(this.selectedIndex < ct-1){
24239                 this.select(this.selectedIndex+1);
24240             }
24241         }
24242     },
24243
24244     // private
24245     selectPrev : function(){
24246         var ct = this.store.getCount();
24247         if(ct > 0){
24248             if(this.selectedIndex == -1){
24249                 this.select(0);
24250             }else if(this.selectedIndex != 0){
24251                 this.select(this.selectedIndex-1);
24252             }
24253         }
24254     },
24255
24256     // private
24257     onKeyUp : function(e){
24258         if(this.editable !== false && !e.isSpecialKey()){
24259             this.lastKey = e.getKey();
24260             this.dqTask.delay(this.queryDelay);
24261         }
24262     },
24263
24264     // private
24265     validateBlur : function(){
24266         return !this.list || !this.list.isVisible();   
24267     },
24268
24269     // private
24270     initQuery : function(){
24271         this.doQuery(this.getRawValue());
24272     },
24273
24274     // private
24275     doForce : function(){
24276         if(this.el.dom.value.length > 0){
24277             this.el.dom.value =
24278                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24279              
24280         }
24281     },
24282
24283     /**
24284      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24285      * query allowing the query action to be canceled if needed.
24286      * @param {String} query The SQL query to execute
24287      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24288      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24289      * saved in the current store (defaults to false)
24290      */
24291     doQuery : function(q, forceAll){
24292         if(q === undefined || q === null){
24293             q = '';
24294         }
24295         var qe = {
24296             query: q,
24297             forceAll: forceAll,
24298             combo: this,
24299             cancel:false
24300         };
24301         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24302             return false;
24303         }
24304         q = qe.query;
24305         forceAll = qe.forceAll;
24306         if(forceAll === true || (q.length >= this.minChars)){
24307             if(this.lastQuery != q || this.alwaysQuery){
24308                 this.lastQuery = q;
24309                 if(this.mode == 'local'){
24310                     this.selectedIndex = -1;
24311                     if(forceAll){
24312                         this.store.clearFilter();
24313                     }else{
24314                         this.store.filter(this.displayField, q);
24315                     }
24316                     this.onLoad();
24317                 }else{
24318                     this.store.baseParams[this.queryParam] = q;
24319                     this.store.load({
24320                         params: this.getParams(q)
24321                     });
24322                     this.expand();
24323                 }
24324             }else{
24325                 this.selectedIndex = -1;
24326                 this.onLoad();   
24327             }
24328         }
24329     },
24330
24331     // private
24332     getParams : function(q){
24333         var p = {};
24334         //p[this.queryParam] = q;
24335         if(this.pageSize){
24336             p.start = 0;
24337             p.limit = this.pageSize;
24338         }
24339         return p;
24340     },
24341
24342     /**
24343      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24344      */
24345     collapse : function(){
24346         if(!this.isExpanded()){
24347             return;
24348         }
24349         this.list.hide();
24350         Roo.get(document).un('mousedown', this.collapseIf, this);
24351         Roo.get(document).un('mousewheel', this.collapseIf, this);
24352         if (!this.editable) {
24353             Roo.get(document).un('keydown', this.listKeyPress, this);
24354         }
24355         this.fireEvent('collapse', this);
24356     },
24357
24358     // private
24359     collapseIf : function(e){
24360         if(!e.within(this.wrap) && !e.within(this.list)){
24361             this.collapse();
24362         }
24363     },
24364
24365     /**
24366      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24367      */
24368     expand : function(){
24369         if(this.isExpanded() || !this.hasFocus){
24370             return;
24371         }
24372         this.list.alignTo(this.el, this.listAlign);
24373         this.list.show();
24374         Roo.get(document).on('mousedown', this.collapseIf, this);
24375         Roo.get(document).on('mousewheel', this.collapseIf, this);
24376         if (!this.editable) {
24377             Roo.get(document).on('keydown', this.listKeyPress, this);
24378         }
24379         
24380         this.fireEvent('expand', this);
24381     },
24382
24383     // private
24384     // Implements the default empty TriggerField.onTriggerClick function
24385     onTriggerClick : function(){
24386         if(this.disabled){
24387             return;
24388         }
24389         if(this.isExpanded()){
24390             this.collapse();
24391             if (!this.blockFocus) {
24392                 this.el.focus();
24393             }
24394             
24395         }else {
24396             this.hasFocus = true;
24397             if(this.triggerAction == 'all') {
24398                 this.doQuery(this.allQuery, true);
24399             } else {
24400                 this.doQuery(this.getRawValue());
24401             }
24402             if (!this.blockFocus) {
24403                 this.el.focus();
24404             }
24405         }
24406     },
24407     listKeyPress : function(e)
24408     {
24409         //Roo.log('listkeypress');
24410         // scroll to first matching element based on key pres..
24411         if (e.isSpecialKey()) {
24412             return false;
24413         }
24414         var k = String.fromCharCode(e.getKey()).toUpperCase();
24415         //Roo.log(k);
24416         var match  = false;
24417         var csel = this.view.getSelectedNodes();
24418         var cselitem = false;
24419         if (csel.length) {
24420             var ix = this.view.indexOf(csel[0]);
24421             cselitem  = this.store.getAt(ix);
24422             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24423                 cselitem = false;
24424             }
24425             
24426         }
24427         
24428         this.store.each(function(v) { 
24429             if (cselitem) {
24430                 // start at existing selection.
24431                 if (cselitem.id == v.id) {
24432                     cselitem = false;
24433                 }
24434                 return;
24435             }
24436                 
24437             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24438                 match = this.store.indexOf(v);
24439                 return false;
24440             }
24441         }, this);
24442         
24443         if (match === false) {
24444             return true; // no more action?
24445         }
24446         // scroll to?
24447         this.view.select(match);
24448         var sn = Roo.get(this.view.getSelectedNodes()[0])
24449         sn.scrollIntoView(sn.dom.parentNode, false);
24450     }
24451
24452     /** 
24453     * @cfg {Boolean} grow 
24454     * @hide 
24455     */
24456     /** 
24457     * @cfg {Number} growMin 
24458     * @hide 
24459     */
24460     /** 
24461     * @cfg {Number} growMax 
24462     * @hide 
24463     */
24464     /**
24465      * @hide
24466      * @method autoSize
24467      */
24468 });/*
24469  * Copyright(c) 2010-2012, Roo J Solutions Limited
24470  *
24471  * Licence LGPL
24472  *
24473  */
24474
24475 /**
24476  * @class Roo.form.ComboBoxArray
24477  * @extends Roo.form.TextField
24478  * A facebook style adder... for lists of email / people / countries  etc...
24479  * pick multiple items from a combo box, and shows each one.
24480  *
24481  *  Fred [x]  Brian [x]  [Pick another |v]
24482  *
24483  *
24484  *  For this to work: it needs various extra information
24485  *    - normal combo problay has
24486  *      name, hiddenName
24487  *    + displayField, valueField
24488  *
24489  *    For our purpose...
24490  *
24491  *
24492  *   If we change from 'extends' to wrapping...
24493  *   
24494  *  
24495  *
24496  
24497  
24498  * @constructor
24499  * Create a new ComboBoxArray.
24500  * @param {Object} config Configuration options
24501  */
24502  
24503
24504 Roo.form.ComboBoxArray = function(config)
24505 {
24506     
24507     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24508     
24509     this.items = new Roo.util.MixedCollection(false);
24510     
24511     // construct the child combo...
24512     
24513     
24514     
24515     
24516    
24517     
24518 }
24519
24520  
24521 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24522
24523     /**
24524      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24525      */
24526     
24527     lastData : false,
24528     
24529     // behavies liek a hiddne field
24530     inputType:      'hidden',
24531     /**
24532      * @cfg {Number} width The width of the box that displays the selected element
24533      */ 
24534     width:          300,
24535
24536     
24537     
24538     /**
24539      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24540      */
24541     name : false,
24542     /**
24543      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24544      */
24545     hiddenName : false,
24546     
24547     
24548     // private the array of items that are displayed..
24549     items  : false,
24550     // private - the hidden field el.
24551     hiddenEl : false,
24552     // private - the filed el..
24553     el : false,
24554     
24555     //validateValue : function() { return true; }, // all values are ok!
24556     //onAddClick: function() { },
24557     
24558     onRender : function(ct, position) 
24559     {
24560         
24561         // create the standard hidden element
24562         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24563         
24564         
24565         // give fake names to child combo;
24566         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24567         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24568         
24569         this.combo = Roo.factory(this.combo, Roo.form);
24570         this.combo.onRender(ct, position);
24571         this.combo.onResize(this.combo.width,0);
24572         this.combo.initEvents();
24573         
24574         // assigned so form know we need to do this..
24575         this.store          = this.combo.store;
24576         this.valueField     = this.combo.valueField;
24577         this.displayField   = this.combo.displayField ;
24578         
24579         
24580         this.combo.wrap.addClass('x-cbarray-grp');
24581         
24582         var cbwrap = this.combo.wrap.createChild(
24583             {tag: 'div', cls: 'x-cbarray-cb'},
24584             this.combo.el.dom
24585         );
24586         
24587              
24588         this.hiddenEl = this.combo.wrap.createChild({
24589             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24590         });
24591         this.el = this.combo.wrap.createChild({
24592             tag: 'input',  type:'hidden' , name: this.name, value : ''
24593         });
24594          //   this.el.dom.removeAttribute("name");
24595         
24596         
24597         this.outerWrap = this.combo.wrap;
24598         this.wrap = cbwrap;
24599         
24600         this.outerWrap.setWidth(this.width);
24601         this.outerWrap.dom.removeChild(this.el.dom);
24602         
24603         this.wrap.dom.appendChild(this.el.dom);
24604         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24605         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24606         
24607         this.combo.trigger.setStyle('position','relative');
24608         this.combo.trigger.setStyle('left', '0px');
24609         this.combo.trigger.setStyle('top', '2px');
24610         
24611         this.combo.el.setStyle('vertical-align', 'text-bottom');
24612         
24613         //this.trigger.setStyle('vertical-align', 'top');
24614         
24615         // this should use the code from combo really... on('add' ....)
24616         if (this.adder) {
24617             
24618         
24619             this.adder = this.outerWrap.createChild(
24620                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24621             var _t = this;
24622             this.adder.on('click', function(e) {
24623                 _t.fireEvent('adderclick', this, e);
24624             }, _t);
24625         }
24626         //var _t = this;
24627         //this.adder.on('click', this.onAddClick, _t);
24628         
24629         
24630         this.combo.on('select', function(cb, rec, ix) {
24631             this.addItem(rec.data);
24632             
24633             cb.setValue('');
24634             cb.el.dom.value = '';
24635             //cb.lastData = rec.data;
24636             // add to list
24637             
24638         }, this);
24639         
24640         
24641     },
24642     
24643     
24644     getName: function()
24645     {
24646         // returns hidden if it's set..
24647         if (!this.rendered) {return ''};
24648         return  this.hiddenName ? this.hiddenName : this.name;
24649         
24650     },
24651     
24652     
24653     onResize: function(w, h){
24654         
24655         return;
24656         // not sure if this is needed..
24657         //this.combo.onResize(w,h);
24658         
24659         if(typeof w != 'number'){
24660             // we do not handle it!?!?
24661             return;
24662         }
24663         var tw = this.combo.trigger.getWidth();
24664         tw += this.addicon ? this.addicon.getWidth() : 0;
24665         tw += this.editicon ? this.editicon.getWidth() : 0;
24666         var x = w - tw;
24667         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24668             
24669         this.combo.trigger.setStyle('left', '0px');
24670         
24671         if(this.list && this.listWidth === undefined){
24672             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24673             this.list.setWidth(lw);
24674             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24675         }
24676         
24677     
24678         
24679     },
24680     
24681     addItem: function(rec)
24682     {
24683         var valueField = this.combo.valueField;
24684         var displayField = this.combo.displayField;
24685         if (this.items.indexOfKey(rec[valueField]) > -1) {
24686             //console.log("GOT " + rec.data.id);
24687             return;
24688         }
24689         
24690         var x = new Roo.form.ComboBoxArray.Item({
24691             //id : rec[this.idField],
24692             data : rec,
24693             displayField : displayField ,
24694             tipField : displayField ,
24695             cb : this
24696         });
24697         // use the 
24698         this.items.add(rec[valueField],x);
24699         // add it before the element..
24700         this.updateHiddenEl();
24701         x.render(this.outerWrap, this.wrap.dom);
24702         // add the image handler..
24703     },
24704     
24705     updateHiddenEl : function()
24706     {
24707         this.validate();
24708         if (!this.hiddenEl) {
24709             return;
24710         }
24711         var ar = [];
24712         var idField = this.combo.valueField;
24713         
24714         this.items.each(function(f) {
24715             ar.push(f.data[idField]);
24716            
24717         });
24718         this.hiddenEl.dom.value = ar.join(',');
24719         this.validate();
24720     },
24721     
24722     reset : function()
24723     {
24724         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24725         this.items.each(function(f) {
24726            f.remove(); 
24727         });
24728         this.el.dom.value = '';
24729         if (this.hiddenEl) {
24730             this.hiddenEl.dom.value = '';
24731         }
24732         
24733     },
24734     getValue: function()
24735     {
24736         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24737     },
24738     setValue: function(v) // not a valid action - must use addItems..
24739     {
24740          
24741         this.reset();
24742         
24743         
24744         
24745         if (this.store.isLocal && (typeof(v) == 'string')) {
24746             // then we can use the store to find the values..
24747             // comma seperated at present.. this needs to allow JSON based encoding..
24748             this.hiddenEl.value  = v;
24749             var v_ar = [];
24750             Roo.each(v.split(','), function(k) {
24751                 Roo.log("CHECK " + this.valueField + ',' + k);
24752                 var li = this.store.query(this.valueField, k);
24753                 if (!li.length) {
24754                     return;
24755                 }
24756                 add = {};
24757                 add[this.valueField] = k;
24758                 add[this.displayField] = li.item(0).data[this.displayField];
24759                 
24760                 this.addItem(add);
24761             }, this) 
24762              
24763         }
24764         if (typeof(v) == 'object') {
24765             // then let's assume it's an array of objects..
24766             Roo.each(v, function(l) {
24767                 this.addItem(l);
24768             }, this);
24769              
24770         }
24771         
24772         
24773     },
24774     setFromData: function(v)
24775     {
24776         // this recieves an object, if setValues is called.
24777         this.reset();
24778         this.el.dom.value = v[this.displayField];
24779         this.hiddenEl.dom.value = v[this.valueField];
24780         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24781             return;
24782         }
24783         var kv = v[this.valueField];
24784         var dv = v[this.displayField];
24785         kv = typeof(kv) != 'string' ? '' : kv;
24786         dv = typeof(dv) != 'string' ? '' : dv;
24787         
24788         
24789         var keys = kv.split(',');
24790         var display = dv.split(',');
24791         for (var i = 0 ; i < keys.length; i++) {
24792             
24793             add = {};
24794             add[this.valueField] = keys[i];
24795             add[this.displayField] = display[i];
24796             this.addItem(add);
24797         }
24798       
24799         
24800     },
24801     
24802     
24803     validateValue : function(value){
24804         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24805         
24806     }
24807     
24808 });
24809
24810
24811
24812 /**
24813  * @class Roo.form.ComboBoxArray.Item
24814  * @extends Roo.BoxComponent
24815  * A selected item in the list
24816  *  Fred [x]  Brian [x]  [Pick another |v]
24817  * 
24818  * @constructor
24819  * Create a new item.
24820  * @param {Object} config Configuration options
24821  */
24822  
24823 Roo.form.ComboBoxArray.Item = function(config) {
24824     config.id = Roo.id();
24825     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24826 }
24827
24828 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24829     data : {},
24830     cb: false,
24831     displayField : false,
24832     tipField : false,
24833     
24834     
24835     defaultAutoCreate : {
24836         tag: 'div',
24837         cls: 'x-cbarray-item',
24838         cn : [ 
24839             { tag: 'div' },
24840             {
24841                 tag: 'img',
24842                 width:16,
24843                 height : 16,
24844                 src : Roo.BLANK_IMAGE_URL ,
24845                 align: 'center'
24846             }
24847         ]
24848         
24849     },
24850     
24851  
24852     onRender : function(ct, position)
24853     {
24854         Roo.form.Field.superclass.onRender.call(this, ct, position);
24855         
24856         if(!this.el){
24857             var cfg = this.getAutoCreate();
24858             this.el = ct.createChild(cfg, position);
24859         }
24860         
24861         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24862         
24863         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24864             this.cb.renderer(this.data) :
24865             String.format('{0}',this.data[this.displayField]);
24866         
24867             
24868         this.el.child('div').dom.setAttribute('qtip',
24869                         String.format('{0}',this.data[this.tipField])
24870         );
24871         
24872         this.el.child('img').on('click', this.remove, this);
24873         
24874     },
24875    
24876     remove : function()
24877     {
24878         
24879         this.cb.items.remove(this);
24880         this.el.child('img').un('click', this.remove, this);
24881         this.el.remove();
24882         this.cb.updateHiddenEl();
24883     }
24884     
24885     
24886 });/*
24887  * Based on:
24888  * Ext JS Library 1.1.1
24889  * Copyright(c) 2006-2007, Ext JS, LLC.
24890  *
24891  * Originally Released Under LGPL - original licence link has changed is not relivant.
24892  *
24893  * Fork - LGPL
24894  * <script type="text/javascript">
24895  */
24896 /**
24897  * @class Roo.form.Checkbox
24898  * @extends Roo.form.Field
24899  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24900  * @constructor
24901  * Creates a new Checkbox
24902  * @param {Object} config Configuration options
24903  */
24904 Roo.form.Checkbox = function(config){
24905     Roo.form.Checkbox.superclass.constructor.call(this, config);
24906     this.addEvents({
24907         /**
24908          * @event check
24909          * Fires when the checkbox is checked or unchecked.
24910              * @param {Roo.form.Checkbox} this This checkbox
24911              * @param {Boolean} checked The new checked value
24912              */
24913         check : true
24914     });
24915 };
24916
24917 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24918     /**
24919      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24920      */
24921     focusClass : undefined,
24922     /**
24923      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24924      */
24925     fieldClass: "x-form-field",
24926     /**
24927      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24928      */
24929     checked: false,
24930     /**
24931      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24932      * {tag: "input", type: "checkbox", autocomplete: "off"})
24933      */
24934     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24935     /**
24936      * @cfg {String} boxLabel The text that appears beside the checkbox
24937      */
24938     boxLabel : "",
24939     /**
24940      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24941      */  
24942     inputValue : '1',
24943     /**
24944      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24945      */
24946      valueOff: '0', // value when not checked..
24947
24948     actionMode : 'viewEl', 
24949     //
24950     // private
24951     itemCls : 'x-menu-check-item x-form-item',
24952     groupClass : 'x-menu-group-item',
24953     inputType : 'hidden',
24954     
24955     
24956     inSetChecked: false, // check that we are not calling self...
24957     
24958     inputElement: false, // real input element?
24959     basedOn: false, // ????
24960     
24961     isFormField: true, // not sure where this is needed!!!!
24962
24963     onResize : function(){
24964         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24965         if(!this.boxLabel){
24966             this.el.alignTo(this.wrap, 'c-c');
24967         }
24968     },
24969
24970     initEvents : function(){
24971         Roo.form.Checkbox.superclass.initEvents.call(this);
24972         this.el.on("click", this.onClick,  this);
24973         this.el.on("change", this.onClick,  this);
24974     },
24975
24976
24977     getResizeEl : function(){
24978         return this.wrap;
24979     },
24980
24981     getPositionEl : function(){
24982         return this.wrap;
24983     },
24984
24985     // private
24986     onRender : function(ct, position){
24987         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24988         /*
24989         if(this.inputValue !== undefined){
24990             this.el.dom.value = this.inputValue;
24991         }
24992         */
24993         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24994         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24995         var viewEl = this.wrap.createChild({ 
24996             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24997         this.viewEl = viewEl;   
24998         this.wrap.on('click', this.onClick,  this); 
24999         
25000         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25001         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25002         
25003         
25004         
25005         if(this.boxLabel){
25006             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25007         //    viewEl.on('click', this.onClick,  this); 
25008         }
25009         //if(this.checked){
25010             this.setChecked(this.checked);
25011         //}else{
25012             //this.checked = this.el.dom;
25013         //}
25014
25015     },
25016
25017     // private
25018     initValue : Roo.emptyFn,
25019
25020     /**
25021      * Returns the checked state of the checkbox.
25022      * @return {Boolean} True if checked, else false
25023      */
25024     getValue : function(){
25025         if(this.el){
25026             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25027         }
25028         return this.valueOff;
25029         
25030     },
25031
25032         // private
25033     onClick : function(){ 
25034         this.setChecked(!this.checked);
25035
25036         //if(this.el.dom.checked != this.checked){
25037         //    this.setValue(this.el.dom.checked);
25038        // }
25039     },
25040
25041     /**
25042      * Sets the checked state of the checkbox.
25043      * On is always based on a string comparison between inputValue and the param.
25044      * @param {Boolean/String} value - the value to set 
25045      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25046      */
25047     setValue : function(v,suppressEvent){
25048         
25049         
25050         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25051         //if(this.el && this.el.dom){
25052         //    this.el.dom.checked = this.checked;
25053         //    this.el.dom.defaultChecked = this.checked;
25054         //}
25055         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25056         //this.fireEvent("check", this, this.checked);
25057     },
25058     // private..
25059     setChecked : function(state,suppressEvent)
25060     {
25061         if (this.inSetChecked) {
25062             this.checked = state;
25063             return;
25064         }
25065         
25066     
25067         if(this.wrap){
25068             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25069         }
25070         this.checked = state;
25071         if(suppressEvent !== true){
25072             this.fireEvent('check', this, state);
25073         }
25074         this.inSetChecked = true;
25075         this.el.dom.value = state ? this.inputValue : this.valueOff;
25076         this.inSetChecked = false;
25077         
25078     },
25079     // handle setting of hidden value by some other method!!?!?
25080     setFromHidden: function()
25081     {
25082         if(!this.el){
25083             return;
25084         }
25085         //console.log("SET FROM HIDDEN");
25086         //alert('setFrom hidden');
25087         this.setValue(this.el.dom.value);
25088     },
25089     
25090     onDestroy : function()
25091     {
25092         if(this.viewEl){
25093             Roo.get(this.viewEl).remove();
25094         }
25095          
25096         Roo.form.Checkbox.superclass.onDestroy.call(this);
25097     }
25098
25099 });/*
25100  * Based on:
25101  * Ext JS Library 1.1.1
25102  * Copyright(c) 2006-2007, Ext JS, LLC.
25103  *
25104  * Originally Released Under LGPL - original licence link has changed is not relivant.
25105  *
25106  * Fork - LGPL
25107  * <script type="text/javascript">
25108  */
25109  
25110 /**
25111  * @class Roo.form.Radio
25112  * @extends Roo.form.Checkbox
25113  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25114  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25115  * @constructor
25116  * Creates a new Radio
25117  * @param {Object} config Configuration options
25118  */
25119 Roo.form.Radio = function(){
25120     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25121 };
25122 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25123     inputType: 'radio',
25124
25125     /**
25126      * If this radio is part of a group, it will return the selected value
25127      * @return {String}
25128      */
25129     getGroupValue : function(){
25130         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25131     }
25132 });//<script type="text/javascript">
25133
25134 /*
25135  * Ext JS Library 1.1.1
25136  * Copyright(c) 2006-2007, Ext JS, LLC.
25137  * licensing@extjs.com
25138  * 
25139  * http://www.extjs.com/license
25140  */
25141  
25142  /*
25143   * 
25144   * Known bugs:
25145   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25146   * - IE ? - no idea how much works there.
25147   * 
25148   * 
25149   * 
25150   */
25151  
25152
25153 /**
25154  * @class Ext.form.HtmlEditor
25155  * @extends Ext.form.Field
25156  * Provides a lightweight HTML Editor component.
25157  *
25158  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25159  * 
25160  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25161  * supported by this editor.</b><br/><br/>
25162  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25163  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25164  */
25165 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25166       /**
25167      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25168      */
25169     toolbars : false,
25170     /**
25171      * @cfg {String} createLinkText The default text for the create link prompt
25172      */
25173     createLinkText : 'Please enter the URL for the link:',
25174     /**
25175      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25176      */
25177     defaultLinkValue : 'http:/'+'/',
25178    
25179      /**
25180      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25181      *                        Roo.resizable.
25182      */
25183     resizable : false,
25184      /**
25185      * @cfg {Number} height (in pixels)
25186      */   
25187     height: 300,
25188    /**
25189      * @cfg {Number} width (in pixels)
25190      */   
25191     width: 500,
25192     
25193     /**
25194      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25195      * 
25196      */
25197     stylesheets: false,
25198     
25199     // id of frame..
25200     frameId: false,
25201     
25202     // private properties
25203     validationEvent : false,
25204     deferHeight: true,
25205     initialized : false,
25206     activated : false,
25207     sourceEditMode : false,
25208     onFocus : Roo.emptyFn,
25209     iframePad:3,
25210     hideMode:'offsets',
25211     
25212     defaultAutoCreate : { // modified by initCompnoent..
25213         tag: "textarea",
25214         style:"width:500px;height:300px;",
25215         autocomplete: "off"
25216     },
25217
25218     // private
25219     initComponent : function(){
25220         this.addEvents({
25221             /**
25222              * @event initialize
25223              * Fires when the editor is fully initialized (including the iframe)
25224              * @param {HtmlEditor} this
25225              */
25226             initialize: true,
25227             /**
25228              * @event activate
25229              * Fires when the editor is first receives the focus. Any insertion must wait
25230              * until after this event.
25231              * @param {HtmlEditor} this
25232              */
25233             activate: true,
25234              /**
25235              * @event beforesync
25236              * Fires before the textarea is updated with content from the editor iframe. Return false
25237              * to cancel the sync.
25238              * @param {HtmlEditor} this
25239              * @param {String} html
25240              */
25241             beforesync: true,
25242              /**
25243              * @event beforepush
25244              * Fires before the iframe editor is updated with content from the textarea. Return false
25245              * to cancel the push.
25246              * @param {HtmlEditor} this
25247              * @param {String} html
25248              */
25249             beforepush: true,
25250              /**
25251              * @event sync
25252              * Fires when the textarea is updated with content from the editor iframe.
25253              * @param {HtmlEditor} this
25254              * @param {String} html
25255              */
25256             sync: true,
25257              /**
25258              * @event push
25259              * Fires when the iframe editor is updated with content from the textarea.
25260              * @param {HtmlEditor} this
25261              * @param {String} html
25262              */
25263             push: true,
25264              /**
25265              * @event editmodechange
25266              * Fires when the editor switches edit modes
25267              * @param {HtmlEditor} this
25268              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25269              */
25270             editmodechange: true,
25271             /**
25272              * @event editorevent
25273              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25274              * @param {HtmlEditor} this
25275              */
25276             editorevent: true
25277         });
25278         this.defaultAutoCreate =  {
25279             tag: "textarea",
25280             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25281             autocomplete: "off"
25282         };
25283     },
25284
25285     /**
25286      * Protected method that will not generally be called directly. It
25287      * is called when the editor creates its toolbar. Override this method if you need to
25288      * add custom toolbar buttons.
25289      * @param {HtmlEditor} editor
25290      */
25291     createToolbar : function(editor){
25292         if (!editor.toolbars || !editor.toolbars.length) {
25293             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25294         }
25295         
25296         for (var i =0 ; i < editor.toolbars.length;i++) {
25297             editor.toolbars[i] = Roo.factory(
25298                     typeof(editor.toolbars[i]) == 'string' ?
25299                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25300                 Roo.form.HtmlEditor);
25301             editor.toolbars[i].init(editor);
25302         }
25303          
25304         
25305     },
25306
25307     /**
25308      * Protected method that will not generally be called directly. It
25309      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25310      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25311      */
25312     getDocMarkup : function(){
25313         // body styles..
25314         var st = '';
25315         if (this.stylesheets === false) {
25316             
25317             Roo.get(document.head).select('style').each(function(node) {
25318                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25319             });
25320             
25321             Roo.get(document.head).select('link').each(function(node) { 
25322                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25323             });
25324             
25325         } else if (!this.stylesheets.length) {
25326                 // simple..
25327                 st = '<style type="text/css">' +
25328                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25329                    '</style>';
25330         } else {
25331             Roo.each(this.stylesheets, function(s) {
25332                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25333             });
25334             
25335         }
25336         
25337         st +=  '<style type="text/css">' +
25338             'IMG { cursor: pointer } ' +
25339         '</style>';
25340
25341         
25342         return '<html><head>' + st  +
25343             //<style type="text/css">' +
25344             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25345             //'</style>' +
25346             ' </head><body class="roo-htmleditor-body"></body></html>';
25347     },
25348
25349     // private
25350     onRender : function(ct, position)
25351     {
25352         var _t = this;
25353         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25354         this.el.dom.style.border = '0 none';
25355         this.el.dom.setAttribute('tabIndex', -1);
25356         this.el.addClass('x-hidden');
25357         if(Roo.isIE){ // fix IE 1px bogus margin
25358             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25359         }
25360         this.wrap = this.el.wrap({
25361             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25362         });
25363         
25364         if (this.resizable) {
25365             this.resizeEl = new Roo.Resizable(this.wrap, {
25366                 pinned : true,
25367                 wrap: true,
25368                 dynamic : true,
25369                 minHeight : this.height,
25370                 height: this.height,
25371                 handles : this.resizable,
25372                 width: this.width,
25373                 listeners : {
25374                     resize : function(r, w, h) {
25375                         _t.onResize(w,h); // -something
25376                     }
25377                 }
25378             });
25379             
25380         }
25381
25382         this.frameId = Roo.id();
25383         
25384         this.createToolbar(this);
25385         
25386       
25387         
25388         var iframe = this.wrap.createChild({
25389             tag: 'iframe',
25390             id: this.frameId,
25391             name: this.frameId,
25392             frameBorder : 'no',
25393             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25394         }, this.el
25395         );
25396         
25397        // console.log(iframe);
25398         //this.wrap.dom.appendChild(iframe);
25399
25400         this.iframe = iframe.dom;
25401
25402          this.assignDocWin();
25403         
25404         this.doc.designMode = 'on';
25405        
25406         this.doc.open();
25407         this.doc.write(this.getDocMarkup());
25408         this.doc.close();
25409
25410         
25411         var task = { // must defer to wait for browser to be ready
25412             run : function(){
25413                 //console.log("run task?" + this.doc.readyState);
25414                 this.assignDocWin();
25415                 if(this.doc.body || this.doc.readyState == 'complete'){
25416                     try {
25417                         this.doc.designMode="on";
25418                     } catch (e) {
25419                         return;
25420                     }
25421                     Roo.TaskMgr.stop(task);
25422                     this.initEditor.defer(10, this);
25423                 }
25424             },
25425             interval : 10,
25426             duration:10000,
25427             scope: this
25428         };
25429         Roo.TaskMgr.start(task);
25430
25431         if(!this.width){
25432             this.setSize(this.wrap.getSize());
25433         }
25434         if (this.resizeEl) {
25435             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25436             // should trigger onReize..
25437         }
25438     },
25439
25440     // private
25441     onResize : function(w, h)
25442     {
25443         //Roo.log('resize: ' +w + ',' + h );
25444         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25445         if(this.el && this.iframe){
25446             if(typeof w == 'number'){
25447                 var aw = w - this.wrap.getFrameWidth('lr');
25448                 this.el.setWidth(this.adjustWidth('textarea', aw));
25449                 this.iframe.style.width = aw + 'px';
25450             }
25451             if(typeof h == 'number'){
25452                 var tbh = 0;
25453                 for (var i =0; i < this.toolbars.length;i++) {
25454                     // fixme - ask toolbars for heights?
25455                     tbh += this.toolbars[i].tb.el.getHeight();
25456                     if (this.toolbars[i].footer) {
25457                         tbh += this.toolbars[i].footer.el.getHeight();
25458                     }
25459                 }
25460                 
25461                 
25462                 
25463                 
25464                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25465                 ah -= 5; // knock a few pixes off for look..
25466                 this.el.setHeight(this.adjustWidth('textarea', ah));
25467                 this.iframe.style.height = ah + 'px';
25468                 if(this.doc){
25469                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25470                 }
25471             }
25472         }
25473     },
25474
25475     /**
25476      * Toggles the editor between standard and source edit mode.
25477      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25478      */
25479     toggleSourceEdit : function(sourceEditMode){
25480         
25481         this.sourceEditMode = sourceEditMode === true;
25482         
25483         if(this.sourceEditMode){
25484 //            Roo.log('in');
25485 //            Roo.log(this.syncValue());
25486             this.syncValue();
25487             this.iframe.className = 'x-hidden';
25488             this.el.removeClass('x-hidden');
25489             this.el.dom.removeAttribute('tabIndex');
25490             this.el.focus();
25491         }else{
25492 //            Roo.log('out')
25493 //            Roo.log(this.pushValue()); 
25494             this.pushValue();
25495             this.iframe.className = '';
25496             this.el.addClass('x-hidden');
25497             this.el.dom.setAttribute('tabIndex', -1);
25498             this.deferFocus();
25499         }
25500         this.setSize(this.wrap.getSize());
25501         this.fireEvent('editmodechange', this, this.sourceEditMode);
25502     },
25503
25504     // private used internally
25505     createLink : function(){
25506         var url = prompt(this.createLinkText, this.defaultLinkValue);
25507         if(url && url != 'http:/'+'/'){
25508             this.relayCmd('createlink', url);
25509         }
25510     },
25511
25512     // private (for BoxComponent)
25513     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25514
25515     // private (for BoxComponent)
25516     getResizeEl : function(){
25517         return this.wrap;
25518     },
25519
25520     // private (for BoxComponent)
25521     getPositionEl : function(){
25522         return this.wrap;
25523     },
25524
25525     // private
25526     initEvents : function(){
25527         this.originalValue = this.getValue();
25528     },
25529
25530     /**
25531      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25532      * @method
25533      */
25534     markInvalid : Roo.emptyFn,
25535     /**
25536      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25537      * @method
25538      */
25539     clearInvalid : Roo.emptyFn,
25540
25541     setValue : function(v){
25542         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25543         this.pushValue();
25544     },
25545
25546     /**
25547      * Protected method that will not generally be called directly. If you need/want
25548      * custom HTML cleanup, this is the method you should override.
25549      * @param {String} html The HTML to be cleaned
25550      * return {String} The cleaned HTML
25551      */
25552     cleanHtml : function(html){
25553         html = String(html);
25554         if(html.length > 5){
25555             if(Roo.isSafari){ // strip safari nonsense
25556                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25557             }
25558         }
25559         if(html == '&nbsp;'){
25560             html = '';
25561         }
25562         return html;
25563     },
25564
25565     /**
25566      * Protected method that will not generally be called directly. Syncs the contents
25567      * of the editor iframe with the textarea.
25568      */
25569     syncValue : function(){
25570         if(this.initialized){
25571             var bd = (this.doc.body || this.doc.documentElement);
25572             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25573             var html = bd.innerHTML;
25574             if(Roo.isSafari){
25575                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25576                 var m = bs.match(/text-align:(.*?);/i);
25577                 if(m && m[1]){
25578                     html = '<div style="'+m[0]+'">' + html + '</div>';
25579                 }
25580             }
25581             html = this.cleanHtml(html);
25582             // fix up the special chars.. normaly like back quotes in word...
25583             // however we do not want to do this with chinese..
25584             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25585                 var cc = b.charCodeAt();
25586                 if (
25587                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25588                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25589                     (cc >= 0xf900 && cc < 0xfb00 )
25590                 ) {
25591                         return b;
25592                 }
25593                 return "&#"+cc+";" 
25594             });
25595             if(this.fireEvent('beforesync', this, html) !== false){
25596                 this.el.dom.value = html;
25597                 this.fireEvent('sync', this, html);
25598             }
25599         }
25600     },
25601
25602     /**
25603      * Protected method that will not generally be called directly. Pushes the value of the textarea
25604      * into the iframe editor.
25605      */
25606     pushValue : function(){
25607         if(this.initialized){
25608             var v = this.el.dom.value;
25609             
25610             if(v.length < 1){
25611                 v = '&#160;';
25612             }
25613             
25614             if(this.fireEvent('beforepush', this, v) !== false){
25615                 var d = (this.doc.body || this.doc.documentElement);
25616                 d.innerHTML = v;
25617                 this.cleanUpPaste();
25618                 this.el.dom.value = d.innerHTML;
25619                 this.fireEvent('push', this, v);
25620             }
25621         }
25622     },
25623
25624     // private
25625     deferFocus : function(){
25626         this.focus.defer(10, this);
25627     },
25628
25629     // doc'ed in Field
25630     focus : function(){
25631         if(this.win && !this.sourceEditMode){
25632             this.win.focus();
25633         }else{
25634             this.el.focus();
25635         }
25636     },
25637     
25638     assignDocWin: function()
25639     {
25640         var iframe = this.iframe;
25641         
25642          if(Roo.isIE){
25643             this.doc = iframe.contentWindow.document;
25644             this.win = iframe.contentWindow;
25645         } else {
25646             if (!Roo.get(this.frameId)) {
25647                 return;
25648             }
25649             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25650             this.win = Roo.get(this.frameId).dom.contentWindow;
25651         }
25652     },
25653     
25654     // private
25655     initEditor : function(){
25656         //console.log("INIT EDITOR");
25657         this.assignDocWin();
25658         
25659         
25660         
25661         this.doc.designMode="on";
25662         this.doc.open();
25663         this.doc.write(this.getDocMarkup());
25664         this.doc.close();
25665         
25666         var dbody = (this.doc.body || this.doc.documentElement);
25667         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25668         // this copies styles from the containing element into thsi one..
25669         // not sure why we need all of this..
25670         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25671         ss['background-attachment'] = 'fixed'; // w3c
25672         dbody.bgProperties = 'fixed'; // ie
25673         Roo.DomHelper.applyStyles(dbody, ss);
25674         Roo.EventManager.on(this.doc, {
25675             //'mousedown': this.onEditorEvent,
25676             'mouseup': this.onEditorEvent,
25677             'dblclick': this.onEditorEvent,
25678             'click': this.onEditorEvent,
25679             'keyup': this.onEditorEvent,
25680             buffer:100,
25681             scope: this
25682         });
25683         if(Roo.isGecko){
25684             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25685         }
25686         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25687             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25688         }
25689         this.initialized = true;
25690
25691         this.fireEvent('initialize', this);
25692         this.pushValue();
25693     },
25694
25695     // private
25696     onDestroy : function(){
25697         
25698         
25699         
25700         if(this.rendered){
25701             
25702             for (var i =0; i < this.toolbars.length;i++) {
25703                 // fixme - ask toolbars for heights?
25704                 this.toolbars[i].onDestroy();
25705             }
25706             
25707             this.wrap.dom.innerHTML = '';
25708             this.wrap.remove();
25709         }
25710     },
25711
25712     // private
25713     onFirstFocus : function(){
25714         
25715         this.assignDocWin();
25716         
25717         
25718         this.activated = true;
25719         for (var i =0; i < this.toolbars.length;i++) {
25720             this.toolbars[i].onFirstFocus();
25721         }
25722        
25723         if(Roo.isGecko){ // prevent silly gecko errors
25724             this.win.focus();
25725             var s = this.win.getSelection();
25726             if(!s.focusNode || s.focusNode.nodeType != 3){
25727                 var r = s.getRangeAt(0);
25728                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25729                 r.collapse(true);
25730                 this.deferFocus();
25731             }
25732             try{
25733                 this.execCmd('useCSS', true);
25734                 this.execCmd('styleWithCSS', false);
25735             }catch(e){}
25736         }
25737         this.fireEvent('activate', this);
25738     },
25739
25740     // private
25741     adjustFont: function(btn){
25742         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25743         //if(Roo.isSafari){ // safari
25744         //    adjust *= 2;
25745        // }
25746         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25747         if(Roo.isSafari){ // safari
25748             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25749             v =  (v < 10) ? 10 : v;
25750             v =  (v > 48) ? 48 : v;
25751             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25752             
25753         }
25754         
25755         
25756         v = Math.max(1, v+adjust);
25757         
25758         this.execCmd('FontSize', v  );
25759     },
25760
25761     onEditorEvent : function(e){
25762         this.fireEvent('editorevent', this, e);
25763       //  this.updateToolbar();
25764         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25765     },
25766
25767     insertTag : function(tg)
25768     {
25769         // could be a bit smarter... -> wrap the current selected tRoo..
25770         if (tg.toLowerCase() == 'span') {
25771             
25772             range = this.createRange(this.getSelection());
25773             var wrappingNode = this.doc.createElement("span");
25774             wrappingNode.appendChild(range.extractContents());
25775             range.insertNode(wrappingNode);
25776
25777             return;
25778             
25779             
25780             
25781         }
25782         this.execCmd("formatblock",   tg);
25783         
25784     },
25785     
25786     insertText : function(txt)
25787     {
25788         
25789         
25790         var range = this.createRange();
25791         range.deleteContents();
25792                //alert(Sender.getAttribute('label'));
25793                
25794         range.insertNode(this.doc.createTextNode(txt));
25795     } ,
25796     
25797     // private
25798     relayBtnCmd : function(btn){
25799         this.relayCmd(btn.cmd);
25800     },
25801
25802     /**
25803      * Executes a Midas editor command on the editor document and performs necessary focus and
25804      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25805      * @param {String} cmd The Midas command
25806      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25807      */
25808     relayCmd : function(cmd, value){
25809         this.win.focus();
25810         this.execCmd(cmd, value);
25811         this.fireEvent('editorevent', this);
25812         //this.updateToolbar();
25813         this.deferFocus();
25814     },
25815
25816     /**
25817      * Executes a Midas editor command directly on the editor document.
25818      * For visual commands, you should use {@link #relayCmd} instead.
25819      * <b>This should only be called after the editor is initialized.</b>
25820      * @param {String} cmd The Midas command
25821      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25822      */
25823     execCmd : function(cmd, value){
25824         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25825         this.syncValue();
25826     },
25827  
25828  
25829    
25830     /**
25831      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25832      * to insert tRoo.
25833      * @param {String} text | dom node.. 
25834      */
25835     insertAtCursor : function(text)
25836     {
25837         
25838         
25839         
25840         if(!this.activated){
25841             return;
25842         }
25843         /*
25844         if(Roo.isIE){
25845             this.win.focus();
25846             var r = this.doc.selection.createRange();
25847             if(r){
25848                 r.collapse(true);
25849                 r.pasteHTML(text);
25850                 this.syncValue();
25851                 this.deferFocus();
25852             
25853             }
25854             return;
25855         }
25856         */
25857         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25858             this.win.focus();
25859             
25860             
25861             // from jquery ui (MIT licenced)
25862             var range, node;
25863             var win = this.win;
25864             
25865             if (win.getSelection && win.getSelection().getRangeAt) {
25866                 range = win.getSelection().getRangeAt(0);
25867                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25868                 range.insertNode(node);
25869             } else if (win.document.selection && win.document.selection.createRange) {
25870                 // no firefox support
25871                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25872                 win.document.selection.createRange().pasteHTML(txt);
25873             } else {
25874                 // no firefox support
25875                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25876                 this.execCmd('InsertHTML', txt);
25877             } 
25878             
25879             this.syncValue();
25880             
25881             this.deferFocus();
25882         }
25883     },
25884  // private
25885     mozKeyPress : function(e){
25886         if(e.ctrlKey){
25887             var c = e.getCharCode(), cmd;
25888           
25889             if(c > 0){
25890                 c = String.fromCharCode(c).toLowerCase();
25891                 switch(c){
25892                     case 'b':
25893                         cmd = 'bold';
25894                         break;
25895                     case 'i':
25896                         cmd = 'italic';
25897                         break;
25898                     
25899                     case 'u':
25900                         cmd = 'underline';
25901                         break;
25902                     
25903                     case 'v':
25904                         this.cleanUpPaste.defer(100, this);
25905                         return;
25906                         
25907                 }
25908                 if(cmd){
25909                     this.win.focus();
25910                     this.execCmd(cmd);
25911                     this.deferFocus();
25912                     e.preventDefault();
25913                 }
25914                 
25915             }
25916         }
25917     },
25918
25919     // private
25920     fixKeys : function(){ // load time branching for fastest keydown performance
25921         if(Roo.isIE){
25922             return function(e){
25923                 var k = e.getKey(), r;
25924                 if(k == e.TAB){
25925                     e.stopEvent();
25926                     r = this.doc.selection.createRange();
25927                     if(r){
25928                         r.collapse(true);
25929                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25930                         this.deferFocus();
25931                     }
25932                     return;
25933                 }
25934                 
25935                 if(k == e.ENTER){
25936                     r = this.doc.selection.createRange();
25937                     if(r){
25938                         var target = r.parentElement();
25939                         if(!target || target.tagName.toLowerCase() != 'li'){
25940                             e.stopEvent();
25941                             r.pasteHTML('<br />');
25942                             r.collapse(false);
25943                             r.select();
25944                         }
25945                     }
25946                 }
25947                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25948                     this.cleanUpPaste.defer(100, this);
25949                     return;
25950                 }
25951                 
25952                 
25953             };
25954         }else if(Roo.isOpera){
25955             return function(e){
25956                 var k = e.getKey();
25957                 if(k == e.TAB){
25958                     e.stopEvent();
25959                     this.win.focus();
25960                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25961                     this.deferFocus();
25962                 }
25963                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25964                     this.cleanUpPaste.defer(100, this);
25965                     return;
25966                 }
25967                 
25968             };
25969         }else if(Roo.isSafari){
25970             return function(e){
25971                 var k = e.getKey();
25972                 
25973                 if(k == e.TAB){
25974                     e.stopEvent();
25975                     this.execCmd('InsertText','\t');
25976                     this.deferFocus();
25977                     return;
25978                 }
25979                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25980                     this.cleanUpPaste.defer(100, this);
25981                     return;
25982                 }
25983                 
25984              };
25985         }
25986     }(),
25987     
25988     getAllAncestors: function()
25989     {
25990         var p = this.getSelectedNode();
25991         var a = [];
25992         if (!p) {
25993             a.push(p); // push blank onto stack..
25994             p = this.getParentElement();
25995         }
25996         
25997         
25998         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25999             a.push(p);
26000             p = p.parentNode;
26001         }
26002         a.push(this.doc.body);
26003         return a;
26004     },
26005     lastSel : false,
26006     lastSelNode : false,
26007     
26008     
26009     getSelection : function() 
26010     {
26011         this.assignDocWin();
26012         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26013     },
26014     
26015     getSelectedNode: function() 
26016     {
26017         // this may only work on Gecko!!!
26018         
26019         // should we cache this!!!!
26020         
26021         
26022         
26023          
26024         var range = this.createRange(this.getSelection()).cloneRange();
26025         
26026         if (Roo.isIE) {
26027             var parent = range.parentElement();
26028             while (true) {
26029                 var testRange = range.duplicate();
26030                 testRange.moveToElementText(parent);
26031                 if (testRange.inRange(range)) {
26032                     break;
26033                 }
26034                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26035                     break;
26036                 }
26037                 parent = parent.parentElement;
26038             }
26039             return parent;
26040         }
26041         
26042         // is ancestor a text element.
26043         var ac =  range.commonAncestorContainer;
26044         if (ac.nodeType == 3) {
26045             ac = ac.parentNode;
26046         }
26047         
26048         var ar = ac.childNodes;
26049          
26050         var nodes = [];
26051         var other_nodes = [];
26052         var has_other_nodes = false;
26053         for (var i=0;i<ar.length;i++) {
26054             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26055                 continue;
26056             }
26057             // fullly contained node.
26058             
26059             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26060                 nodes.push(ar[i]);
26061                 continue;
26062             }
26063             
26064             // probably selected..
26065             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26066                 other_nodes.push(ar[i]);
26067                 continue;
26068             }
26069             // outer..
26070             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26071                 continue;
26072             }
26073             
26074             
26075             has_other_nodes = true;
26076         }
26077         if (!nodes.length && other_nodes.length) {
26078             nodes= other_nodes;
26079         }
26080         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26081             return false;
26082         }
26083         
26084         return nodes[0];
26085     },
26086     createRange: function(sel)
26087     {
26088         // this has strange effects when using with 
26089         // top toolbar - not sure if it's a great idea.
26090         //this.editor.contentWindow.focus();
26091         if (typeof sel != "undefined") {
26092             try {
26093                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26094             } catch(e) {
26095                 return this.doc.createRange();
26096             }
26097         } else {
26098             return this.doc.createRange();
26099         }
26100     },
26101     getParentElement: function()
26102     {
26103         
26104         this.assignDocWin();
26105         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26106         
26107         var range = this.createRange(sel);
26108          
26109         try {
26110             var p = range.commonAncestorContainer;
26111             while (p.nodeType == 3) { // text node
26112                 p = p.parentNode;
26113             }
26114             return p;
26115         } catch (e) {
26116             return null;
26117         }
26118     
26119     },
26120     /***
26121      *
26122      * Range intersection.. the hard stuff...
26123      *  '-1' = before
26124      *  '0' = hits..
26125      *  '1' = after.
26126      *         [ -- selected range --- ]
26127      *   [fail]                        [fail]
26128      *
26129      *    basically..
26130      *      if end is before start or  hits it. fail.
26131      *      if start is after end or hits it fail.
26132      *
26133      *   if either hits (but other is outside. - then it's not 
26134      *   
26135      *    
26136      **/
26137     
26138     
26139     // @see http://www.thismuchiknow.co.uk/?p=64.
26140     rangeIntersectsNode : function(range, node)
26141     {
26142         var nodeRange = node.ownerDocument.createRange();
26143         try {
26144             nodeRange.selectNode(node);
26145         } catch (e) {
26146             nodeRange.selectNodeContents(node);
26147         }
26148     
26149         var rangeStartRange = range.cloneRange();
26150         rangeStartRange.collapse(true);
26151     
26152         var rangeEndRange = range.cloneRange();
26153         rangeEndRange.collapse(false);
26154     
26155         var nodeStartRange = nodeRange.cloneRange();
26156         nodeStartRange.collapse(true);
26157     
26158         var nodeEndRange = nodeRange.cloneRange();
26159         nodeEndRange.collapse(false);
26160     
26161         return rangeStartRange.compareBoundaryPoints(
26162                  Range.START_TO_START, nodeEndRange) == -1 &&
26163                rangeEndRange.compareBoundaryPoints(
26164                  Range.START_TO_START, nodeStartRange) == 1;
26165         
26166          
26167     },
26168     rangeCompareNode : function(range, node)
26169     {
26170         var nodeRange = node.ownerDocument.createRange();
26171         try {
26172             nodeRange.selectNode(node);
26173         } catch (e) {
26174             nodeRange.selectNodeContents(node);
26175         }
26176         
26177         
26178         range.collapse(true);
26179     
26180         nodeRange.collapse(true);
26181      
26182         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26183         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26184          
26185         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26186         
26187         var nodeIsBefore   =  ss == 1;
26188         var nodeIsAfter    = ee == -1;
26189         
26190         if (nodeIsBefore && nodeIsAfter)
26191             return 0; // outer
26192         if (!nodeIsBefore && nodeIsAfter)
26193             return 1; //right trailed.
26194         
26195         if (nodeIsBefore && !nodeIsAfter)
26196             return 2;  // left trailed.
26197         // fully contined.
26198         return 3;
26199     },
26200
26201     // private? - in a new class?
26202     cleanUpPaste :  function()
26203     {
26204         // cleans up the whole document..
26205          Roo.log('cleanuppaste');
26206         this.cleanUpChildren(this.doc.body);
26207         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26208         if (clean != this.doc.body.innerHTML) {
26209             this.doc.body.innerHTML = clean;
26210         }
26211         
26212     },
26213     
26214     cleanWordChars : function(input) {// change the chars to hex code
26215         var he = Roo.form.HtmlEditor;
26216         
26217         var output = input;
26218         Roo.each(he.swapCodes, function(sw) { 
26219             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26220             
26221             output = output.replace(swapper, sw[1]);
26222         });
26223         
26224         return output;
26225     },
26226     
26227     
26228     cleanUpChildren : function (n)
26229     {
26230         if (!n.childNodes.length) {
26231             return;
26232         }
26233         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26234            this.cleanUpChild(n.childNodes[i]);
26235         }
26236     },
26237     
26238     
26239         
26240     
26241     cleanUpChild : function (node)
26242     {
26243         var ed = this;
26244         //console.log(node);
26245         if (node.nodeName == "#text") {
26246             // clean up silly Windows -- stuff?
26247             return; 
26248         }
26249         if (node.nodeName == "#comment") {
26250             node.parentNode.removeChild(node);
26251             // clean up silly Windows -- stuff?
26252             return; 
26253         }
26254         
26255         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26256             // remove node.
26257             node.parentNode.removeChild(node);
26258             return;
26259             
26260         }
26261         
26262         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26263         
26264         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26265         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26266         
26267         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26268         //    remove_keep_children = true;
26269         //}
26270         
26271         if (remove_keep_children) {
26272             this.cleanUpChildren(node);
26273             // inserts everything just before this node...
26274             while (node.childNodes.length) {
26275                 var cn = node.childNodes[0];
26276                 node.removeChild(cn);
26277                 node.parentNode.insertBefore(cn, node);
26278             }
26279             node.parentNode.removeChild(node);
26280             return;
26281         }
26282         
26283         if (!node.attributes || !node.attributes.length) {
26284             this.cleanUpChildren(node);
26285             return;
26286         }
26287         
26288         function cleanAttr(n,v)
26289         {
26290             
26291             if (v.match(/^\./) || v.match(/^\//)) {
26292                 return;
26293             }
26294             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26295                 return;
26296             }
26297             if (v.match(/^#/)) {
26298                 return;
26299             }
26300 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26301             node.removeAttribute(n);
26302             
26303         }
26304         
26305         function cleanStyle(n,v)
26306         {
26307             if (v.match(/expression/)) { //XSS?? should we even bother..
26308                 node.removeAttribute(n);
26309                 return;
26310             }
26311             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26312             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26313             
26314             
26315             var parts = v.split(/;/);
26316             var clean = [];
26317             
26318             Roo.each(parts, function(p) {
26319                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26320                 if (!p.length) {
26321                     return true;
26322                 }
26323                 var l = p.split(':').shift().replace(/\s+/g,'');
26324                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26325                 
26326                 
26327                 if ( cblack.indexOf(l) > -1) {
26328 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26329                     //node.removeAttribute(n);
26330                     return true;
26331                 }
26332                 //Roo.log()
26333                 // only allow 'c whitelisted system attributes'
26334                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26335 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26336                     //node.removeAttribute(n);
26337                     return true;
26338                 }
26339                 
26340                 
26341                  
26342                 
26343                 clean.push(p);
26344                 return true;
26345             });
26346             if (clean.length) { 
26347                 node.setAttribute(n, clean.join(';'));
26348             } else {
26349                 node.removeAttribute(n);
26350             }
26351             
26352         }
26353         
26354         
26355         for (var i = node.attributes.length-1; i > -1 ; i--) {
26356             var a = node.attributes[i];
26357             //console.log(a);
26358             
26359             if (a.name.toLowerCase().substr(0,2)=='on')  {
26360                 node.removeAttribute(a.name);
26361                 continue;
26362             }
26363             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26364                 node.removeAttribute(a.name);
26365                 continue;
26366             }
26367             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26368                 cleanAttr(a.name,a.value); // fixme..
26369                 continue;
26370             }
26371             if (a.name == 'style') {
26372                 cleanStyle(a.name,a.value);
26373                 continue;
26374             }
26375             /// clean up MS crap..
26376             // tecnically this should be a list of valid class'es..
26377             
26378             
26379             if (a.name == 'class') {
26380                 if (a.value.match(/^Mso/)) {
26381                     node.className = '';
26382                 }
26383                 
26384                 if (a.value.match(/body/)) {
26385                     node.className = '';
26386                 }
26387                 continue;
26388             }
26389             
26390             // style cleanup!?
26391             // class cleanup?
26392             
26393         }
26394         
26395         
26396         this.cleanUpChildren(node);
26397         
26398         
26399     }
26400     
26401     
26402     // hide stuff that is not compatible
26403     /**
26404      * @event blur
26405      * @hide
26406      */
26407     /**
26408      * @event change
26409      * @hide
26410      */
26411     /**
26412      * @event focus
26413      * @hide
26414      */
26415     /**
26416      * @event specialkey
26417      * @hide
26418      */
26419     /**
26420      * @cfg {String} fieldClass @hide
26421      */
26422     /**
26423      * @cfg {String} focusClass @hide
26424      */
26425     /**
26426      * @cfg {String} autoCreate @hide
26427      */
26428     /**
26429      * @cfg {String} inputType @hide
26430      */
26431     /**
26432      * @cfg {String} invalidClass @hide
26433      */
26434     /**
26435      * @cfg {String} invalidText @hide
26436      */
26437     /**
26438      * @cfg {String} msgFx @hide
26439      */
26440     /**
26441      * @cfg {String} validateOnBlur @hide
26442      */
26443 });
26444
26445 Roo.form.HtmlEditor.white = [
26446         'area', 'br', 'img', 'input', 'hr', 'wbr',
26447         
26448        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26449        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26450        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26451        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26452        'table',   'ul',         'xmp', 
26453        
26454        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26455       'thead',   'tr', 
26456      
26457       'dir', 'menu', 'ol', 'ul', 'dl',
26458        
26459       'embed',  'object'
26460 ];
26461
26462
26463 Roo.form.HtmlEditor.black = [
26464     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26465         'applet', // 
26466         'base',   'basefont', 'bgsound', 'blink',  'body', 
26467         'frame',  'frameset', 'head',    'html',   'ilayer', 
26468         'iframe', 'layer',  'link',     'meta',    'object',   
26469         'script', 'style' ,'title',  'xml' // clean later..
26470 ];
26471 Roo.form.HtmlEditor.clean = [
26472     'script', 'style', 'title', 'xml'
26473 ];
26474 Roo.form.HtmlEditor.remove = [
26475     'font'
26476 ];
26477 // attributes..
26478
26479 Roo.form.HtmlEditor.ablack = [
26480     'on'
26481 ];
26482     
26483 Roo.form.HtmlEditor.aclean = [ 
26484     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26485 ];
26486
26487 // protocols..
26488 Roo.form.HtmlEditor.pwhite= [
26489         'http',  'https',  'mailto'
26490 ];
26491
26492 // white listed style attributes.
26493 Roo.form.HtmlEditor.cwhite= [
26494       //  'text-align', /// default is to allow most things..
26495       
26496          
26497 //        'font-size'//??
26498 ];
26499
26500 // black listed style attributes.
26501 Roo.form.HtmlEditor.cblack= [
26502       //  'font-size' -- this can be set by the project 
26503 ];
26504
26505
26506 Roo.form.HtmlEditor.swapCodes   =[ 
26507     [    8211, "--" ], 
26508     [    8212, "--" ], 
26509     [    8216,  "'" ],  
26510     [    8217, "'" ],  
26511     [    8220, '"' ],  
26512     [    8221, '"' ],  
26513     [    8226, "*" ],  
26514     [    8230, "..." ]
26515 ]; 
26516
26517     // <script type="text/javascript">
26518 /*
26519  * Based on
26520  * Ext JS Library 1.1.1
26521  * Copyright(c) 2006-2007, Ext JS, LLC.
26522  *  
26523  
26524  */
26525
26526 /**
26527  * @class Roo.form.HtmlEditorToolbar1
26528  * Basic Toolbar
26529  * 
26530  * Usage:
26531  *
26532  new Roo.form.HtmlEditor({
26533     ....
26534     toolbars : [
26535         new Roo.form.HtmlEditorToolbar1({
26536             disable : { fonts: 1 , format: 1, ..., ... , ...],
26537             btns : [ .... ]
26538         })
26539     }
26540      
26541  * 
26542  * @cfg {Object} disable List of elements to disable..
26543  * @cfg {Array} btns List of additional buttons.
26544  * 
26545  * 
26546  * NEEDS Extra CSS? 
26547  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26548  */
26549  
26550 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26551 {
26552     
26553     Roo.apply(this, config);
26554     
26555     // default disabled, based on 'good practice'..
26556     this.disable = this.disable || {};
26557     Roo.applyIf(this.disable, {
26558         fontSize : true,
26559         colors : true,
26560         specialElements : true
26561     });
26562     
26563     
26564     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26565     // dont call parent... till later.
26566 }
26567
26568 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26569     
26570     tb: false,
26571     
26572     rendered: false,
26573     
26574     editor : false,
26575     /**
26576      * @cfg {Object} disable  List of toolbar elements to disable
26577          
26578      */
26579     disable : false,
26580       /**
26581      * @cfg {Array} fontFamilies An array of available font families
26582      */
26583     fontFamilies : [
26584         'Arial',
26585         'Courier New',
26586         'Tahoma',
26587         'Times New Roman',
26588         'Verdana'
26589     ],
26590     
26591     specialChars : [
26592            "&#169;",
26593           "&#174;",     
26594           "&#8482;",    
26595           "&#163;" ,    
26596          // "&#8212;",    
26597           "&#8230;",    
26598           "&#247;" ,    
26599         //  "&#225;" ,     ?? a acute?
26600            "&#8364;"    , //Euro
26601        //   "&#8220;"    ,
26602         //  "&#8221;"    ,
26603         //  "&#8226;"    ,
26604           "&#176;"  //   , // degrees
26605
26606          // "&#233;"     , // e ecute
26607          // "&#250;"     , // u ecute?
26608     ],
26609     
26610     specialElements : [
26611         {
26612             text: "Insert Table",
26613             xtype: 'MenuItem',
26614             xns : Roo.Menu,
26615             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26616                 
26617         },
26618         {    
26619             text: "Insert Image",
26620             xtype: 'MenuItem',
26621             xns : Roo.Menu,
26622             ihtml : '<img src="about:blank"/>'
26623             
26624         }
26625         
26626          
26627     ],
26628     
26629     
26630     inputElements : [ 
26631             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26632             "input:submit", "input:button", "select", "textarea", "label" ],
26633     formats : [
26634         ["p"] ,  
26635         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26636         ["pre"],[ "code"], 
26637         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26638         ['div'],['span']
26639     ],
26640      /**
26641      * @cfg {String} defaultFont default font to use.
26642      */
26643     defaultFont: 'tahoma',
26644    
26645     fontSelect : false,
26646     
26647     
26648     formatCombo : false,
26649     
26650     init : function(editor)
26651     {
26652         this.editor = editor;
26653         
26654         
26655         var fid = editor.frameId;
26656         var etb = this;
26657         function btn(id, toggle, handler){
26658             var xid = fid + '-'+ id ;
26659             return {
26660                 id : xid,
26661                 cmd : id,
26662                 cls : 'x-btn-icon x-edit-'+id,
26663                 enableToggle:toggle !== false,
26664                 scope: editor, // was editor...
26665                 handler:handler||editor.relayBtnCmd,
26666                 clickEvent:'mousedown',
26667                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26668                 tabIndex:-1
26669             };
26670         }
26671         
26672         
26673         
26674         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26675         this.tb = tb;
26676          // stop form submits
26677         tb.el.on('click', function(e){
26678             e.preventDefault(); // what does this do?
26679         });
26680
26681         if(!this.disable.font) { // && !Roo.isSafari){
26682             /* why no safari for fonts 
26683             editor.fontSelect = tb.el.createChild({
26684                 tag:'select',
26685                 tabIndex: -1,
26686                 cls:'x-font-select',
26687                 html: this.createFontOptions()
26688             });
26689             
26690             editor.fontSelect.on('change', function(){
26691                 var font = editor.fontSelect.dom.value;
26692                 editor.relayCmd('fontname', font);
26693                 editor.deferFocus();
26694             }, editor);
26695             
26696             tb.add(
26697                 editor.fontSelect.dom,
26698                 '-'
26699             );
26700             */
26701             
26702         };
26703         if(!this.disable.formats){
26704             this.formatCombo = new Roo.form.ComboBox({
26705                 store: new Roo.data.SimpleStore({
26706                     id : 'tag',
26707                     fields: ['tag'],
26708                     data : this.formats // from states.js
26709                 }),
26710                 blockFocus : true,
26711                 name : '',
26712                 //autoCreate : {tag: "div",  size: "20"},
26713                 displayField:'tag',
26714                 typeAhead: false,
26715                 mode: 'local',
26716                 editable : false,
26717                 triggerAction: 'all',
26718                 emptyText:'Add tag',
26719                 selectOnFocus:true,
26720                 width:135,
26721                 listeners : {
26722                     'select': function(c, r, i) {
26723                         editor.insertTag(r.get('tag'));
26724                         editor.focus();
26725                     }
26726                 }
26727
26728             });
26729             tb.addField(this.formatCombo);
26730             
26731         }
26732         
26733         if(!this.disable.format){
26734             tb.add(
26735                 btn('bold'),
26736                 btn('italic'),
26737                 btn('underline')
26738             );
26739         };
26740         if(!this.disable.fontSize){
26741             tb.add(
26742                 '-',
26743                 
26744                 
26745                 btn('increasefontsize', false, editor.adjustFont),
26746                 btn('decreasefontsize', false, editor.adjustFont)
26747             );
26748         };
26749         
26750         
26751         if(!this.disable.colors){
26752             tb.add(
26753                 '-', {
26754                     id:editor.frameId +'-forecolor',
26755                     cls:'x-btn-icon x-edit-forecolor',
26756                     clickEvent:'mousedown',
26757                     tooltip: this.buttonTips['forecolor'] || undefined,
26758                     tabIndex:-1,
26759                     menu : new Roo.menu.ColorMenu({
26760                         allowReselect: true,
26761                         focus: Roo.emptyFn,
26762                         value:'000000',
26763                         plain:true,
26764                         selectHandler: function(cp, color){
26765                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26766                             editor.deferFocus();
26767                         },
26768                         scope: editor,
26769                         clickEvent:'mousedown'
26770                     })
26771                 }, {
26772                     id:editor.frameId +'backcolor',
26773                     cls:'x-btn-icon x-edit-backcolor',
26774                     clickEvent:'mousedown',
26775                     tooltip: this.buttonTips['backcolor'] || undefined,
26776                     tabIndex:-1,
26777                     menu : new Roo.menu.ColorMenu({
26778                         focus: Roo.emptyFn,
26779                         value:'FFFFFF',
26780                         plain:true,
26781                         allowReselect: true,
26782                         selectHandler: function(cp, color){
26783                             if(Roo.isGecko){
26784                                 editor.execCmd('useCSS', false);
26785                                 editor.execCmd('hilitecolor', color);
26786                                 editor.execCmd('useCSS', true);
26787                                 editor.deferFocus();
26788                             }else{
26789                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26790                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26791                                 editor.deferFocus();
26792                             }
26793                         },
26794                         scope:editor,
26795                         clickEvent:'mousedown'
26796                     })
26797                 }
26798             );
26799         };
26800         // now add all the items...
26801         
26802
26803         if(!this.disable.alignments){
26804             tb.add(
26805                 '-',
26806                 btn('justifyleft'),
26807                 btn('justifycenter'),
26808                 btn('justifyright')
26809             );
26810         };
26811
26812         //if(!Roo.isSafari){
26813             if(!this.disable.links){
26814                 tb.add(
26815                     '-',
26816                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26817                 );
26818             };
26819
26820             if(!this.disable.lists){
26821                 tb.add(
26822                     '-',
26823                     btn('insertorderedlist'),
26824                     btn('insertunorderedlist')
26825                 );
26826             }
26827             if(!this.disable.sourceEdit){
26828                 tb.add(
26829                     '-',
26830                     btn('sourceedit', true, function(btn){
26831                         this.toggleSourceEdit(btn.pressed);
26832                     })
26833                 );
26834             }
26835         //}
26836         
26837         var smenu = { };
26838         // special menu.. - needs to be tidied up..
26839         if (!this.disable.special) {
26840             smenu = {
26841                 text: "&#169;",
26842                 cls: 'x-edit-none',
26843                 
26844                 menu : {
26845                     items : []
26846                 }
26847             };
26848             for (var i =0; i < this.specialChars.length; i++) {
26849                 smenu.menu.items.push({
26850                     
26851                     html: this.specialChars[i],
26852                     handler: function(a,b) {
26853                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26854                         //editor.insertAtCursor(a.html);
26855                         
26856                     },
26857                     tabIndex:-1
26858                 });
26859             }
26860             
26861             
26862             tb.add(smenu);
26863             
26864             
26865         }
26866          
26867         if (!this.disable.specialElements) {
26868             var semenu = {
26869                 text: "Other;",
26870                 cls: 'x-edit-none',
26871                 menu : {
26872                     items : []
26873                 }
26874             };
26875             for (var i =0; i < this.specialElements.length; i++) {
26876                 semenu.menu.items.push(
26877                     Roo.apply({ 
26878                         handler: function(a,b) {
26879                             editor.insertAtCursor(this.ihtml);
26880                         }
26881                     }, this.specialElements[i])
26882                 );
26883                     
26884             }
26885             
26886             tb.add(semenu);
26887             
26888             
26889         }
26890          
26891         
26892         if (this.btns) {
26893             for(var i =0; i< this.btns.length;i++) {
26894                 var b = Roo.factory(this.btns[i],Roo.form);
26895                 b.cls =  'x-edit-none';
26896                 b.scope = editor;
26897                 tb.add(b);
26898             }
26899         
26900         }
26901         
26902         
26903         
26904         // disable everything...
26905         
26906         this.tb.items.each(function(item){
26907            if(item.id != editor.frameId+ '-sourceedit'){
26908                 item.disable();
26909             }
26910         });
26911         this.rendered = true;
26912         
26913         // the all the btns;
26914         editor.on('editorevent', this.updateToolbar, this);
26915         // other toolbars need to implement this..
26916         //editor.on('editmodechange', this.updateToolbar, this);
26917     },
26918     
26919     
26920     
26921     /**
26922      * Protected method that will not generally be called directly. It triggers
26923      * a toolbar update by reading the markup state of the current selection in the editor.
26924      */
26925     updateToolbar: function(){
26926
26927         if(!this.editor.activated){
26928             this.editor.onFirstFocus();
26929             return;
26930         }
26931
26932         var btns = this.tb.items.map, 
26933             doc = this.editor.doc,
26934             frameId = this.editor.frameId;
26935
26936         if(!this.disable.font && !Roo.isSafari){
26937             /*
26938             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26939             if(name != this.fontSelect.dom.value){
26940                 this.fontSelect.dom.value = name;
26941             }
26942             */
26943         }
26944         if(!this.disable.format){
26945             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26946             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26947             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26948         }
26949         if(!this.disable.alignments){
26950             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26951             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26952             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26953         }
26954         if(!Roo.isSafari && !this.disable.lists){
26955             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26956             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26957         }
26958         
26959         var ans = this.editor.getAllAncestors();
26960         if (this.formatCombo) {
26961             
26962             
26963             var store = this.formatCombo.store;
26964             this.formatCombo.setValue("");
26965             for (var i =0; i < ans.length;i++) {
26966                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26967                     // select it..
26968                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26969                     break;
26970                 }
26971             }
26972         }
26973         
26974         
26975         
26976         // hides menus... - so this cant be on a menu...
26977         Roo.menu.MenuMgr.hideAll();
26978
26979         //this.editorsyncValue();
26980     },
26981    
26982     
26983     createFontOptions : function(){
26984         var buf = [], fs = this.fontFamilies, ff, lc;
26985         
26986         
26987         
26988         for(var i = 0, len = fs.length; i< len; i++){
26989             ff = fs[i];
26990             lc = ff.toLowerCase();
26991             buf.push(
26992                 '<option value="',lc,'" style="font-family:',ff,';"',
26993                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26994                     ff,
26995                 '</option>'
26996             );
26997         }
26998         return buf.join('');
26999     },
27000     
27001     toggleSourceEdit : function(sourceEditMode){
27002         if(sourceEditMode === undefined){
27003             sourceEditMode = !this.sourceEditMode;
27004         }
27005         this.sourceEditMode = sourceEditMode === true;
27006         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27007         // just toggle the button?
27008         if(btn.pressed !== this.editor.sourceEditMode){
27009             btn.toggle(this.editor.sourceEditMode);
27010             return;
27011         }
27012         
27013         if(this.sourceEditMode){
27014             this.tb.items.each(function(item){
27015                 if(item.cmd != 'sourceedit'){
27016                     item.disable();
27017                 }
27018             });
27019           
27020         }else{
27021             if(this.initialized){
27022                 this.tb.items.each(function(item){
27023                     item.enable();
27024                 });
27025             }
27026             
27027         }
27028         // tell the editor that it's been pressed..
27029         this.editor.toggleSourceEdit(sourceEditMode);
27030        
27031     },
27032      /**
27033      * Object collection of toolbar tooltips for the buttons in the editor. The key
27034      * is the command id associated with that button and the value is a valid QuickTips object.
27035      * For example:
27036 <pre><code>
27037 {
27038     bold : {
27039         title: 'Bold (Ctrl+B)',
27040         text: 'Make the selected text bold.',
27041         cls: 'x-html-editor-tip'
27042     },
27043     italic : {
27044         title: 'Italic (Ctrl+I)',
27045         text: 'Make the selected text italic.',
27046         cls: 'x-html-editor-tip'
27047     },
27048     ...
27049 </code></pre>
27050     * @type Object
27051      */
27052     buttonTips : {
27053         bold : {
27054             title: 'Bold (Ctrl+B)',
27055             text: 'Make the selected text bold.',
27056             cls: 'x-html-editor-tip'
27057         },
27058         italic : {
27059             title: 'Italic (Ctrl+I)',
27060             text: 'Make the selected text italic.',
27061             cls: 'x-html-editor-tip'
27062         },
27063         underline : {
27064             title: 'Underline (Ctrl+U)',
27065             text: 'Underline the selected text.',
27066             cls: 'x-html-editor-tip'
27067         },
27068         increasefontsize : {
27069             title: 'Grow Text',
27070             text: 'Increase the font size.',
27071             cls: 'x-html-editor-tip'
27072         },
27073         decreasefontsize : {
27074             title: 'Shrink Text',
27075             text: 'Decrease the font size.',
27076             cls: 'x-html-editor-tip'
27077         },
27078         backcolor : {
27079             title: 'Text Highlight Color',
27080             text: 'Change the background color of the selected text.',
27081             cls: 'x-html-editor-tip'
27082         },
27083         forecolor : {
27084             title: 'Font Color',
27085             text: 'Change the color of the selected text.',
27086             cls: 'x-html-editor-tip'
27087         },
27088         justifyleft : {
27089             title: 'Align Text Left',
27090             text: 'Align text to the left.',
27091             cls: 'x-html-editor-tip'
27092         },
27093         justifycenter : {
27094             title: 'Center Text',
27095             text: 'Center text in the editor.',
27096             cls: 'x-html-editor-tip'
27097         },
27098         justifyright : {
27099             title: 'Align Text Right',
27100             text: 'Align text to the right.',
27101             cls: 'x-html-editor-tip'
27102         },
27103         insertunorderedlist : {
27104             title: 'Bullet List',
27105             text: 'Start a bulleted list.',
27106             cls: 'x-html-editor-tip'
27107         },
27108         insertorderedlist : {
27109             title: 'Numbered List',
27110             text: 'Start a numbered list.',
27111             cls: 'x-html-editor-tip'
27112         },
27113         createlink : {
27114             title: 'Hyperlink',
27115             text: 'Make the selected text a hyperlink.',
27116             cls: 'x-html-editor-tip'
27117         },
27118         sourceedit : {
27119             title: 'Source Edit',
27120             text: 'Switch to source editing mode.',
27121             cls: 'x-html-editor-tip'
27122         }
27123     },
27124     // private
27125     onDestroy : function(){
27126         if(this.rendered){
27127             
27128             this.tb.items.each(function(item){
27129                 if(item.menu){
27130                     item.menu.removeAll();
27131                     if(item.menu.el){
27132                         item.menu.el.destroy();
27133                     }
27134                 }
27135                 item.destroy();
27136             });
27137              
27138         }
27139     },
27140     onFirstFocus: function() {
27141         this.tb.items.each(function(item){
27142            item.enable();
27143         });
27144     }
27145 });
27146
27147
27148
27149
27150 // <script type="text/javascript">
27151 /*
27152  * Based on
27153  * Ext JS Library 1.1.1
27154  * Copyright(c) 2006-2007, Ext JS, LLC.
27155  *  
27156  
27157  */
27158
27159  
27160 /**
27161  * @class Roo.form.HtmlEditor.ToolbarContext
27162  * Context Toolbar
27163  * 
27164  * Usage:
27165  *
27166  new Roo.form.HtmlEditor({
27167     ....
27168     toolbars : [
27169         { xtype: 'ToolbarStandard', styles : {} }
27170         { xtype: 'ToolbarContext', disable : {} }
27171     ]
27172 })
27173
27174      
27175  * 
27176  * @config : {Object} disable List of elements to disable.. (not done yet.)
27177  * @config : {Object} styles  Map of styles available.
27178  * 
27179  */
27180
27181 Roo.form.HtmlEditor.ToolbarContext = function(config)
27182 {
27183     
27184     Roo.apply(this, config);
27185     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27186     // dont call parent... till later.
27187     this.styles = this.styles || {};
27188 }
27189
27190  
27191
27192 Roo.form.HtmlEditor.ToolbarContext.types = {
27193     'IMG' : {
27194         width : {
27195             title: "Width",
27196             width: 40
27197         },
27198         height:  {
27199             title: "Height",
27200             width: 40
27201         },
27202         align: {
27203             title: "Align",
27204             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27205             width : 80
27206             
27207         },
27208         border: {
27209             title: "Border",
27210             width: 40
27211         },
27212         alt: {
27213             title: "Alt",
27214             width: 120
27215         },
27216         src : {
27217             title: "Src",
27218             width: 220
27219         }
27220         
27221     },
27222     'A' : {
27223         name : {
27224             title: "Name",
27225             width: 50
27226         },
27227         href:  {
27228             title: "Href",
27229             width: 220
27230         } // border?
27231         
27232     },
27233     'TABLE' : {
27234         rows : {
27235             title: "Rows",
27236             width: 20
27237         },
27238         cols : {
27239             title: "Cols",
27240             width: 20
27241         },
27242         width : {
27243             title: "Width",
27244             width: 40
27245         },
27246         height : {
27247             title: "Height",
27248             width: 40
27249         },
27250         border : {
27251             title: "Border",
27252             width: 20
27253         }
27254     },
27255     'TD' : {
27256         width : {
27257             title: "Width",
27258             width: 40
27259         },
27260         height : {
27261             title: "Height",
27262             width: 40
27263         },   
27264         align: {
27265             title: "Align",
27266             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27267             width: 80
27268         },
27269         valign: {
27270             title: "Valign",
27271             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27272             width: 80
27273         },
27274         colspan: {
27275             title: "Colspan",
27276             width: 20
27277             
27278         },
27279          'font-family'  : {
27280             title : "Font",
27281             style : 'fontFamily',
27282             displayField: 'display',
27283             optname : 'font-family',
27284             width: 140
27285         }
27286     },
27287     'INPUT' : {
27288         name : {
27289             title: "name",
27290             width: 120
27291         },
27292         value : {
27293             title: "Value",
27294             width: 120
27295         },
27296         width : {
27297             title: "Width",
27298             width: 40
27299         }
27300     },
27301     'LABEL' : {
27302         'for' : {
27303             title: "For",
27304             width: 120
27305         }
27306     },
27307     'TEXTAREA' : {
27308           name : {
27309             title: "name",
27310             width: 120
27311         },
27312         rows : {
27313             title: "Rows",
27314             width: 20
27315         },
27316         cols : {
27317             title: "Cols",
27318             width: 20
27319         }
27320     },
27321     'SELECT' : {
27322         name : {
27323             title: "name",
27324             width: 120
27325         },
27326         selectoptions : {
27327             title: "Options",
27328             width: 200
27329         }
27330     },
27331     
27332     // should we really allow this??
27333     // should this just be 
27334     'BODY' : {
27335         title : {
27336             title: "Title",
27337             width: 200,
27338             disabled : true
27339         }
27340     },
27341     'SPAN' : {
27342         'font-family'  : {
27343             title : "Font",
27344             style : 'fontFamily',
27345             displayField: 'display',
27346             optname : 'font-family',
27347             width: 140
27348         }
27349     },
27350     'DIV' : {
27351         'font-family'  : {
27352             title : "Font",
27353             style : 'fontFamily',
27354             displayField: 'display',
27355             optname : 'font-family',
27356             width: 140
27357         }
27358     },
27359      'P' : {
27360         'font-family'  : {
27361             title : "Font",
27362             style : 'fontFamily',
27363             displayField: 'display',
27364             optname : 'font-family',
27365             width: 140
27366         }
27367     },
27368     
27369     '*' : {
27370         // empty..
27371     }
27372
27373 };
27374
27375 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27376 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27377
27378 Roo.form.HtmlEditor.ToolbarContext.options = {
27379         'font-family'  : [ 
27380                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27381                 [ 'Courier New', 'Courier New'],
27382                 [ 'Tahoma', 'Tahoma'],
27383                 [ 'Times New Roman,serif', 'Times'],
27384                 [ 'Verdana','Verdana' ]
27385         ]
27386 };
27387
27388 // fixme - these need to be configurable..
27389  
27390
27391 Roo.form.HtmlEditor.ToolbarContext.types
27392
27393
27394 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27395     
27396     tb: false,
27397     
27398     rendered: false,
27399     
27400     editor : false,
27401     /**
27402      * @cfg {Object} disable  List of toolbar elements to disable
27403          
27404      */
27405     disable : false,
27406     /**
27407      * @cfg {Object} styles List of styles 
27408      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27409      *
27410      * These must be defined in the page, so they get rendered correctly..
27411      * .headline { }
27412      * TD.underline { }
27413      * 
27414      */
27415     styles : false,
27416     
27417     options: false,
27418     
27419     toolbars : false,
27420     
27421     init : function(editor)
27422     {
27423         this.editor = editor;
27424         
27425         
27426         var fid = editor.frameId;
27427         var etb = this;
27428         function btn(id, toggle, handler){
27429             var xid = fid + '-'+ id ;
27430             return {
27431                 id : xid,
27432                 cmd : id,
27433                 cls : 'x-btn-icon x-edit-'+id,
27434                 enableToggle:toggle !== false,
27435                 scope: editor, // was editor...
27436                 handler:handler||editor.relayBtnCmd,
27437                 clickEvent:'mousedown',
27438                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27439                 tabIndex:-1
27440             };
27441         }
27442         // create a new element.
27443         var wdiv = editor.wrap.createChild({
27444                 tag: 'div'
27445             }, editor.wrap.dom.firstChild.nextSibling, true);
27446         
27447         // can we do this more than once??
27448         
27449          // stop form submits
27450       
27451  
27452         // disable everything...
27453         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27454         this.toolbars = {};
27455            
27456         for (var i in  ty) {
27457           
27458             this.toolbars[i] = this.buildToolbar(ty[i],i);
27459         }
27460         this.tb = this.toolbars.BODY;
27461         this.tb.el.show();
27462         this.buildFooter();
27463         this.footer.show();
27464         editor.on('hide', function( ) { this.footer.hide() }, this);
27465         editor.on('show', function( ) { this.footer.show() }, this);
27466         
27467          
27468         this.rendered = true;
27469         
27470         // the all the btns;
27471         editor.on('editorevent', this.updateToolbar, this);
27472         // other toolbars need to implement this..
27473         //editor.on('editmodechange', this.updateToolbar, this);
27474     },
27475     
27476     
27477     
27478     /**
27479      * Protected method that will not generally be called directly. It triggers
27480      * a toolbar update by reading the markup state of the current selection in the editor.
27481      */
27482     updateToolbar: function(editor,ev,sel){
27483
27484         //Roo.log(ev);
27485         // capture mouse up - this is handy for selecting images..
27486         // perhaps should go somewhere else...
27487         if(!this.editor.activated){
27488              this.editor.onFirstFocus();
27489             return;
27490         }
27491         
27492         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27493         // selectNode - might want to handle IE?
27494         if (ev &&
27495             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27496             ev.target && ev.target.tagName == 'IMG') {
27497             // they have click on an image...
27498             // let's see if we can change the selection...
27499             sel = ev.target;
27500          
27501               var nodeRange = sel.ownerDocument.createRange();
27502             try {
27503                 nodeRange.selectNode(sel);
27504             } catch (e) {
27505                 nodeRange.selectNodeContents(sel);
27506             }
27507             //nodeRange.collapse(true);
27508             var s = editor.win.getSelection();
27509             s.removeAllRanges();
27510             s.addRange(nodeRange);
27511         }  
27512         
27513       
27514         var updateFooter = sel ? false : true;
27515         
27516         
27517         var ans = this.editor.getAllAncestors();
27518         
27519         // pick
27520         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27521         
27522         if (!sel) { 
27523             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27524             sel = sel ? sel : this.editor.doc.body;
27525             sel = sel.tagName.length ? sel : this.editor.doc.body;
27526             
27527         }
27528         // pick a menu that exists..
27529         var tn = sel.tagName.toUpperCase();
27530         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27531         
27532         tn = sel.tagName.toUpperCase();
27533         
27534         var lastSel = this.tb.selectedNode
27535         
27536         this.tb.selectedNode = sel;
27537         
27538         // if current menu does not match..
27539         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27540                 
27541             this.tb.el.hide();
27542             ///console.log("show: " + tn);
27543             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27544             this.tb.el.show();
27545             // update name
27546             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27547             
27548             
27549             // update attributes
27550             if (this.tb.fields) {
27551                 this.tb.fields.each(function(e) {
27552                     if (e.stylename) {
27553                         e.setValue(sel.style[e.stylename]);
27554                         return;
27555                     } 
27556                    e.setValue(sel.getAttribute(e.attrname));
27557                 });
27558             }
27559             
27560             var hasStyles = false;
27561             for(var i in this.styles) {
27562                 hasStyles = true;
27563                 break;
27564             }
27565             
27566             // update styles
27567             if (hasStyles) { 
27568                 var st = this.tb.fields.item(0);
27569                 
27570                 st.store.removeAll();
27571                
27572                 
27573                 var cn = sel.className.split(/\s+/);
27574                 
27575                 var avs = [];
27576                 if (this.styles['*']) {
27577                     
27578                     Roo.each(this.styles['*'], function(v) {
27579                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27580                     });
27581                 }
27582                 if (this.styles[tn]) { 
27583                     Roo.each(this.styles[tn], function(v) {
27584                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27585                     });
27586                 }
27587                 
27588                 st.store.loadData(avs);
27589                 st.collapse();
27590                 st.setValue(cn);
27591             }
27592             // flag our selected Node.
27593             this.tb.selectedNode = sel;
27594            
27595            
27596             Roo.menu.MenuMgr.hideAll();
27597
27598         }
27599         
27600         if (!updateFooter) {
27601             //this.footDisp.dom.innerHTML = ''; 
27602             return;
27603         }
27604         // update the footer
27605         //
27606         var html = '';
27607         
27608         this.footerEls = ans.reverse();
27609         Roo.each(this.footerEls, function(a,i) {
27610             if (!a) { return; }
27611             html += html.length ? ' &gt; '  :  '';
27612             
27613             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27614             
27615         });
27616        
27617         // 
27618         var sz = this.footDisp.up('td').getSize();
27619         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27620         this.footDisp.dom.style.marginLeft = '5px';
27621         
27622         this.footDisp.dom.style.overflow = 'hidden';
27623         
27624         this.footDisp.dom.innerHTML = html;
27625             
27626         //this.editorsyncValue();
27627     },
27628      
27629     
27630    
27631        
27632     // private
27633     onDestroy : function(){
27634         if(this.rendered){
27635             
27636             this.tb.items.each(function(item){
27637                 if(item.menu){
27638                     item.menu.removeAll();
27639                     if(item.menu.el){
27640                         item.menu.el.destroy();
27641                     }
27642                 }
27643                 item.destroy();
27644             });
27645              
27646         }
27647     },
27648     onFirstFocus: function() {
27649         // need to do this for all the toolbars..
27650         this.tb.items.each(function(item){
27651            item.enable();
27652         });
27653     },
27654     buildToolbar: function(tlist, nm)
27655     {
27656         var editor = this.editor;
27657          // create a new element.
27658         var wdiv = editor.wrap.createChild({
27659                 tag: 'div'
27660             }, editor.wrap.dom.firstChild.nextSibling, true);
27661         
27662        
27663         var tb = new Roo.Toolbar(wdiv);
27664         // add the name..
27665         
27666         tb.add(nm+ ":&nbsp;");
27667         
27668         var styles = [];
27669         for(var i in this.styles) {
27670             styles.push(i);
27671         }
27672         
27673         // styles...
27674         if (styles && styles.length) {
27675             
27676             // this needs a multi-select checkbox...
27677             tb.addField( new Roo.form.ComboBox({
27678                 store: new Roo.data.SimpleStore({
27679                     id : 'val',
27680                     fields: ['val', 'selected'],
27681                     data : [] 
27682                 }),
27683                 name : '-roo-edit-className',
27684                 attrname : 'className',
27685                 displayField: 'val',
27686                 typeAhead: false,
27687                 mode: 'local',
27688                 editable : false,
27689                 triggerAction: 'all',
27690                 emptyText:'Select Style',
27691                 selectOnFocus:true,
27692                 width: 130,
27693                 listeners : {
27694                     'select': function(c, r, i) {
27695                         // initial support only for on class per el..
27696                         tb.selectedNode.className =  r ? r.get('val') : '';
27697                         editor.syncValue();
27698                     }
27699                 }
27700     
27701             }));
27702         }
27703         
27704         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27705         var tbops = tbc.options;
27706         
27707         for (var i in tlist) {
27708             
27709             var item = tlist[i];
27710             tb.add(item.title + ":&nbsp;");
27711             
27712             
27713             //optname == used so you can configure the options available..
27714             var opts = item.opts ? item.opts : false;
27715             if (item.optname) {
27716                 opts = tbops[item.optname];
27717            
27718             }
27719             
27720             if (opts) {
27721                 // opts == pulldown..
27722                 tb.addField( new Roo.form.ComboBox({
27723                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27724                         id : 'val',
27725                         fields: ['val', 'display'],
27726                         data : opts  
27727                     }),
27728                     name : '-roo-edit-' + i,
27729                     attrname : i,
27730                     stylename : item.style ? item.style : false,
27731                     displayField: item.displayField ? item.displayField : 'val',
27732                     valueField :  'val',
27733                     typeAhead: false,
27734                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27735                     editable : false,
27736                     triggerAction: 'all',
27737                     emptyText:'Select',
27738                     selectOnFocus:true,
27739                     width: item.width ? item.width  : 130,
27740                     listeners : {
27741                         'select': function(c, r, i) {
27742                             if (c.stylename) {
27743                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27744                                 return;
27745                             }
27746                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27747                         }
27748                     }
27749
27750                 }));
27751                 continue;
27752                     
27753                  
27754                 
27755                 tb.addField( new Roo.form.TextField({
27756                     name: i,
27757                     width: 100,
27758                     //allowBlank:false,
27759                     value: ''
27760                 }));
27761                 continue;
27762             }
27763             tb.addField( new Roo.form.TextField({
27764                 name: '-roo-edit-' + i,
27765                 attrname : i,
27766                 
27767                 width: item.width,
27768                 //allowBlank:true,
27769                 value: '',
27770                 listeners: {
27771                     'change' : function(f, nv, ov) {
27772                         tb.selectedNode.setAttribute(f.attrname, nv);
27773                     }
27774                 }
27775             }));
27776              
27777         }
27778         tb.addFill();
27779         var _this = this;
27780         tb.addButton( {
27781             text: 'Remove Tag',
27782     
27783             listeners : {
27784                 click : function ()
27785                 {
27786                     // remove
27787                     // undo does not work.
27788                      
27789                     var sn = tb.selectedNode;
27790                     
27791                     var pn = sn.parentNode;
27792                     
27793                     var stn =  sn.childNodes[0];
27794                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27795                     while (sn.childNodes.length) {
27796                         var node = sn.childNodes[0];
27797                         sn.removeChild(node);
27798                         //Roo.log(node);
27799                         pn.insertBefore(node, sn);
27800                         
27801                     }
27802                     pn.removeChild(sn);
27803                     var range = editor.createRange();
27804         
27805                     range.setStart(stn,0);
27806                     range.setEnd(en,0); //????
27807                     //range.selectNode(sel);
27808                     
27809                     
27810                     var selection = editor.getSelection();
27811                     selection.removeAllRanges();
27812                     selection.addRange(range);
27813                     
27814                     
27815                     
27816                     //_this.updateToolbar(null, null, pn);
27817                     _this.updateToolbar(null, null, null);
27818                     _this.footDisp.dom.innerHTML = ''; 
27819                 }
27820             }
27821             
27822                     
27823                 
27824             
27825         });
27826         
27827         
27828         tb.el.on('click', function(e){
27829             e.preventDefault(); // what does this do?
27830         });
27831         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27832         tb.el.hide();
27833         tb.name = nm;
27834         // dont need to disable them... as they will get hidden
27835         return tb;
27836          
27837         
27838     },
27839     buildFooter : function()
27840     {
27841         
27842         var fel = this.editor.wrap.createChild();
27843         this.footer = new Roo.Toolbar(fel);
27844         // toolbar has scrolly on left / right?
27845         var footDisp= new Roo.Toolbar.Fill();
27846         var _t = this;
27847         this.footer.add(
27848             {
27849                 text : '&lt;',
27850                 xtype: 'Button',
27851                 handler : function() {
27852                     _t.footDisp.scrollTo('left',0,true)
27853                 }
27854             }
27855         );
27856         this.footer.add( footDisp );
27857         this.footer.add( 
27858             {
27859                 text : '&gt;',
27860                 xtype: 'Button',
27861                 handler : function() {
27862                     // no animation..
27863                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27864                 }
27865             }
27866         );
27867         var fel = Roo.get(footDisp.el);
27868         fel.addClass('x-editor-context');
27869         this.footDispWrap = fel; 
27870         this.footDispWrap.overflow  = 'hidden';
27871         
27872         this.footDisp = fel.createChild();
27873         this.footDispWrap.on('click', this.onContextClick, this)
27874         
27875         
27876     },
27877     onContextClick : function (ev,dom)
27878     {
27879         ev.preventDefault();
27880         var  cn = dom.className;
27881         //Roo.log(cn);
27882         if (!cn.match(/x-ed-loc-/)) {
27883             return;
27884         }
27885         var n = cn.split('-').pop();
27886         var ans = this.footerEls;
27887         var sel = ans[n];
27888         
27889          // pick
27890         var range = this.editor.createRange();
27891         
27892         range.selectNodeContents(sel);
27893         //range.selectNode(sel);
27894         
27895         
27896         var selection = this.editor.getSelection();
27897         selection.removeAllRanges();
27898         selection.addRange(range);
27899         
27900         
27901         
27902         this.updateToolbar(null, null, sel);
27903         
27904         
27905     }
27906     
27907     
27908     
27909     
27910     
27911 });
27912
27913
27914
27915
27916
27917 /*
27918  * Based on:
27919  * Ext JS Library 1.1.1
27920  * Copyright(c) 2006-2007, Ext JS, LLC.
27921  *
27922  * Originally Released Under LGPL - original licence link has changed is not relivant.
27923  *
27924  * Fork - LGPL
27925  * <script type="text/javascript">
27926  */
27927  
27928 /**
27929  * @class Roo.form.BasicForm
27930  * @extends Roo.util.Observable
27931  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27932  * @constructor
27933  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27934  * @param {Object} config Configuration options
27935  */
27936 Roo.form.BasicForm = function(el, config){
27937     this.allItems = [];
27938     this.childForms = [];
27939     Roo.apply(this, config);
27940     /*
27941      * The Roo.form.Field items in this form.
27942      * @type MixedCollection
27943      */
27944      
27945      
27946     this.items = new Roo.util.MixedCollection(false, function(o){
27947         return o.id || (o.id = Roo.id());
27948     });
27949     this.addEvents({
27950         /**
27951          * @event beforeaction
27952          * Fires before any action is performed. Return false to cancel the action.
27953          * @param {Form} this
27954          * @param {Action} action The action to be performed
27955          */
27956         beforeaction: true,
27957         /**
27958          * @event actionfailed
27959          * Fires when an action fails.
27960          * @param {Form} this
27961          * @param {Action} action The action that failed
27962          */
27963         actionfailed : true,
27964         /**
27965          * @event actioncomplete
27966          * Fires when an action is completed.
27967          * @param {Form} this
27968          * @param {Action} action The action that completed
27969          */
27970         actioncomplete : true
27971     });
27972     if(el){
27973         this.initEl(el);
27974     }
27975     Roo.form.BasicForm.superclass.constructor.call(this);
27976 };
27977
27978 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27979     /**
27980      * @cfg {String} method
27981      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27982      */
27983     /**
27984      * @cfg {DataReader} reader
27985      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27986      * This is optional as there is built-in support for processing JSON.
27987      */
27988     /**
27989      * @cfg {DataReader} errorReader
27990      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27991      * This is completely optional as there is built-in support for processing JSON.
27992      */
27993     /**
27994      * @cfg {String} url
27995      * The URL to use for form actions if one isn't supplied in the action options.
27996      */
27997     /**
27998      * @cfg {Boolean} fileUpload
27999      * Set to true if this form is a file upload.
28000      */
28001      
28002     /**
28003      * @cfg {Object} baseParams
28004      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28005      */
28006      /**
28007      
28008     /**
28009      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28010      */
28011     timeout: 30,
28012
28013     // private
28014     activeAction : null,
28015
28016     /**
28017      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28018      * or setValues() data instead of when the form was first created.
28019      */
28020     trackResetOnLoad : false,
28021     
28022     
28023     /**
28024      * childForms - used for multi-tab forms
28025      * @type {Array}
28026      */
28027     childForms : false,
28028     
28029     /**
28030      * allItems - full list of fields.
28031      * @type {Array}
28032      */
28033     allItems : false,
28034     
28035     /**
28036      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28037      * element by passing it or its id or mask the form itself by passing in true.
28038      * @type Mixed
28039      */
28040     waitMsgTarget : false,
28041
28042     // private
28043     initEl : function(el){
28044         this.el = Roo.get(el);
28045         this.id = this.el.id || Roo.id();
28046         this.el.on('submit', this.onSubmit, this);
28047         this.el.addClass('x-form');
28048     },
28049
28050     // private
28051     onSubmit : function(e){
28052         e.stopEvent();
28053     },
28054
28055     /**
28056      * Returns true if client-side validation on the form is successful.
28057      * @return Boolean
28058      */
28059     isValid : function(){
28060         var valid = true;
28061         this.items.each(function(f){
28062            if(!f.validate()){
28063                valid = false;
28064            }
28065         });
28066         return valid;
28067     },
28068
28069     /**
28070      * Returns true if any fields in this form have changed since their original load.
28071      * @return Boolean
28072      */
28073     isDirty : function(){
28074         var dirty = false;
28075         this.items.each(function(f){
28076            if(f.isDirty()){
28077                dirty = true;
28078                return false;
28079            }
28080         });
28081         return dirty;
28082     },
28083
28084     /**
28085      * Performs a predefined action (submit or load) or custom actions you define on this form.
28086      * @param {String} actionName The name of the action type
28087      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28088      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28089      * accept other config options):
28090      * <pre>
28091 Property          Type             Description
28092 ----------------  ---------------  ----------------------------------------------------------------------------------
28093 url               String           The url for the action (defaults to the form's url)
28094 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28095 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28096 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28097                                    validate the form on the client (defaults to false)
28098      * </pre>
28099      * @return {BasicForm} this
28100      */
28101     doAction : function(action, options){
28102         if(typeof action == 'string'){
28103             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28104         }
28105         if(this.fireEvent('beforeaction', this, action) !== false){
28106             this.beforeAction(action);
28107             action.run.defer(100, action);
28108         }
28109         return this;
28110     },
28111
28112     /**
28113      * Shortcut to do a submit action.
28114      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28115      * @return {BasicForm} this
28116      */
28117     submit : function(options){
28118         this.doAction('submit', options);
28119         return this;
28120     },
28121
28122     /**
28123      * Shortcut to do a load action.
28124      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28125      * @return {BasicForm} this
28126      */
28127     load : function(options){
28128         this.doAction('load', options);
28129         return this;
28130     },
28131
28132     /**
28133      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28134      * @param {Record} record The record to edit
28135      * @return {BasicForm} this
28136      */
28137     updateRecord : function(record){
28138         record.beginEdit();
28139         var fs = record.fields;
28140         fs.each(function(f){
28141             var field = this.findField(f.name);
28142             if(field){
28143                 record.set(f.name, field.getValue());
28144             }
28145         }, this);
28146         record.endEdit();
28147         return this;
28148     },
28149
28150     /**
28151      * Loads an Roo.data.Record into this form.
28152      * @param {Record} record The record to load
28153      * @return {BasicForm} this
28154      */
28155     loadRecord : function(record){
28156         this.setValues(record.data);
28157         return this;
28158     },
28159
28160     // private
28161     beforeAction : function(action){
28162         var o = action.options;
28163         
28164        
28165         if(this.waitMsgTarget === true){
28166             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28167         }else if(this.waitMsgTarget){
28168             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28169             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28170         }else {
28171             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28172         }
28173          
28174     },
28175
28176     // private
28177     afterAction : function(action, success){
28178         this.activeAction = null;
28179         var o = action.options;
28180         
28181         if(this.waitMsgTarget === true){
28182             this.el.unmask();
28183         }else if(this.waitMsgTarget){
28184             this.waitMsgTarget.unmask();
28185         }else{
28186             Roo.MessageBox.updateProgress(1);
28187             Roo.MessageBox.hide();
28188         }
28189          
28190         if(success){
28191             if(o.reset){
28192                 this.reset();
28193             }
28194             Roo.callback(o.success, o.scope, [this, action]);
28195             this.fireEvent('actioncomplete', this, action);
28196             
28197         }else{
28198             
28199             // failure condition..
28200             // we have a scenario where updates need confirming.
28201             // eg. if a locking scenario exists..
28202             // we look for { errors : { needs_confirm : true }} in the response.
28203             if (
28204                 (typeof(action.result) != 'undefined')  &&
28205                 (typeof(action.result.errors) != 'undefined')  &&
28206                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28207            ){
28208                 var _t = this;
28209                 Roo.MessageBox.confirm(
28210                     "Change requires confirmation",
28211                     action.result.errorMsg,
28212                     function(r) {
28213                         if (r != 'yes') {
28214                             return;
28215                         }
28216                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28217                     }
28218                     
28219                 );
28220                 
28221                 
28222                 
28223                 return;
28224             }
28225             
28226             Roo.callback(o.failure, o.scope, [this, action]);
28227             // show an error message if no failed handler is set..
28228             if (!this.hasListener('actionfailed')) {
28229                 Roo.MessageBox.alert("Error",
28230                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28231                         action.result.errorMsg :
28232                         "Saving Failed, please check your entries or try again"
28233                 );
28234             }
28235             
28236             this.fireEvent('actionfailed', this, action);
28237         }
28238         
28239     },
28240
28241     /**
28242      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28243      * @param {String} id The value to search for
28244      * @return Field
28245      */
28246     findField : function(id){
28247         var field = this.items.get(id);
28248         if(!field){
28249             this.items.each(function(f){
28250                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28251                     field = f;
28252                     return false;
28253                 }
28254             });
28255         }
28256         return field || null;
28257     },
28258
28259     /**
28260      * Add a secondary form to this one, 
28261      * Used to provide tabbed forms. One form is primary, with hidden values 
28262      * which mirror the elements from the other forms.
28263      * 
28264      * @param {Roo.form.Form} form to add.
28265      * 
28266      */
28267     addForm : function(form)
28268     {
28269        
28270         if (this.childForms.indexOf(form) > -1) {
28271             // already added..
28272             return;
28273         }
28274         this.childForms.push(form);
28275         var n = '';
28276         Roo.each(form.allItems, function (fe) {
28277             
28278             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28279             if (this.findField(n)) { // already added..
28280                 return;
28281             }
28282             var add = new Roo.form.Hidden({
28283                 name : n
28284             });
28285             add.render(this.el);
28286             
28287             this.add( add );
28288         }, this);
28289         
28290     },
28291     /**
28292      * Mark fields in this form invalid in bulk.
28293      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28294      * @return {BasicForm} this
28295      */
28296     markInvalid : function(errors){
28297         if(errors instanceof Array){
28298             for(var i = 0, len = errors.length; i < len; i++){
28299                 var fieldError = errors[i];
28300                 var f = this.findField(fieldError.id);
28301                 if(f){
28302                     f.markInvalid(fieldError.msg);
28303                 }
28304             }
28305         }else{
28306             var field, id;
28307             for(id in errors){
28308                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28309                     field.markInvalid(errors[id]);
28310                 }
28311             }
28312         }
28313         Roo.each(this.childForms || [], function (f) {
28314             f.markInvalid(errors);
28315         });
28316         
28317         return this;
28318     },
28319
28320     /**
28321      * Set values for fields in this form in bulk.
28322      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28323      * @return {BasicForm} this
28324      */
28325     setValues : function(values){
28326         if(values instanceof Array){ // array of objects
28327             for(var i = 0, len = values.length; i < len; i++){
28328                 var v = values[i];
28329                 var f = this.findField(v.id);
28330                 if(f){
28331                     f.setValue(v.value);
28332                     if(this.trackResetOnLoad){
28333                         f.originalValue = f.getValue();
28334                     }
28335                 }
28336             }
28337         }else{ // object hash
28338             var field, id;
28339             for(id in values){
28340                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28341                     
28342                     if (field.setFromData && 
28343                         field.valueField && 
28344                         field.displayField &&
28345                         // combos' with local stores can 
28346                         // be queried via setValue()
28347                         // to set their value..
28348                         (field.store && !field.store.isLocal)
28349                         ) {
28350                         // it's a combo
28351                         var sd = { };
28352                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28353                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28354                         field.setFromData(sd);
28355                         
28356                     } else {
28357                         field.setValue(values[id]);
28358                     }
28359                     
28360                     
28361                     if(this.trackResetOnLoad){
28362                         field.originalValue = field.getValue();
28363                     }
28364                 }
28365             }
28366         }
28367          
28368         Roo.each(this.childForms || [], function (f) {
28369             f.setValues(values);
28370         });
28371                 
28372         return this;
28373     },
28374
28375     /**
28376      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28377      * they are returned as an array.
28378      * @param {Boolean} asString
28379      * @return {Object}
28380      */
28381     getValues : function(asString){
28382         if (this.childForms) {
28383             // copy values from the child forms
28384             Roo.each(this.childForms, function (f) {
28385                 this.setValues(f.getValues());
28386             }, this);
28387         }
28388         
28389         
28390         
28391         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28392         if(asString === true){
28393             return fs;
28394         }
28395         return Roo.urlDecode(fs);
28396     },
28397     
28398     /**
28399      * Returns the fields in this form as an object with key/value pairs. 
28400      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28401      * @return {Object}
28402      */
28403     getFieldValues : function(with_hidden)
28404     {
28405         if (this.childForms) {
28406             // copy values from the child forms
28407             // should this call getFieldValues - probably not as we do not currently copy
28408             // hidden fields when we generate..
28409             Roo.each(this.childForms, function (f) {
28410                 this.setValues(f.getValues());
28411             }, this);
28412         }
28413         
28414         var ret = {};
28415         this.items.each(function(f){
28416             if (!f.getName()) {
28417                 return;
28418             }
28419             var v = f.getValue();
28420             // not sure if this supported any more..
28421             if ((typeof(v) == 'object') && f.getRawValue) {
28422                 v = f.getRawValue() ; // dates..
28423             }
28424             // combo boxes where name != hiddenName...
28425             if (f.name != f.getName()) {
28426                 ret[f.name] = f.getRawValue();
28427             }
28428             ret[f.getName()] = v;
28429         });
28430         
28431         return ret;
28432     },
28433
28434     /**
28435      * Clears all invalid messages in this form.
28436      * @return {BasicForm} this
28437      */
28438     clearInvalid : function(){
28439         this.items.each(function(f){
28440            f.clearInvalid();
28441         });
28442         
28443         Roo.each(this.childForms || [], function (f) {
28444             f.clearInvalid();
28445         });
28446         
28447         
28448         return this;
28449     },
28450
28451     /**
28452      * Resets this form.
28453      * @return {BasicForm} this
28454      */
28455     reset : function(){
28456         this.items.each(function(f){
28457             f.reset();
28458         });
28459         
28460         Roo.each(this.childForms || [], function (f) {
28461             f.reset();
28462         });
28463        
28464         
28465         return this;
28466     },
28467
28468     /**
28469      * Add Roo.form components to this form.
28470      * @param {Field} field1
28471      * @param {Field} field2 (optional)
28472      * @param {Field} etc (optional)
28473      * @return {BasicForm} this
28474      */
28475     add : function(){
28476         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28477         return this;
28478     },
28479
28480
28481     /**
28482      * Removes a field from the items collection (does NOT remove its markup).
28483      * @param {Field} field
28484      * @return {BasicForm} this
28485      */
28486     remove : function(field){
28487         this.items.remove(field);
28488         return this;
28489     },
28490
28491     /**
28492      * Looks at the fields in this form, checks them for an id attribute,
28493      * and calls applyTo on the existing dom element with that id.
28494      * @return {BasicForm} this
28495      */
28496     render : function(){
28497         this.items.each(function(f){
28498             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28499                 f.applyTo(f.id);
28500             }
28501         });
28502         return this;
28503     },
28504
28505     /**
28506      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28507      * @param {Object} values
28508      * @return {BasicForm} this
28509      */
28510     applyToFields : function(o){
28511         this.items.each(function(f){
28512            Roo.apply(f, o);
28513         });
28514         return this;
28515     },
28516
28517     /**
28518      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28519      * @param {Object} values
28520      * @return {BasicForm} this
28521      */
28522     applyIfToFields : function(o){
28523         this.items.each(function(f){
28524            Roo.applyIf(f, o);
28525         });
28526         return this;
28527     }
28528 });
28529
28530 // back compat
28531 Roo.BasicForm = Roo.form.BasicForm;/*
28532  * Based on:
28533  * Ext JS Library 1.1.1
28534  * Copyright(c) 2006-2007, Ext JS, LLC.
28535  *
28536  * Originally Released Under LGPL - original licence link has changed is not relivant.
28537  *
28538  * Fork - LGPL
28539  * <script type="text/javascript">
28540  */
28541
28542 /**
28543  * @class Roo.form.Form
28544  * @extends Roo.form.BasicForm
28545  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28546  * @constructor
28547  * @param {Object} config Configuration options
28548  */
28549 Roo.form.Form = function(config){
28550     var xitems =  [];
28551     if (config.items) {
28552         xitems = config.items;
28553         delete config.items;
28554     }
28555    
28556     
28557     Roo.form.Form.superclass.constructor.call(this, null, config);
28558     this.url = this.url || this.action;
28559     if(!this.root){
28560         this.root = new Roo.form.Layout(Roo.applyIf({
28561             id: Roo.id()
28562         }, config));
28563     }
28564     this.active = this.root;
28565     /**
28566      * Array of all the buttons that have been added to this form via {@link addButton}
28567      * @type Array
28568      */
28569     this.buttons = [];
28570     this.allItems = [];
28571     this.addEvents({
28572         /**
28573          * @event clientvalidation
28574          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28575          * @param {Form} this
28576          * @param {Boolean} valid true if the form has passed client-side validation
28577          */
28578         clientvalidation: true,
28579         /**
28580          * @event rendered
28581          * Fires when the form is rendered
28582          * @param {Roo.form.Form} form
28583          */
28584         rendered : true
28585     });
28586     
28587     if (this.progressUrl) {
28588             // push a hidden field onto the list of fields..
28589             this.addxtype( {
28590                     xns: Roo.form, 
28591                     xtype : 'Hidden', 
28592                     name : 'UPLOAD_IDENTIFIER' 
28593             });
28594         }
28595         
28596     
28597     Roo.each(xitems, this.addxtype, this);
28598     
28599     
28600     
28601 };
28602
28603 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28604     /**
28605      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28606      */
28607     /**
28608      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28609      */
28610     /**
28611      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28612      */
28613     buttonAlign:'center',
28614
28615     /**
28616      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28617      */
28618     minButtonWidth:75,
28619
28620     /**
28621      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28622      * This property cascades to child containers if not set.
28623      */
28624     labelAlign:'left',
28625
28626     /**
28627      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28628      * fires a looping event with that state. This is required to bind buttons to the valid
28629      * state using the config value formBind:true on the button.
28630      */
28631     monitorValid : false,
28632
28633     /**
28634      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28635      */
28636     monitorPoll : 200,
28637     
28638     /**
28639      * @cfg {String} progressUrl - Url to return progress data 
28640      */
28641     
28642     progressUrl : false,
28643   
28644     /**
28645      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28646      * fields are added and the column is closed. If no fields are passed the column remains open
28647      * until end() is called.
28648      * @param {Object} config The config to pass to the column
28649      * @param {Field} field1 (optional)
28650      * @param {Field} field2 (optional)
28651      * @param {Field} etc (optional)
28652      * @return Column The column container object
28653      */
28654     column : function(c){
28655         var col = new Roo.form.Column(c);
28656         this.start(col);
28657         if(arguments.length > 1){ // duplicate code required because of Opera
28658             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28659             this.end();
28660         }
28661         return col;
28662     },
28663
28664     /**
28665      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28666      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28667      * until end() is called.
28668      * @param {Object} config The config to pass to the fieldset
28669      * @param {Field} field1 (optional)
28670      * @param {Field} field2 (optional)
28671      * @param {Field} etc (optional)
28672      * @return FieldSet The fieldset container object
28673      */
28674     fieldset : function(c){
28675         var fs = new Roo.form.FieldSet(c);
28676         this.start(fs);
28677         if(arguments.length > 1){ // duplicate code required because of Opera
28678             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28679             this.end();
28680         }
28681         return fs;
28682     },
28683
28684     /**
28685      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28686      * fields are added and the container is closed. If no fields are passed the container remains open
28687      * until end() is called.
28688      * @param {Object} config The config to pass to the Layout
28689      * @param {Field} field1 (optional)
28690      * @param {Field} field2 (optional)
28691      * @param {Field} etc (optional)
28692      * @return Layout The container object
28693      */
28694     container : function(c){
28695         var l = new Roo.form.Layout(c);
28696         this.start(l);
28697         if(arguments.length > 1){ // duplicate code required because of Opera
28698             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28699             this.end();
28700         }
28701         return l;
28702     },
28703
28704     /**
28705      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28706      * @param {Object} container A Roo.form.Layout or subclass of Layout
28707      * @return {Form} this
28708      */
28709     start : function(c){
28710         // cascade label info
28711         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28712         this.active.stack.push(c);
28713         c.ownerCt = this.active;
28714         this.active = c;
28715         return this;
28716     },
28717
28718     /**
28719      * Closes the current open container
28720      * @return {Form} this
28721      */
28722     end : function(){
28723         if(this.active == this.root){
28724             return this;
28725         }
28726         this.active = this.active.ownerCt;
28727         return this;
28728     },
28729
28730     /**
28731      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28732      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28733      * as the label of the field.
28734      * @param {Field} field1
28735      * @param {Field} field2 (optional)
28736      * @param {Field} etc. (optional)
28737      * @return {Form} this
28738      */
28739     add : function(){
28740         this.active.stack.push.apply(this.active.stack, arguments);
28741         this.allItems.push.apply(this.allItems,arguments);
28742         var r = [];
28743         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28744             if(a[i].isFormField){
28745                 r.push(a[i]);
28746             }
28747         }
28748         if(r.length > 0){
28749             Roo.form.Form.superclass.add.apply(this, r);
28750         }
28751         return this;
28752     },
28753     
28754
28755     
28756     
28757     
28758      /**
28759      * Find any element that has been added to a form, using it's ID or name
28760      * This can include framesets, columns etc. along with regular fields..
28761      * @param {String} id - id or name to find.
28762      
28763      * @return {Element} e - or false if nothing found.
28764      */
28765     findbyId : function(id)
28766     {
28767         var ret = false;
28768         if (!id) {
28769             return ret;
28770         }
28771         Roo.each(this.allItems, function(f){
28772             if (f.id == id || f.name == id ){
28773                 ret = f;
28774                 return false;
28775             }
28776         });
28777         return ret;
28778     },
28779
28780     
28781     
28782     /**
28783      * Render this form into the passed container. This should only be called once!
28784      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28785      * @return {Form} this
28786      */
28787     render : function(ct)
28788     {
28789         
28790         
28791         
28792         ct = Roo.get(ct);
28793         var o = this.autoCreate || {
28794             tag: 'form',
28795             method : this.method || 'POST',
28796             id : this.id || Roo.id()
28797         };
28798         this.initEl(ct.createChild(o));
28799
28800         this.root.render(this.el);
28801         
28802        
28803              
28804         this.items.each(function(f){
28805             f.render('x-form-el-'+f.id);
28806         });
28807
28808         if(this.buttons.length > 0){
28809             // tables are required to maintain order and for correct IE layout
28810             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28811                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28812                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28813             }}, null, true);
28814             var tr = tb.getElementsByTagName('tr')[0];
28815             for(var i = 0, len = this.buttons.length; i < len; i++) {
28816                 var b = this.buttons[i];
28817                 var td = document.createElement('td');
28818                 td.className = 'x-form-btn-td';
28819                 b.render(tr.appendChild(td));
28820             }
28821         }
28822         if(this.monitorValid){ // initialize after render
28823             this.startMonitoring();
28824         }
28825         this.fireEvent('rendered', this);
28826         return this;
28827     },
28828
28829     /**
28830      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28831      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28832      * object or a valid Roo.DomHelper element config
28833      * @param {Function} handler The function called when the button is clicked
28834      * @param {Object} scope (optional) The scope of the handler function
28835      * @return {Roo.Button}
28836      */
28837     addButton : function(config, handler, scope){
28838         var bc = {
28839             handler: handler,
28840             scope: scope,
28841             minWidth: this.minButtonWidth,
28842             hideParent:true
28843         };
28844         if(typeof config == "string"){
28845             bc.text = config;
28846         }else{
28847             Roo.apply(bc, config);
28848         }
28849         var btn = new Roo.Button(null, bc);
28850         this.buttons.push(btn);
28851         return btn;
28852     },
28853
28854      /**
28855      * Adds a series of form elements (using the xtype property as the factory method.
28856      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28857      * @param {Object} config 
28858      */
28859     
28860     addxtype : function()
28861     {
28862         var ar = Array.prototype.slice.call(arguments, 0);
28863         var ret = false;
28864         for(var i = 0; i < ar.length; i++) {
28865             if (!ar[i]) {
28866                 continue; // skip -- if this happends something invalid got sent, we 
28867                 // should ignore it, as basically that interface element will not show up
28868                 // and that should be pretty obvious!!
28869             }
28870             
28871             if (Roo.form[ar[i].xtype]) {
28872                 ar[i].form = this;
28873                 var fe = Roo.factory(ar[i], Roo.form);
28874                 if (!ret) {
28875                     ret = fe;
28876                 }
28877                 fe.form = this;
28878                 if (fe.store) {
28879                     fe.store.form = this;
28880                 }
28881                 if (fe.isLayout) {  
28882                          
28883                     this.start(fe);
28884                     this.allItems.push(fe);
28885                     if (fe.items && fe.addxtype) {
28886                         fe.addxtype.apply(fe, fe.items);
28887                         delete fe.items;
28888                     }
28889                      this.end();
28890                     continue;
28891                 }
28892                 
28893                 
28894                  
28895                 this.add(fe);
28896               //  console.log('adding ' + ar[i].xtype);
28897             }
28898             if (ar[i].xtype == 'Button') {  
28899                 //console.log('adding button');
28900                 //console.log(ar[i]);
28901                 this.addButton(ar[i]);
28902                 this.allItems.push(fe);
28903                 continue;
28904             }
28905             
28906             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28907                 alert('end is not supported on xtype any more, use items');
28908             //    this.end();
28909             //    //console.log('adding end');
28910             }
28911             
28912         }
28913         return ret;
28914     },
28915     
28916     /**
28917      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28918      * option "monitorValid"
28919      */
28920     startMonitoring : function(){
28921         if(!this.bound){
28922             this.bound = true;
28923             Roo.TaskMgr.start({
28924                 run : this.bindHandler,
28925                 interval : this.monitorPoll || 200,
28926                 scope: this
28927             });
28928         }
28929     },
28930
28931     /**
28932      * Stops monitoring of the valid state of this form
28933      */
28934     stopMonitoring : function(){
28935         this.bound = false;
28936     },
28937
28938     // private
28939     bindHandler : function(){
28940         if(!this.bound){
28941             return false; // stops binding
28942         }
28943         var valid = true;
28944         this.items.each(function(f){
28945             if(!f.isValid(true)){
28946                 valid = false;
28947                 return false;
28948             }
28949         });
28950         for(var i = 0, len = this.buttons.length; i < len; i++){
28951             var btn = this.buttons[i];
28952             if(btn.formBind === true && btn.disabled === valid){
28953                 btn.setDisabled(!valid);
28954             }
28955         }
28956         this.fireEvent('clientvalidation', this, valid);
28957     }
28958     
28959     
28960     
28961     
28962     
28963     
28964     
28965     
28966 });
28967
28968
28969 // back compat
28970 Roo.Form = Roo.form.Form;
28971 /*
28972  * Based on:
28973  * Ext JS Library 1.1.1
28974  * Copyright(c) 2006-2007, Ext JS, LLC.
28975  *
28976  * Originally Released Under LGPL - original licence link has changed is not relivant.
28977  *
28978  * Fork - LGPL
28979  * <script type="text/javascript">
28980  */
28981  
28982  /**
28983  * @class Roo.form.Action
28984  * Internal Class used to handle form actions
28985  * @constructor
28986  * @param {Roo.form.BasicForm} el The form element or its id
28987  * @param {Object} config Configuration options
28988  */
28989  
28990  
28991 // define the action interface
28992 Roo.form.Action = function(form, options){
28993     this.form = form;
28994     this.options = options || {};
28995 };
28996 /**
28997  * Client Validation Failed
28998  * @const 
28999  */
29000 Roo.form.Action.CLIENT_INVALID = 'client';
29001 /**
29002  * Server Validation Failed
29003  * @const 
29004  */
29005  Roo.form.Action.SERVER_INVALID = 'server';
29006  /**
29007  * Connect to Server Failed
29008  * @const 
29009  */
29010 Roo.form.Action.CONNECT_FAILURE = 'connect';
29011 /**
29012  * Reading Data from Server Failed
29013  * @const 
29014  */
29015 Roo.form.Action.LOAD_FAILURE = 'load';
29016
29017 Roo.form.Action.prototype = {
29018     type : 'default',
29019     failureType : undefined,
29020     response : undefined,
29021     result : undefined,
29022
29023     // interface method
29024     run : function(options){
29025
29026     },
29027
29028     // interface method
29029     success : function(response){
29030
29031     },
29032
29033     // interface method
29034     handleResponse : function(response){
29035
29036     },
29037
29038     // default connection failure
29039     failure : function(response){
29040         
29041         this.response = response;
29042         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29043         this.form.afterAction(this, false);
29044     },
29045
29046     processResponse : function(response){
29047         this.response = response;
29048         if(!response.responseText){
29049             return true;
29050         }
29051         this.result = this.handleResponse(response);
29052         return this.result;
29053     },
29054
29055     // utility functions used internally
29056     getUrl : function(appendParams){
29057         var url = this.options.url || this.form.url || this.form.el.dom.action;
29058         if(appendParams){
29059             var p = this.getParams();
29060             if(p){
29061                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29062             }
29063         }
29064         return url;
29065     },
29066
29067     getMethod : function(){
29068         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29069     },
29070
29071     getParams : function(){
29072         var bp = this.form.baseParams;
29073         var p = this.options.params;
29074         if(p){
29075             if(typeof p == "object"){
29076                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29077             }else if(typeof p == 'string' && bp){
29078                 p += '&' + Roo.urlEncode(bp);
29079             }
29080         }else if(bp){
29081             p = Roo.urlEncode(bp);
29082         }
29083         return p;
29084     },
29085
29086     createCallback : function(){
29087         return {
29088             success: this.success,
29089             failure: this.failure,
29090             scope: this,
29091             timeout: (this.form.timeout*1000),
29092             upload: this.form.fileUpload ? this.success : undefined
29093         };
29094     }
29095 };
29096
29097 Roo.form.Action.Submit = function(form, options){
29098     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29099 };
29100
29101 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29102     type : 'submit',
29103
29104     haveProgress : false,
29105     uploadComplete : false,
29106     
29107     // uploadProgress indicator.
29108     uploadProgress : function()
29109     {
29110         if (!this.form.progressUrl) {
29111             return;
29112         }
29113         
29114         if (!this.haveProgress) {
29115             Roo.MessageBox.progress("Uploading", "Uploading");
29116         }
29117         if (this.uploadComplete) {
29118            Roo.MessageBox.hide();
29119            return;
29120         }
29121         
29122         this.haveProgress = true;
29123    
29124         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29125         
29126         var c = new Roo.data.Connection();
29127         c.request({
29128             url : this.form.progressUrl,
29129             params: {
29130                 id : uid
29131             },
29132             method: 'GET',
29133             success : function(req){
29134                //console.log(data);
29135                 var rdata = false;
29136                 var edata;
29137                 try  {
29138                    rdata = Roo.decode(req.responseText)
29139                 } catch (e) {
29140                     Roo.log("Invalid data from server..");
29141                     Roo.log(edata);
29142                     return;
29143                 }
29144                 if (!rdata || !rdata.success) {
29145                     Roo.log(rdata);
29146                     Roo.MessageBox.alert(Roo.encode(rdata));
29147                     return;
29148                 }
29149                 var data = rdata.data;
29150                 
29151                 if (this.uploadComplete) {
29152                    Roo.MessageBox.hide();
29153                    return;
29154                 }
29155                    
29156                 if (data){
29157                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29158                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29159                     );
29160                 }
29161                 this.uploadProgress.defer(2000,this);
29162             },
29163        
29164             failure: function(data) {
29165                 Roo.log('progress url failed ');
29166                 Roo.log(data);
29167             },
29168             scope : this
29169         });
29170            
29171     },
29172     
29173     
29174     run : function()
29175     {
29176         // run get Values on the form, so it syncs any secondary forms.
29177         this.form.getValues();
29178         
29179         var o = this.options;
29180         var method = this.getMethod();
29181         var isPost = method == 'POST';
29182         if(o.clientValidation === false || this.form.isValid()){
29183             
29184             if (this.form.progressUrl) {
29185                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29186                     (new Date() * 1) + '' + Math.random());
29187                     
29188             } 
29189             
29190             
29191             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29192                 form:this.form.el.dom,
29193                 url:this.getUrl(!isPost),
29194                 method: method,
29195                 params:isPost ? this.getParams() : null,
29196                 isUpload: this.form.fileUpload
29197             }));
29198             
29199             this.uploadProgress();
29200
29201         }else if (o.clientValidation !== false){ // client validation failed
29202             this.failureType = Roo.form.Action.CLIENT_INVALID;
29203             this.form.afterAction(this, false);
29204         }
29205     },
29206
29207     success : function(response)
29208     {
29209         this.uploadComplete= true;
29210         if (this.haveProgress) {
29211             Roo.MessageBox.hide();
29212         }
29213         
29214         
29215         var result = this.processResponse(response);
29216         if(result === true || result.success){
29217             this.form.afterAction(this, true);
29218             return;
29219         }
29220         if(result.errors){
29221             this.form.markInvalid(result.errors);
29222             this.failureType = Roo.form.Action.SERVER_INVALID;
29223         }
29224         this.form.afterAction(this, false);
29225     },
29226     failure : function(response)
29227     {
29228         this.uploadComplete= true;
29229         if (this.haveProgress) {
29230             Roo.MessageBox.hide();
29231         }
29232         
29233         this.response = response;
29234         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29235         this.form.afterAction(this, false);
29236     },
29237     
29238     handleResponse : function(response){
29239         if(this.form.errorReader){
29240             var rs = this.form.errorReader.read(response);
29241             var errors = [];
29242             if(rs.records){
29243                 for(var i = 0, len = rs.records.length; i < len; i++) {
29244                     var r = rs.records[i];
29245                     errors[i] = r.data;
29246                 }
29247             }
29248             if(errors.length < 1){
29249                 errors = null;
29250             }
29251             return {
29252                 success : rs.success,
29253                 errors : errors
29254             };
29255         }
29256         var ret = false;
29257         try {
29258             ret = Roo.decode(response.responseText);
29259         } catch (e) {
29260             ret = {
29261                 success: false,
29262                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29263                 errors : []
29264             };
29265         }
29266         return ret;
29267         
29268     }
29269 });
29270
29271
29272 Roo.form.Action.Load = function(form, options){
29273     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29274     this.reader = this.form.reader;
29275 };
29276
29277 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29278     type : 'load',
29279
29280     run : function(){
29281         
29282         Roo.Ajax.request(Roo.apply(
29283                 this.createCallback(), {
29284                     method:this.getMethod(),
29285                     url:this.getUrl(false),
29286                     params:this.getParams()
29287         }));
29288     },
29289
29290     success : function(response){
29291         
29292         var result = this.processResponse(response);
29293         if(result === true || !result.success || !result.data){
29294             this.failureType = Roo.form.Action.LOAD_FAILURE;
29295             this.form.afterAction(this, false);
29296             return;
29297         }
29298         this.form.clearInvalid();
29299         this.form.setValues(result.data);
29300         this.form.afterAction(this, true);
29301     },
29302
29303     handleResponse : function(response){
29304         if(this.form.reader){
29305             var rs = this.form.reader.read(response);
29306             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29307             return {
29308                 success : rs.success,
29309                 data : data
29310             };
29311         }
29312         return Roo.decode(response.responseText);
29313     }
29314 });
29315
29316 Roo.form.Action.ACTION_TYPES = {
29317     'load' : Roo.form.Action.Load,
29318     'submit' : Roo.form.Action.Submit
29319 };/*
29320  * Based on:
29321  * Ext JS Library 1.1.1
29322  * Copyright(c) 2006-2007, Ext JS, LLC.
29323  *
29324  * Originally Released Under LGPL - original licence link has changed is not relivant.
29325  *
29326  * Fork - LGPL
29327  * <script type="text/javascript">
29328  */
29329  
29330 /**
29331  * @class Roo.form.Layout
29332  * @extends Roo.Component
29333  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29334  * @constructor
29335  * @param {Object} config Configuration options
29336  */
29337 Roo.form.Layout = function(config){
29338     var xitems = [];
29339     if (config.items) {
29340         xitems = config.items;
29341         delete config.items;
29342     }
29343     Roo.form.Layout.superclass.constructor.call(this, config);
29344     this.stack = [];
29345     Roo.each(xitems, this.addxtype, this);
29346      
29347 };
29348
29349 Roo.extend(Roo.form.Layout, Roo.Component, {
29350     /**
29351      * @cfg {String/Object} autoCreate
29352      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29353      */
29354     /**
29355      * @cfg {String/Object/Function} style
29356      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29357      * a function which returns such a specification.
29358      */
29359     /**
29360      * @cfg {String} labelAlign
29361      * Valid values are "left," "top" and "right" (defaults to "left")
29362      */
29363     /**
29364      * @cfg {Number} labelWidth
29365      * Fixed width in pixels of all field labels (defaults to undefined)
29366      */
29367     /**
29368      * @cfg {Boolean} clear
29369      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29370      */
29371     clear : true,
29372     /**
29373      * @cfg {String} labelSeparator
29374      * The separator to use after field labels (defaults to ':')
29375      */
29376     labelSeparator : ':',
29377     /**
29378      * @cfg {Boolean} hideLabels
29379      * True to suppress the display of field labels in this layout (defaults to false)
29380      */
29381     hideLabels : false,
29382
29383     // private
29384     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29385     
29386     isLayout : true,
29387     
29388     // private
29389     onRender : function(ct, position){
29390         if(this.el){ // from markup
29391             this.el = Roo.get(this.el);
29392         }else {  // generate
29393             var cfg = this.getAutoCreate();
29394             this.el = ct.createChild(cfg, position);
29395         }
29396         if(this.style){
29397             this.el.applyStyles(this.style);
29398         }
29399         if(this.labelAlign){
29400             this.el.addClass('x-form-label-'+this.labelAlign);
29401         }
29402         if(this.hideLabels){
29403             this.labelStyle = "display:none";
29404             this.elementStyle = "padding-left:0;";
29405         }else{
29406             if(typeof this.labelWidth == 'number'){
29407                 this.labelStyle = "width:"+this.labelWidth+"px;";
29408                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29409             }
29410             if(this.labelAlign == 'top'){
29411                 this.labelStyle = "width:auto;";
29412                 this.elementStyle = "padding-left:0;";
29413             }
29414         }
29415         var stack = this.stack;
29416         var slen = stack.length;
29417         if(slen > 0){
29418             if(!this.fieldTpl){
29419                 var t = new Roo.Template(
29420                     '<div class="x-form-item {5}">',
29421                         '<label for="{0}" style="{2}">{1}{4}</label>',
29422                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29423                         '</div>',
29424                     '</div><div class="x-form-clear-left"></div>'
29425                 );
29426                 t.disableFormats = true;
29427                 t.compile();
29428                 Roo.form.Layout.prototype.fieldTpl = t;
29429             }
29430             for(var i = 0; i < slen; i++) {
29431                 if(stack[i].isFormField){
29432                     this.renderField(stack[i]);
29433                 }else{
29434                     this.renderComponent(stack[i]);
29435                 }
29436             }
29437         }
29438         if(this.clear){
29439             this.el.createChild({cls:'x-form-clear'});
29440         }
29441     },
29442
29443     // private
29444     renderField : function(f){
29445         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29446                f.id, //0
29447                f.fieldLabel, //1
29448                f.labelStyle||this.labelStyle||'', //2
29449                this.elementStyle||'', //3
29450                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29451                f.itemCls||this.itemCls||''  //5
29452        ], true).getPrevSibling());
29453     },
29454
29455     // private
29456     renderComponent : function(c){
29457         c.render(c.isLayout ? this.el : this.el.createChild());    
29458     },
29459     /**
29460      * Adds a object form elements (using the xtype property as the factory method.)
29461      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29462      * @param {Object} config 
29463      */
29464     addxtype : function(o)
29465     {
29466         // create the lement.
29467         o.form = this.form;
29468         var fe = Roo.factory(o, Roo.form);
29469         this.form.allItems.push(fe);
29470         this.stack.push(fe);
29471         
29472         if (fe.isFormField) {
29473             this.form.items.add(fe);
29474         }
29475          
29476         return fe;
29477     }
29478 });
29479
29480 /**
29481  * @class Roo.form.Column
29482  * @extends Roo.form.Layout
29483  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29484  * @constructor
29485  * @param {Object} config Configuration options
29486  */
29487 Roo.form.Column = function(config){
29488     Roo.form.Column.superclass.constructor.call(this, config);
29489 };
29490
29491 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29492     /**
29493      * @cfg {Number/String} width
29494      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29495      */
29496     /**
29497      * @cfg {String/Object} autoCreate
29498      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29499      */
29500
29501     // private
29502     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29503
29504     // private
29505     onRender : function(ct, position){
29506         Roo.form.Column.superclass.onRender.call(this, ct, position);
29507         if(this.width){
29508             this.el.setWidth(this.width);
29509         }
29510     }
29511 });
29512
29513
29514 /**
29515  * @class Roo.form.Row
29516  * @extends Roo.form.Layout
29517  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29518  * @constructor
29519  * @param {Object} config Configuration options
29520  */
29521
29522  
29523 Roo.form.Row = function(config){
29524     Roo.form.Row.superclass.constructor.call(this, config);
29525 };
29526  
29527 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29528       /**
29529      * @cfg {Number/String} width
29530      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29531      */
29532     /**
29533      * @cfg {Number/String} height
29534      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29535      */
29536     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29537     
29538     padWidth : 20,
29539     // private
29540     onRender : function(ct, position){
29541         //console.log('row render');
29542         if(!this.rowTpl){
29543             var t = new Roo.Template(
29544                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29545                     '<label for="{0}" style="{2}">{1}{4}</label>',
29546                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29547                     '</div>',
29548                 '</div>'
29549             );
29550             t.disableFormats = true;
29551             t.compile();
29552             Roo.form.Layout.prototype.rowTpl = t;
29553         }
29554         this.fieldTpl = this.rowTpl;
29555         
29556         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29557         var labelWidth = 100;
29558         
29559         if ((this.labelAlign != 'top')) {
29560             if (typeof this.labelWidth == 'number') {
29561                 labelWidth = this.labelWidth
29562             }
29563             this.padWidth =  20 + labelWidth;
29564             
29565         }
29566         
29567         Roo.form.Column.superclass.onRender.call(this, ct, position);
29568         if(this.width){
29569             this.el.setWidth(this.width);
29570         }
29571         if(this.height){
29572             this.el.setHeight(this.height);
29573         }
29574     },
29575     
29576     // private
29577     renderField : function(f){
29578         f.fieldEl = this.fieldTpl.append(this.el, [
29579                f.id, f.fieldLabel,
29580                f.labelStyle||this.labelStyle||'',
29581                this.elementStyle||'',
29582                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29583                f.itemCls||this.itemCls||'',
29584                f.width ? f.width + this.padWidth : 160 + this.padWidth
29585        ],true);
29586     }
29587 });
29588  
29589
29590 /**
29591  * @class Roo.form.FieldSet
29592  * @extends Roo.form.Layout
29593  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29594  * @constructor
29595  * @param {Object} config Configuration options
29596  */
29597 Roo.form.FieldSet = function(config){
29598     Roo.form.FieldSet.superclass.constructor.call(this, config);
29599 };
29600
29601 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29602     /**
29603      * @cfg {String} legend
29604      * The text to display as the legend for the FieldSet (defaults to '')
29605      */
29606     /**
29607      * @cfg {String/Object} autoCreate
29608      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29609      */
29610
29611     // private
29612     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29613
29614     // private
29615     onRender : function(ct, position){
29616         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29617         if(this.legend){
29618             this.setLegend(this.legend);
29619         }
29620     },
29621
29622     // private
29623     setLegend : function(text){
29624         if(this.rendered){
29625             this.el.child('legend').update(text);
29626         }
29627     }
29628 });/*
29629  * Based on:
29630  * Ext JS Library 1.1.1
29631  * Copyright(c) 2006-2007, Ext JS, LLC.
29632  *
29633  * Originally Released Under LGPL - original licence link has changed is not relivant.
29634  *
29635  * Fork - LGPL
29636  * <script type="text/javascript">
29637  */
29638 /**
29639  * @class Roo.form.VTypes
29640  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29641  * @singleton
29642  */
29643 Roo.form.VTypes = function(){
29644     // closure these in so they are only created once.
29645     var alpha = /^[a-zA-Z_]+$/;
29646     var alphanum = /^[a-zA-Z0-9_]+$/;
29647     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29648     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29649
29650     // All these messages and functions are configurable
29651     return {
29652         /**
29653          * The function used to validate email addresses
29654          * @param {String} value The email address
29655          */
29656         'email' : function(v){
29657             return email.test(v);
29658         },
29659         /**
29660          * The error text to display when the email validation function returns false
29661          * @type String
29662          */
29663         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29664         /**
29665          * The keystroke filter mask to be applied on email input
29666          * @type RegExp
29667          */
29668         'emailMask' : /[a-z0-9_\.\-@]/i,
29669
29670         /**
29671          * The function used to validate URLs
29672          * @param {String} value The URL
29673          */
29674         'url' : function(v){
29675             return url.test(v);
29676         },
29677         /**
29678          * The error text to display when the url validation function returns false
29679          * @type String
29680          */
29681         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29682         
29683         /**
29684          * The function used to validate alpha values
29685          * @param {String} value The value
29686          */
29687         'alpha' : function(v){
29688             return alpha.test(v);
29689         },
29690         /**
29691          * The error text to display when the alpha validation function returns false
29692          * @type String
29693          */
29694         'alphaText' : 'This field should only contain letters and _',
29695         /**
29696          * The keystroke filter mask to be applied on alpha input
29697          * @type RegExp
29698          */
29699         'alphaMask' : /[a-z_]/i,
29700
29701         /**
29702          * The function used to validate alphanumeric values
29703          * @param {String} value The value
29704          */
29705         'alphanum' : function(v){
29706             return alphanum.test(v);
29707         },
29708         /**
29709          * The error text to display when the alphanumeric validation function returns false
29710          * @type String
29711          */
29712         'alphanumText' : 'This field should only contain letters, numbers and _',
29713         /**
29714          * The keystroke filter mask to be applied on alphanumeric input
29715          * @type RegExp
29716          */
29717         'alphanumMask' : /[a-z0-9_]/i
29718     };
29719 }();//<script type="text/javascript">
29720
29721 /**
29722  * @class Roo.form.FCKeditor
29723  * @extends Roo.form.TextArea
29724  * Wrapper around the FCKEditor http://www.fckeditor.net
29725  * @constructor
29726  * Creates a new FCKeditor
29727  * @param {Object} config Configuration options
29728  */
29729 Roo.form.FCKeditor = function(config){
29730     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29731     this.addEvents({
29732          /**
29733          * @event editorinit
29734          * Fired when the editor is initialized - you can add extra handlers here..
29735          * @param {FCKeditor} this
29736          * @param {Object} the FCK object.
29737          */
29738         editorinit : true
29739     });
29740     
29741     
29742 };
29743 Roo.form.FCKeditor.editors = { };
29744 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29745 {
29746     //defaultAutoCreate : {
29747     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29748     //},
29749     // private
29750     /**
29751      * @cfg {Object} fck options - see fck manual for details.
29752      */
29753     fckconfig : false,
29754     
29755     /**
29756      * @cfg {Object} fck toolbar set (Basic or Default)
29757      */
29758     toolbarSet : 'Basic',
29759     /**
29760      * @cfg {Object} fck BasePath
29761      */ 
29762     basePath : '/fckeditor/',
29763     
29764     
29765     frame : false,
29766     
29767     value : '',
29768     
29769    
29770     onRender : function(ct, position)
29771     {
29772         if(!this.el){
29773             this.defaultAutoCreate = {
29774                 tag: "textarea",
29775                 style:"width:300px;height:60px;",
29776                 autocomplete: "off"
29777             };
29778         }
29779         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29780         /*
29781         if(this.grow){
29782             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29783             if(this.preventScrollbars){
29784                 this.el.setStyle("overflow", "hidden");
29785             }
29786             this.el.setHeight(this.growMin);
29787         }
29788         */
29789         //console.log('onrender' + this.getId() );
29790         Roo.form.FCKeditor.editors[this.getId()] = this;
29791          
29792
29793         this.replaceTextarea() ;
29794         
29795     },
29796     
29797     getEditor : function() {
29798         return this.fckEditor;
29799     },
29800     /**
29801      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29802      * @param {Mixed} value The value to set
29803      */
29804     
29805     
29806     setValue : function(value)
29807     {
29808         //console.log('setValue: ' + value);
29809         
29810         if(typeof(value) == 'undefined') { // not sure why this is happending...
29811             return;
29812         }
29813         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29814         
29815         //if(!this.el || !this.getEditor()) {
29816         //    this.value = value;
29817             //this.setValue.defer(100,this,[value]);    
29818         //    return;
29819         //} 
29820         
29821         if(!this.getEditor()) {
29822             return;
29823         }
29824         
29825         this.getEditor().SetData(value);
29826         
29827         //
29828
29829     },
29830
29831     /**
29832      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29833      * @return {Mixed} value The field value
29834      */
29835     getValue : function()
29836     {
29837         
29838         if (this.frame && this.frame.dom.style.display == 'none') {
29839             return Roo.form.FCKeditor.superclass.getValue.call(this);
29840         }
29841         
29842         if(!this.el || !this.getEditor()) {
29843            
29844            // this.getValue.defer(100,this); 
29845             return this.value;
29846         }
29847        
29848         
29849         var value=this.getEditor().GetData();
29850         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29851         return Roo.form.FCKeditor.superclass.getValue.call(this);
29852         
29853
29854     },
29855
29856     /**
29857      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29858      * @return {Mixed} value The field value
29859      */
29860     getRawValue : function()
29861     {
29862         if (this.frame && this.frame.dom.style.display == 'none') {
29863             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29864         }
29865         
29866         if(!this.el || !this.getEditor()) {
29867             //this.getRawValue.defer(100,this); 
29868             return this.value;
29869             return;
29870         }
29871         
29872         
29873         
29874         var value=this.getEditor().GetData();
29875         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29876         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29877          
29878     },
29879     
29880     setSize : function(w,h) {
29881         
29882         
29883         
29884         //if (this.frame && this.frame.dom.style.display == 'none') {
29885         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29886         //    return;
29887         //}
29888         //if(!this.el || !this.getEditor()) {
29889         //    this.setSize.defer(100,this, [w,h]); 
29890         //    return;
29891         //}
29892         
29893         
29894         
29895         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29896         
29897         this.frame.dom.setAttribute('width', w);
29898         this.frame.dom.setAttribute('height', h);
29899         this.frame.setSize(w,h);
29900         
29901     },
29902     
29903     toggleSourceEdit : function(value) {
29904         
29905       
29906          
29907         this.el.dom.style.display = value ? '' : 'none';
29908         this.frame.dom.style.display = value ?  'none' : '';
29909         
29910     },
29911     
29912     
29913     focus: function(tag)
29914     {
29915         if (this.frame.dom.style.display == 'none') {
29916             return Roo.form.FCKeditor.superclass.focus.call(this);
29917         }
29918         if(!this.el || !this.getEditor()) {
29919             this.focus.defer(100,this, [tag]); 
29920             return;
29921         }
29922         
29923         
29924         
29925         
29926         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29927         this.getEditor().Focus();
29928         if (tgs.length) {
29929             if (!this.getEditor().Selection.GetSelection()) {
29930                 this.focus.defer(100,this, [tag]); 
29931                 return;
29932             }
29933             
29934             
29935             var r = this.getEditor().EditorDocument.createRange();
29936             r.setStart(tgs[0],0);
29937             r.setEnd(tgs[0],0);
29938             this.getEditor().Selection.GetSelection().removeAllRanges();
29939             this.getEditor().Selection.GetSelection().addRange(r);
29940             this.getEditor().Focus();
29941         }
29942         
29943     },
29944     
29945     
29946     
29947     replaceTextarea : function()
29948     {
29949         if ( document.getElementById( this.getId() + '___Frame' ) )
29950             return ;
29951         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29952         //{
29953             // We must check the elements firstly using the Id and then the name.
29954         var oTextarea = document.getElementById( this.getId() );
29955         
29956         var colElementsByName = document.getElementsByName( this.getId() ) ;
29957          
29958         oTextarea.style.display = 'none' ;
29959
29960         if ( oTextarea.tabIndex ) {            
29961             this.TabIndex = oTextarea.tabIndex ;
29962         }
29963         
29964         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29965         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29966         this.frame = Roo.get(this.getId() + '___Frame')
29967     },
29968     
29969     _getConfigHtml : function()
29970     {
29971         var sConfig = '' ;
29972
29973         for ( var o in this.fckconfig ) {
29974             sConfig += sConfig.length > 0  ? '&amp;' : '';
29975             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29976         }
29977
29978         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29979     },
29980     
29981     
29982     _getIFrameHtml : function()
29983     {
29984         var sFile = 'fckeditor.html' ;
29985         /* no idea what this is about..
29986         try
29987         {
29988             if ( (/fcksource=true/i).test( window.top.location.search ) )
29989                 sFile = 'fckeditor.original.html' ;
29990         }
29991         catch (e) { 
29992         */
29993
29994         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29995         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29996         
29997         
29998         var html = '<iframe id="' + this.getId() +
29999             '___Frame" src="' + sLink +
30000             '" width="' + this.width +
30001             '" height="' + this.height + '"' +
30002             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30003             ' frameborder="0" scrolling="no"></iframe>' ;
30004
30005         return html ;
30006     },
30007     
30008     _insertHtmlBefore : function( html, element )
30009     {
30010         if ( element.insertAdjacentHTML )       {
30011             // IE
30012             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30013         } else { // Gecko
30014             var oRange = document.createRange() ;
30015             oRange.setStartBefore( element ) ;
30016             var oFragment = oRange.createContextualFragment( html );
30017             element.parentNode.insertBefore( oFragment, element ) ;
30018         }
30019     }
30020     
30021     
30022   
30023     
30024     
30025     
30026     
30027
30028 });
30029
30030 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30031
30032 function FCKeditor_OnComplete(editorInstance){
30033     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30034     f.fckEditor = editorInstance;
30035     //console.log("loaded");
30036     f.fireEvent('editorinit', f, editorInstance);
30037
30038   
30039
30040  
30041
30042
30043
30044
30045
30046
30047
30048
30049
30050
30051
30052
30053
30054
30055
30056 //<script type="text/javascript">
30057 /**
30058  * @class Roo.form.GridField
30059  * @extends Roo.form.Field
30060  * Embed a grid (or editable grid into a form)
30061  * STATUS ALPHA
30062  * 
30063  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30064  * it needs 
30065  * xgrid.store = Roo.data.Store
30066  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30067  * xgrid.store.reader = Roo.data.JsonReader 
30068  * 
30069  * 
30070  * @constructor
30071  * Creates a new GridField
30072  * @param {Object} config Configuration options
30073  */
30074 Roo.form.GridField = function(config){
30075     Roo.form.GridField.superclass.constructor.call(this, config);
30076      
30077 };
30078
30079 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30080     /**
30081      * @cfg {Number} width  - used to restrict width of grid..
30082      */
30083     width : 100,
30084     /**
30085      * @cfg {Number} height - used to restrict height of grid..
30086      */
30087     height : 50,
30088      /**
30089      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30090          * 
30091          *}
30092      */
30093     xgrid : false, 
30094     /**
30095      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30096      * {tag: "input", type: "checkbox", autocomplete: "off"})
30097      */
30098    // defaultAutoCreate : { tag: 'div' },
30099     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30100     /**
30101      * @cfg {String} addTitle Text to include for adding a title.
30102      */
30103     addTitle : false,
30104     //
30105     onResize : function(){
30106         Roo.form.Field.superclass.onResize.apply(this, arguments);
30107     },
30108
30109     initEvents : function(){
30110         // Roo.form.Checkbox.superclass.initEvents.call(this);
30111         // has no events...
30112        
30113     },
30114
30115
30116     getResizeEl : function(){
30117         return this.wrap;
30118     },
30119
30120     getPositionEl : function(){
30121         return this.wrap;
30122     },
30123
30124     // private
30125     onRender : function(ct, position){
30126         
30127         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30128         var style = this.style;
30129         delete this.style;
30130         
30131         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30132         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30133         this.viewEl = this.wrap.createChild({ tag: 'div' });
30134         if (style) {
30135             this.viewEl.applyStyles(style);
30136         }
30137         if (this.width) {
30138             this.viewEl.setWidth(this.width);
30139         }
30140         if (this.height) {
30141             this.viewEl.setHeight(this.height);
30142         }
30143         //if(this.inputValue !== undefined){
30144         //this.setValue(this.value);
30145         
30146         
30147         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30148         
30149         
30150         this.grid.render();
30151         this.grid.getDataSource().on('remove', this.refreshValue, this);
30152         this.grid.getDataSource().on('update', this.refreshValue, this);
30153         this.grid.on('afteredit', this.refreshValue, this);
30154  
30155     },
30156      
30157     
30158     /**
30159      * Sets the value of the item. 
30160      * @param {String} either an object  or a string..
30161      */
30162     setValue : function(v){
30163         //this.value = v;
30164         v = v || []; // empty set..
30165         // this does not seem smart - it really only affects memoryproxy grids..
30166         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30167             var ds = this.grid.getDataSource();
30168             // assumes a json reader..
30169             var data = {}
30170             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30171             ds.loadData( data);
30172         }
30173         // clear selection so it does not get stale.
30174         if (this.grid.sm) { 
30175             this.grid.sm.clearSelections();
30176         }
30177         
30178         Roo.form.GridField.superclass.setValue.call(this, v);
30179         this.refreshValue();
30180         // should load data in the grid really....
30181     },
30182     
30183     // private
30184     refreshValue: function() {
30185          var val = [];
30186         this.grid.getDataSource().each(function(r) {
30187             val.push(r.data);
30188         });
30189         this.el.dom.value = Roo.encode(val);
30190     }
30191     
30192      
30193     
30194     
30195 });/*
30196  * Based on:
30197  * Ext JS Library 1.1.1
30198  * Copyright(c) 2006-2007, Ext JS, LLC.
30199  *
30200  * Originally Released Under LGPL - original licence link has changed is not relivant.
30201  *
30202  * Fork - LGPL
30203  * <script type="text/javascript">
30204  */
30205 /**
30206  * @class Roo.form.DisplayField
30207  * @extends Roo.form.Field
30208  * A generic Field to display non-editable data.
30209  * @constructor
30210  * Creates a new Display Field item.
30211  * @param {Object} config Configuration options
30212  */
30213 Roo.form.DisplayField = function(config){
30214     Roo.form.DisplayField.superclass.constructor.call(this, config);
30215     
30216 };
30217
30218 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30219     inputType:      'hidden',
30220     allowBlank:     true,
30221     readOnly:         true,
30222     
30223  
30224     /**
30225      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30226      */
30227     focusClass : undefined,
30228     /**
30229      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30230      */
30231     fieldClass: 'x-form-field',
30232     
30233      /**
30234      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30235      */
30236     valueRenderer: undefined,
30237     
30238     width: 100,
30239     /**
30240      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30241      * {tag: "input", type: "checkbox", autocomplete: "off"})
30242      */
30243      
30244  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30245
30246     onResize : function(){
30247         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30248         
30249     },
30250
30251     initEvents : function(){
30252         // Roo.form.Checkbox.superclass.initEvents.call(this);
30253         // has no events...
30254        
30255     },
30256
30257
30258     getResizeEl : function(){
30259         return this.wrap;
30260     },
30261
30262     getPositionEl : function(){
30263         return this.wrap;
30264     },
30265
30266     // private
30267     onRender : function(ct, position){
30268         
30269         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30270         //if(this.inputValue !== undefined){
30271         this.wrap = this.el.wrap();
30272         
30273         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30274         
30275         if (this.bodyStyle) {
30276             this.viewEl.applyStyles(this.bodyStyle);
30277         }
30278         //this.viewEl.setStyle('padding', '2px');
30279         
30280         this.setValue(this.value);
30281         
30282     },
30283 /*
30284     // private
30285     initValue : Roo.emptyFn,
30286
30287   */
30288
30289         // private
30290     onClick : function(){
30291         
30292     },
30293
30294     /**
30295      * Sets the checked state of the checkbox.
30296      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30297      */
30298     setValue : function(v){
30299         this.value = v;
30300         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30301         // this might be called before we have a dom element..
30302         if (!this.viewEl) {
30303             return;
30304         }
30305         this.viewEl.dom.innerHTML = html;
30306         Roo.form.DisplayField.superclass.setValue.call(this, v);
30307
30308     }
30309 });/*
30310  * 
30311  * Licence- LGPL
30312  * 
30313  */
30314
30315 /**
30316  * @class Roo.form.DayPicker
30317  * @extends Roo.form.Field
30318  * A Day picker show [M] [T] [W] ....
30319  * @constructor
30320  * Creates a new Day Picker
30321  * @param {Object} config Configuration options
30322  */
30323 Roo.form.DayPicker= function(config){
30324     Roo.form.DayPicker.superclass.constructor.call(this, config);
30325      
30326 };
30327
30328 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30329     /**
30330      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30331      */
30332     focusClass : undefined,
30333     /**
30334      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30335      */
30336     fieldClass: "x-form-field",
30337    
30338     /**
30339      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30340      * {tag: "input", type: "checkbox", autocomplete: "off"})
30341      */
30342     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30343     
30344    
30345     actionMode : 'viewEl', 
30346     //
30347     // private
30348  
30349     inputType : 'hidden',
30350     
30351      
30352     inputElement: false, // real input element?
30353     basedOn: false, // ????
30354     
30355     isFormField: true, // not sure where this is needed!!!!
30356
30357     onResize : function(){
30358         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30359         if(!this.boxLabel){
30360             this.el.alignTo(this.wrap, 'c-c');
30361         }
30362     },
30363
30364     initEvents : function(){
30365         Roo.form.Checkbox.superclass.initEvents.call(this);
30366         this.el.on("click", this.onClick,  this);
30367         this.el.on("change", this.onClick,  this);
30368     },
30369
30370
30371     getResizeEl : function(){
30372         return this.wrap;
30373     },
30374
30375     getPositionEl : function(){
30376         return this.wrap;
30377     },
30378
30379     
30380     // private
30381     onRender : function(ct, position){
30382         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30383        
30384         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30385         
30386         var r1 = '<table><tr>';
30387         var r2 = '<tr class="x-form-daypick-icons">';
30388         for (var i=0; i < 7; i++) {
30389             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30390             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30391         }
30392         
30393         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30394         viewEl.select('img').on('click', this.onClick, this);
30395         this.viewEl = viewEl;   
30396         
30397         
30398         // this will not work on Chrome!!!
30399         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30400         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30401         
30402         
30403           
30404
30405     },
30406
30407     // private
30408     initValue : Roo.emptyFn,
30409
30410     /**
30411      * Returns the checked state of the checkbox.
30412      * @return {Boolean} True if checked, else false
30413      */
30414     getValue : function(){
30415         return this.el.dom.value;
30416         
30417     },
30418
30419         // private
30420     onClick : function(e){ 
30421         //this.setChecked(!this.checked);
30422         Roo.get(e.target).toggleClass('x-menu-item-checked');
30423         this.refreshValue();
30424         //if(this.el.dom.checked != this.checked){
30425         //    this.setValue(this.el.dom.checked);
30426        // }
30427     },
30428     
30429     // private
30430     refreshValue : function()
30431     {
30432         var val = '';
30433         this.viewEl.select('img',true).each(function(e,i,n)  {
30434             val += e.is(".x-menu-item-checked") ? String(n) : '';
30435         });
30436         this.setValue(val, true);
30437     },
30438
30439     /**
30440      * Sets the checked state of the checkbox.
30441      * On is always based on a string comparison between inputValue and the param.
30442      * @param {Boolean/String} value - the value to set 
30443      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30444      */
30445     setValue : function(v,suppressEvent){
30446         if (!this.el.dom) {
30447             return;
30448         }
30449         var old = this.el.dom.value ;
30450         this.el.dom.value = v;
30451         if (suppressEvent) {
30452             return ;
30453         }
30454          
30455         // update display..
30456         this.viewEl.select('img',true).each(function(e,i,n)  {
30457             
30458             var on = e.is(".x-menu-item-checked");
30459             var newv = v.indexOf(String(n)) > -1;
30460             if (on != newv) {
30461                 e.toggleClass('x-menu-item-checked');
30462             }
30463             
30464         });
30465         
30466         
30467         this.fireEvent('change', this, v, old);
30468         
30469         
30470     },
30471    
30472     // handle setting of hidden value by some other method!!?!?
30473     setFromHidden: function()
30474     {
30475         if(!this.el){
30476             return;
30477         }
30478         //console.log("SET FROM HIDDEN");
30479         //alert('setFrom hidden');
30480         this.setValue(this.el.dom.value);
30481     },
30482     
30483     onDestroy : function()
30484     {
30485         if(this.viewEl){
30486             Roo.get(this.viewEl).remove();
30487         }
30488          
30489         Roo.form.DayPicker.superclass.onDestroy.call(this);
30490     }
30491
30492 });/*
30493  * RooJS Library 1.1.1
30494  * Copyright(c) 2008-2011  Alan Knowles
30495  *
30496  * License - LGPL
30497  */
30498  
30499
30500 /**
30501  * @class Roo.form.ComboCheck
30502  * @extends Roo.form.ComboBox
30503  * A combobox for multiple select items.
30504  *
30505  * FIXME - could do with a reset button..
30506  * 
30507  * @constructor
30508  * Create a new ComboCheck
30509  * @param {Object} config Configuration options
30510  */
30511 Roo.form.ComboCheck = function(config){
30512     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30513     // should verify some data...
30514     // like
30515     // hiddenName = required..
30516     // displayField = required
30517     // valudField == required
30518     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30519     var _t = this;
30520     Roo.each(req, function(e) {
30521         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30522             throw "Roo.form.ComboCheck : missing value for: " + e;
30523         }
30524     });
30525     
30526     
30527 };
30528
30529 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30530      
30531      
30532     editable : false,
30533      
30534     selectedClass: 'x-menu-item-checked', 
30535     
30536     // private
30537     onRender : function(ct, position){
30538         var _t = this;
30539         
30540         
30541         
30542         if(!this.tpl){
30543             var cls = 'x-combo-list';
30544
30545             
30546             this.tpl =  new Roo.Template({
30547                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30548                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30549                    '<span>{' + this.displayField + '}</span>' +
30550                     '</div>' 
30551                 
30552             });
30553         }
30554  
30555         
30556         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30557         this.view.singleSelect = false;
30558         this.view.multiSelect = true;
30559         this.view.toggleSelect = true;
30560         this.pageTb.add(new Roo.Toolbar.Fill(), {
30561             
30562             text: 'Done',
30563             handler: function()
30564             {
30565                 _t.collapse();
30566             }
30567         });
30568     },
30569     
30570     onViewOver : function(e, t){
30571         // do nothing...
30572         return;
30573         
30574     },
30575     
30576     onViewClick : function(doFocus,index){
30577         return;
30578         
30579     },
30580     select: function () {
30581         //Roo.log("SELECT CALLED");
30582     },
30583      
30584     selectByValue : function(xv, scrollIntoView){
30585         var ar = this.getValueArray();
30586         var sels = [];
30587         
30588         Roo.each(ar, function(v) {
30589             if(v === undefined || v === null){
30590                 return;
30591             }
30592             var r = this.findRecord(this.valueField, v);
30593             if(r){
30594                 sels.push(this.store.indexOf(r))
30595                 
30596             }
30597         },this);
30598         this.view.select(sels);
30599         return false;
30600     },
30601     
30602     
30603     
30604     onSelect : function(record, index){
30605        // Roo.log("onselect Called");
30606        // this is only called by the clear button now..
30607         this.view.clearSelections();
30608         this.setValue('[]');
30609         if (this.value != this.valueBefore) {
30610             this.fireEvent('change', this, this.value, this.valueBefore);
30611             this.valueBefore = this.value;
30612         }
30613     },
30614     getValueArray : function()
30615     {
30616         var ar = [] ;
30617         
30618         try {
30619             //Roo.log(this.value);
30620             if (typeof(this.value) == 'undefined') {
30621                 return [];
30622             }
30623             var ar = Roo.decode(this.value);
30624             return  ar instanceof Array ? ar : []; //?? valid?
30625             
30626         } catch(e) {
30627             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30628             return [];
30629         }
30630          
30631     },
30632     expand : function ()
30633     {
30634         
30635         Roo.form.ComboCheck.superclass.expand.call(this);
30636         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30637         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30638         
30639
30640     },
30641     
30642     collapse : function(){
30643         Roo.form.ComboCheck.superclass.collapse.call(this);
30644         var sl = this.view.getSelectedIndexes();
30645         var st = this.store;
30646         var nv = [];
30647         var tv = [];
30648         var r;
30649         Roo.each(sl, function(i) {
30650             r = st.getAt(i);
30651             nv.push(r.get(this.valueField));
30652         },this);
30653         this.setValue(Roo.encode(nv));
30654         if (this.value != this.valueBefore) {
30655
30656             this.fireEvent('change', this, this.value, this.valueBefore);
30657             this.valueBefore = this.value;
30658         }
30659         
30660     },
30661     
30662     setValue : function(v){
30663         // Roo.log(v);
30664         this.value = v;
30665         
30666         var vals = this.getValueArray();
30667         var tv = [];
30668         Roo.each(vals, function(k) {
30669             var r = this.findRecord(this.valueField, k);
30670             if(r){
30671                 tv.push(r.data[this.displayField]);
30672             }else if(this.valueNotFoundText !== undefined){
30673                 tv.push( this.valueNotFoundText );
30674             }
30675         },this);
30676        // Roo.log(tv);
30677         
30678         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30679         this.hiddenField.value = v;
30680         this.value = v;
30681     }
30682     
30683 });//<script type="text/javasscript">
30684  
30685
30686 /**
30687  * @class Roo.DDView
30688  * A DnD enabled version of Roo.View.
30689  * @param {Element/String} container The Element in which to create the View.
30690  * @param {String} tpl The template string used to create the markup for each element of the View
30691  * @param {Object} config The configuration properties. These include all the config options of
30692  * {@link Roo.View} plus some specific to this class.<br>
30693  * <p>
30694  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30695  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30696  * <p>
30697  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30698 .x-view-drag-insert-above {
30699         border-top:1px dotted #3366cc;
30700 }
30701 .x-view-drag-insert-below {
30702         border-bottom:1px dotted #3366cc;
30703 }
30704 </code></pre>
30705  * 
30706  */
30707  
30708 Roo.DDView = function(container, tpl, config) {
30709     Roo.DDView.superclass.constructor.apply(this, arguments);
30710     this.getEl().setStyle("outline", "0px none");
30711     this.getEl().unselectable();
30712     if (this.dragGroup) {
30713                 this.setDraggable(this.dragGroup.split(","));
30714     }
30715     if (this.dropGroup) {
30716                 this.setDroppable(this.dropGroup.split(","));
30717     }
30718     if (this.deletable) {
30719         this.setDeletable();
30720     }
30721     this.isDirtyFlag = false;
30722         this.addEvents({
30723                 "drop" : true
30724         });
30725 };
30726
30727 Roo.extend(Roo.DDView, Roo.View, {
30728 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30729 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30730 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30731 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30732
30733         isFormField: true,
30734
30735         reset: Roo.emptyFn,
30736         
30737         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30738
30739         validate: function() {
30740                 return true;
30741         },
30742         
30743         destroy: function() {
30744                 this.purgeListeners();
30745                 this.getEl.removeAllListeners();
30746                 this.getEl().remove();
30747                 if (this.dragZone) {
30748                         if (this.dragZone.destroy) {
30749                                 this.dragZone.destroy();
30750                         }
30751                 }
30752                 if (this.dropZone) {
30753                         if (this.dropZone.destroy) {
30754                                 this.dropZone.destroy();
30755                         }
30756                 }
30757         },
30758
30759 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30760         getName: function() {
30761                 return this.name;
30762         },
30763
30764 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30765         setValue: function(v) {
30766                 if (!this.store) {
30767                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30768                 }
30769                 var data = {};
30770                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30771                 this.store.proxy = new Roo.data.MemoryProxy(data);
30772                 this.store.load();
30773         },
30774
30775 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30776         getValue: function() {
30777                 var result = '(';
30778                 this.store.each(function(rec) {
30779                         result += rec.id + ',';
30780                 });
30781                 return result.substr(0, result.length - 1) + ')';
30782         },
30783         
30784         getIds: function() {
30785                 var i = 0, result = new Array(this.store.getCount());
30786                 this.store.each(function(rec) {
30787                         result[i++] = rec.id;
30788                 });
30789                 return result;
30790         },
30791         
30792         isDirty: function() {
30793                 return this.isDirtyFlag;
30794         },
30795
30796 /**
30797  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30798  *      whole Element becomes the target, and this causes the drop gesture to append.
30799  */
30800     getTargetFromEvent : function(e) {
30801                 var target = e.getTarget();
30802                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30803                 target = target.parentNode;
30804                 }
30805                 if (!target) {
30806                         target = this.el.dom.lastChild || this.el.dom;
30807                 }
30808                 return target;
30809     },
30810
30811 /**
30812  *      Create the drag data which consists of an object which has the property "ddel" as
30813  *      the drag proxy element. 
30814  */
30815     getDragData : function(e) {
30816         var target = this.findItemFromChild(e.getTarget());
30817                 if(target) {
30818                         this.handleSelection(e);
30819                         var selNodes = this.getSelectedNodes();
30820             var dragData = {
30821                 source: this,
30822                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30823                 nodes: selNodes,
30824                 records: []
30825                         };
30826                         var selectedIndices = this.getSelectedIndexes();
30827                         for (var i = 0; i < selectedIndices.length; i++) {
30828                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30829                         }
30830                         if (selNodes.length == 1) {
30831                                 dragData.ddel = target.cloneNode(true); // the div element
30832                         } else {
30833                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30834                                 div.className = 'multi-proxy';
30835                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30836                                         div.appendChild(selNodes[i].cloneNode(true));
30837                                 }
30838                                 dragData.ddel = div;
30839                         }
30840             //console.log(dragData)
30841             //console.log(dragData.ddel.innerHTML)
30842                         return dragData;
30843                 }
30844         //console.log('nodragData')
30845                 return false;
30846     },
30847     
30848 /**     Specify to which ddGroup items in this DDView may be dragged. */
30849     setDraggable: function(ddGroup) {
30850         if (ddGroup instanceof Array) {
30851                 Roo.each(ddGroup, this.setDraggable, this);
30852                 return;
30853         }
30854         if (this.dragZone) {
30855                 this.dragZone.addToGroup(ddGroup);
30856         } else {
30857                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30858                                 containerScroll: true,
30859                                 ddGroup: ddGroup 
30860
30861                         });
30862 //                      Draggability implies selection. DragZone's mousedown selects the element.
30863                         if (!this.multiSelect) { this.singleSelect = true; }
30864
30865 //                      Wire the DragZone's handlers up to methods in *this*
30866                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30867                 }
30868     },
30869
30870 /**     Specify from which ddGroup this DDView accepts drops. */
30871     setDroppable: function(ddGroup) {
30872         if (ddGroup instanceof Array) {
30873                 Roo.each(ddGroup, this.setDroppable, this);
30874                 return;
30875         }
30876         if (this.dropZone) {
30877                 this.dropZone.addToGroup(ddGroup);
30878         } else {
30879                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30880                                 containerScroll: true,
30881                                 ddGroup: ddGroup
30882                         });
30883
30884 //                      Wire the DropZone's handlers up to methods in *this*
30885                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30886                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30887                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30888                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30889                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30890                 }
30891     },
30892
30893 /**     Decide whether to drop above or below a View node. */
30894     getDropPoint : function(e, n, dd){
30895         if (n == this.el.dom) { return "above"; }
30896                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30897                 var c = t + (b - t) / 2;
30898                 var y = Roo.lib.Event.getPageY(e);
30899                 if(y <= c) {
30900                         return "above";
30901                 }else{
30902                         return "below";
30903                 }
30904     },
30905
30906     onNodeEnter : function(n, dd, e, data){
30907                 return false;
30908     },
30909     
30910     onNodeOver : function(n, dd, e, data){
30911                 var pt = this.getDropPoint(e, n, dd);
30912                 // set the insert point style on the target node
30913                 var dragElClass = this.dropNotAllowed;
30914                 if (pt) {
30915                         var targetElClass;
30916                         if (pt == "above"){
30917                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30918                                 targetElClass = "x-view-drag-insert-above";
30919                         } else {
30920                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30921                                 targetElClass = "x-view-drag-insert-below";
30922                         }
30923                         if (this.lastInsertClass != targetElClass){
30924                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30925                                 this.lastInsertClass = targetElClass;
30926                         }
30927                 }
30928                 return dragElClass;
30929         },
30930
30931     onNodeOut : function(n, dd, e, data){
30932                 this.removeDropIndicators(n);
30933     },
30934
30935     onNodeDrop : function(n, dd, e, data){
30936         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30937                 return false;
30938         }
30939         var pt = this.getDropPoint(e, n, dd);
30940                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30941                 if (pt == "below") { insertAt++; }
30942                 for (var i = 0; i < data.records.length; i++) {
30943                         var r = data.records[i];
30944                         var dup = this.store.getById(r.id);
30945                         if (dup && (dd != this.dragZone)) {
30946                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30947                         } else {
30948                                 if (data.copy) {
30949                                         this.store.insert(insertAt++, r.copy());
30950                                 } else {
30951                                         data.source.isDirtyFlag = true;
30952                                         r.store.remove(r);
30953                                         this.store.insert(insertAt++, r);
30954                                 }
30955                                 this.isDirtyFlag = true;
30956                         }
30957                 }
30958                 this.dragZone.cachedTarget = null;
30959                 return true;
30960     },
30961
30962     removeDropIndicators : function(n){
30963                 if(n){
30964                         Roo.fly(n).removeClass([
30965                                 "x-view-drag-insert-above",
30966                                 "x-view-drag-insert-below"]);
30967                         this.lastInsertClass = "_noclass";
30968                 }
30969     },
30970
30971 /**
30972  *      Utility method. Add a delete option to the DDView's context menu.
30973  *      @param {String} imageUrl The URL of the "delete" icon image.
30974  */
30975         setDeletable: function(imageUrl) {
30976                 if (!this.singleSelect && !this.multiSelect) {
30977                         this.singleSelect = true;
30978                 }
30979                 var c = this.getContextMenu();
30980                 this.contextMenu.on("itemclick", function(item) {
30981                         switch (item.id) {
30982                                 case "delete":
30983                                         this.remove(this.getSelectedIndexes());
30984                                         break;
30985                         }
30986                 }, this);
30987                 this.contextMenu.add({
30988                         icon: imageUrl,
30989                         id: "delete",
30990                         text: 'Delete'
30991                 });
30992         },
30993         
30994 /**     Return the context menu for this DDView. */
30995         getContextMenu: function() {
30996                 if (!this.contextMenu) {
30997 //                      Create the View's context menu
30998                         this.contextMenu = new Roo.menu.Menu({
30999                                 id: this.id + "-contextmenu"
31000                         });
31001                         this.el.on("contextmenu", this.showContextMenu, this);
31002                 }
31003                 return this.contextMenu;
31004         },
31005         
31006         disableContextMenu: function() {
31007                 if (this.contextMenu) {
31008                         this.el.un("contextmenu", this.showContextMenu, this);
31009                 }
31010         },
31011
31012         showContextMenu: function(e, item) {
31013         item = this.findItemFromChild(e.getTarget());
31014                 if (item) {
31015                         e.stopEvent();
31016                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
31017                         this.contextMenu.showAt(e.getXY());
31018             }
31019     },
31020
31021 /**
31022  *      Remove {@link Roo.data.Record}s at the specified indices.
31023  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
31024  */
31025     remove: function(selectedIndices) {
31026                 selectedIndices = [].concat(selectedIndices);
31027                 for (var i = 0; i < selectedIndices.length; i++) {
31028                         var rec = this.store.getAt(selectedIndices[i]);
31029                         this.store.remove(rec);
31030                 }
31031     },
31032
31033 /**
31034  *      Double click fires the event, but also, if this is draggable, and there is only one other
31035  *      related DropZone, it transfers the selected node.
31036  */
31037     onDblClick : function(e){
31038         var item = this.findItemFromChild(e.getTarget());
31039         if(item){
31040             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
31041                 return false;
31042             }
31043             if (this.dragGroup) {
31044                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
31045                     while (targets.indexOf(this.dropZone) > -1) {
31046                             targets.remove(this.dropZone);
31047                                 }
31048                     if (targets.length == 1) {
31049                                         this.dragZone.cachedTarget = null;
31050                         var el = Roo.get(targets[0].getEl());
31051                         var box = el.getBox(true);
31052                         targets[0].onNodeDrop(el.dom, {
31053                                 target: el.dom,
31054                                 xy: [box.x, box.y + box.height - 1]
31055                         }, null, this.getDragData(e));
31056                     }
31057                 }
31058         }
31059     },
31060     
31061     handleSelection: function(e) {
31062                 this.dragZone.cachedTarget = null;
31063         var item = this.findItemFromChild(e.getTarget());
31064         if (!item) {
31065                 this.clearSelections(true);
31066                 return;
31067         }
31068                 if (item && (this.multiSelect || this.singleSelect)){
31069                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
31070                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
31071                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
31072                                 this.unselect(item);
31073                         } else {
31074                                 this.select(item, this.multiSelect && e.ctrlKey);
31075                                 this.lastSelection = item;
31076                         }
31077                 }
31078     },
31079
31080     onItemClick : function(item, index, e){
31081                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
31082                         return false;
31083                 }
31084                 return true;
31085     },
31086
31087     unselect : function(nodeInfo, suppressEvent){
31088                 var node = this.getNode(nodeInfo);
31089                 if(node && this.isSelected(node)){
31090                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
31091                                 Roo.fly(node).removeClass(this.selectedClass);
31092                                 this.selections.remove(node);
31093                                 if(!suppressEvent){
31094                                         this.fireEvent("selectionchange", this, this.selections);
31095                                 }
31096                         }
31097                 }
31098     }
31099 });
31100 /*
31101  * Based on:
31102  * Ext JS Library 1.1.1
31103  * Copyright(c) 2006-2007, Ext JS, LLC.
31104  *
31105  * Originally Released Under LGPL - original licence link has changed is not relivant.
31106  *
31107  * Fork - LGPL
31108  * <script type="text/javascript">
31109  */
31110  
31111 /**
31112  * @class Roo.LayoutManager
31113  * @extends Roo.util.Observable
31114  * Base class for layout managers.
31115  */
31116 Roo.LayoutManager = function(container, config){
31117     Roo.LayoutManager.superclass.constructor.call(this);
31118     this.el = Roo.get(container);
31119     // ie scrollbar fix
31120     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31121         document.body.scroll = "no";
31122     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31123         this.el.position('relative');
31124     }
31125     this.id = this.el.id;
31126     this.el.addClass("x-layout-container");
31127     /** false to disable window resize monitoring @type Boolean */
31128     this.monitorWindowResize = true;
31129     this.regions = {};
31130     this.addEvents({
31131         /**
31132          * @event layout
31133          * Fires when a layout is performed. 
31134          * @param {Roo.LayoutManager} this
31135          */
31136         "layout" : true,
31137         /**
31138          * @event regionresized
31139          * Fires when the user resizes a region. 
31140          * @param {Roo.LayoutRegion} region The resized region
31141          * @param {Number} newSize The new size (width for east/west, height for north/south)
31142          */
31143         "regionresized" : true,
31144         /**
31145          * @event regioncollapsed
31146          * Fires when a region is collapsed. 
31147          * @param {Roo.LayoutRegion} region The collapsed region
31148          */
31149         "regioncollapsed" : true,
31150         /**
31151          * @event regionexpanded
31152          * Fires when a region is expanded.  
31153          * @param {Roo.LayoutRegion} region The expanded region
31154          */
31155         "regionexpanded" : true
31156     });
31157     this.updating = false;
31158     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31159 };
31160
31161 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
31162     /**
31163      * Returns true if this layout is currently being updated
31164      * @return {Boolean}
31165      */
31166     isUpdating : function(){
31167         return this.updating; 
31168     },
31169     
31170     /**
31171      * Suspend the LayoutManager from doing auto-layouts while
31172      * making multiple add or remove calls
31173      */
31174     beginUpdate : function(){
31175         this.updating = true;    
31176     },
31177     
31178     /**
31179      * Restore auto-layouts and optionally disable the manager from performing a layout
31180      * @param {Boolean} noLayout true to disable a layout update 
31181      */
31182     endUpdate : function(noLayout){
31183         this.updating = false;
31184         if(!noLayout){
31185             this.layout();
31186         }    
31187     },
31188     
31189     layout: function(){
31190         
31191     },
31192     
31193     onRegionResized : function(region, newSize){
31194         this.fireEvent("regionresized", region, newSize);
31195         this.layout();
31196     },
31197     
31198     onRegionCollapsed : function(region){
31199         this.fireEvent("regioncollapsed", region);
31200     },
31201     
31202     onRegionExpanded : function(region){
31203         this.fireEvent("regionexpanded", region);
31204     },
31205         
31206     /**
31207      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31208      * performs box-model adjustments.
31209      * @return {Object} The size as an object {width: (the width), height: (the height)}
31210      */
31211     getViewSize : function(){
31212         var size;
31213         if(this.el.dom != document.body){
31214             size = this.el.getSize();
31215         }else{
31216             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31217         }
31218         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31219         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31220         return size;
31221     },
31222     
31223     /**
31224      * Returns the Element this layout is bound to.
31225      * @return {Roo.Element}
31226      */
31227     getEl : function(){
31228         return this.el;
31229     },
31230     
31231     /**
31232      * Returns the specified region.
31233      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31234      * @return {Roo.LayoutRegion}
31235      */
31236     getRegion : function(target){
31237         return this.regions[target.toLowerCase()];
31238     },
31239     
31240     onWindowResize : function(){
31241         if(this.monitorWindowResize){
31242             this.layout();
31243         }
31244     }
31245 });/*
31246  * Based on:
31247  * Ext JS Library 1.1.1
31248  * Copyright(c) 2006-2007, Ext JS, LLC.
31249  *
31250  * Originally Released Under LGPL - original licence link has changed is not relivant.
31251  *
31252  * Fork - LGPL
31253  * <script type="text/javascript">
31254  */
31255 /**
31256  * @class Roo.BorderLayout
31257  * @extends Roo.LayoutManager
31258  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31259  * please see: <br><br>
31260  * <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>
31261  * <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>
31262  * Example:
31263  <pre><code>
31264  var layout = new Roo.BorderLayout(document.body, {
31265     north: {
31266         initialSize: 25,
31267         titlebar: false
31268     },
31269     west: {
31270         split:true,
31271         initialSize: 200,
31272         minSize: 175,
31273         maxSize: 400,
31274         titlebar: true,
31275         collapsible: true
31276     },
31277     east: {
31278         split:true,
31279         initialSize: 202,
31280         minSize: 175,
31281         maxSize: 400,
31282         titlebar: true,
31283         collapsible: true
31284     },
31285     south: {
31286         split:true,
31287         initialSize: 100,
31288         minSize: 100,
31289         maxSize: 200,
31290         titlebar: true,
31291         collapsible: true
31292     },
31293     center: {
31294         titlebar: true,
31295         autoScroll:true,
31296         resizeTabs: true,
31297         minTabWidth: 50,
31298         preferredTabWidth: 150
31299     }
31300 });
31301
31302 // shorthand
31303 var CP = Roo.ContentPanel;
31304
31305 layout.beginUpdate();
31306 layout.add("north", new CP("north", "North"));
31307 layout.add("south", new CP("south", {title: "South", closable: true}));
31308 layout.add("west", new CP("west", {title: "West"}));
31309 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31310 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31311 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31312 layout.getRegion("center").showPanel("center1");
31313 layout.endUpdate();
31314 </code></pre>
31315
31316 <b>The container the layout is rendered into can be either the body element or any other element.
31317 If it is not the body element, the container needs to either be an absolute positioned element,
31318 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31319 the container size if it is not the body element.</b>
31320
31321 * @constructor
31322 * Create a new BorderLayout
31323 * @param {String/HTMLElement/Element} container The container this layout is bound to
31324 * @param {Object} config Configuration options
31325  */
31326 Roo.BorderLayout = function(container, config){
31327     config = config || {};
31328     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31329     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31330     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31331         var target = this.factory.validRegions[i];
31332         if(config[target]){
31333             this.addRegion(target, config[target]);
31334         }
31335     }
31336 };
31337
31338 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31339     /**
31340      * Creates and adds a new region if it doesn't already exist.
31341      * @param {String} target The target region key (north, south, east, west or center).
31342      * @param {Object} config The regions config object
31343      * @return {BorderLayoutRegion} The new region
31344      */
31345     addRegion : function(target, config){
31346         if(!this.regions[target]){
31347             var r = this.factory.create(target, this, config);
31348             this.bindRegion(target, r);
31349         }
31350         return this.regions[target];
31351     },
31352
31353     // private (kinda)
31354     bindRegion : function(name, r){
31355         this.regions[name] = r;
31356         r.on("visibilitychange", this.layout, this);
31357         r.on("paneladded", this.layout, this);
31358         r.on("panelremoved", this.layout, this);
31359         r.on("invalidated", this.layout, this);
31360         r.on("resized", this.onRegionResized, this);
31361         r.on("collapsed", this.onRegionCollapsed, this);
31362         r.on("expanded", this.onRegionExpanded, this);
31363     },
31364
31365     /**
31366      * Performs a layout update.
31367      */
31368     layout : function(){
31369         if(this.updating) return;
31370         var size = this.getViewSize();
31371         var w = size.width;
31372         var h = size.height;
31373         var centerW = w;
31374         var centerH = h;
31375         var centerY = 0;
31376         var centerX = 0;
31377         //var x = 0, y = 0;
31378
31379         var rs = this.regions;
31380         var north = rs["north"];
31381         var south = rs["south"]; 
31382         var west = rs["west"];
31383         var east = rs["east"];
31384         var center = rs["center"];
31385         //if(this.hideOnLayout){ // not supported anymore
31386             //c.el.setStyle("display", "none");
31387         //}
31388         if(north && north.isVisible()){
31389             var b = north.getBox();
31390             var m = north.getMargins();
31391             b.width = w - (m.left+m.right);
31392             b.x = m.left;
31393             b.y = m.top;
31394             centerY = b.height + b.y + m.bottom;
31395             centerH -= centerY;
31396             north.updateBox(this.safeBox(b));
31397         }
31398         if(south && south.isVisible()){
31399             var b = south.getBox();
31400             var m = south.getMargins();
31401             b.width = w - (m.left+m.right);
31402             b.x = m.left;
31403             var totalHeight = (b.height + m.top + m.bottom);
31404             b.y = h - totalHeight + m.top;
31405             centerH -= totalHeight;
31406             south.updateBox(this.safeBox(b));
31407         }
31408         if(west && west.isVisible()){
31409             var b = west.getBox();
31410             var m = west.getMargins();
31411             b.height = centerH - (m.top+m.bottom);
31412             b.x = m.left;
31413             b.y = centerY + m.top;
31414             var totalWidth = (b.width + m.left + m.right);
31415             centerX += totalWidth;
31416             centerW -= totalWidth;
31417             west.updateBox(this.safeBox(b));
31418         }
31419         if(east && east.isVisible()){
31420             var b = east.getBox();
31421             var m = east.getMargins();
31422             b.height = centerH - (m.top+m.bottom);
31423             var totalWidth = (b.width + m.left + m.right);
31424             b.x = w - totalWidth + m.left;
31425             b.y = centerY + m.top;
31426             centerW -= totalWidth;
31427             east.updateBox(this.safeBox(b));
31428         }
31429         if(center){
31430             var m = center.getMargins();
31431             var centerBox = {
31432                 x: centerX + m.left,
31433                 y: centerY + m.top,
31434                 width: centerW - (m.left+m.right),
31435                 height: centerH - (m.top+m.bottom)
31436             };
31437             //if(this.hideOnLayout){
31438                 //center.el.setStyle("display", "block");
31439             //}
31440             center.updateBox(this.safeBox(centerBox));
31441         }
31442         this.el.repaint();
31443         this.fireEvent("layout", this);
31444     },
31445
31446     // private
31447     safeBox : function(box){
31448         box.width = Math.max(0, box.width);
31449         box.height = Math.max(0, box.height);
31450         return box;
31451     },
31452
31453     /**
31454      * Adds a ContentPanel (or subclass) to this layout.
31455      * @param {String} target The target region key (north, south, east, west or center).
31456      * @param {Roo.ContentPanel} panel The panel to add
31457      * @return {Roo.ContentPanel} The added panel
31458      */
31459     add : function(target, panel){
31460          
31461         target = target.toLowerCase();
31462         return this.regions[target].add(panel);
31463     },
31464
31465     /**
31466      * Remove a ContentPanel (or subclass) to this layout.
31467      * @param {String} target The target region key (north, south, east, west or center).
31468      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31469      * @return {Roo.ContentPanel} The removed panel
31470      */
31471     remove : function(target, panel){
31472         target = target.toLowerCase();
31473         return this.regions[target].remove(panel);
31474     },
31475
31476     /**
31477      * Searches all regions for a panel with the specified id
31478      * @param {String} panelId
31479      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31480      */
31481     findPanel : function(panelId){
31482         var rs = this.regions;
31483         for(var target in rs){
31484             if(typeof rs[target] != "function"){
31485                 var p = rs[target].getPanel(panelId);
31486                 if(p){
31487                     return p;
31488                 }
31489             }
31490         }
31491         return null;
31492     },
31493
31494     /**
31495      * Searches all regions for a panel with the specified id and activates (shows) it.
31496      * @param {String/ContentPanel} panelId The panels id or the panel itself
31497      * @return {Roo.ContentPanel} The shown panel or null
31498      */
31499     showPanel : function(panelId) {
31500       var rs = this.regions;
31501       for(var target in rs){
31502          var r = rs[target];
31503          if(typeof r != "function"){
31504             if(r.hasPanel(panelId)){
31505                return r.showPanel(panelId);
31506             }
31507          }
31508       }
31509       return null;
31510    },
31511
31512    /**
31513      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31514      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31515      */
31516     restoreState : function(provider){
31517         if(!provider){
31518             provider = Roo.state.Manager;
31519         }
31520         var sm = new Roo.LayoutStateManager();
31521         sm.init(this, provider);
31522     },
31523
31524     /**
31525      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31526      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31527      * a valid ContentPanel config object.  Example:
31528      * <pre><code>
31529 // Create the main layout
31530 var layout = new Roo.BorderLayout('main-ct', {
31531     west: {
31532         split:true,
31533         minSize: 175,
31534         titlebar: true
31535     },
31536     center: {
31537         title:'Components'
31538     }
31539 }, 'main-ct');
31540
31541 // Create and add multiple ContentPanels at once via configs
31542 layout.batchAdd({
31543    west: {
31544        id: 'source-files',
31545        autoCreate:true,
31546        title:'Ext Source Files',
31547        autoScroll:true,
31548        fitToFrame:true
31549    },
31550    center : {
31551        el: cview,
31552        autoScroll:true,
31553        fitToFrame:true,
31554        toolbar: tb,
31555        resizeEl:'cbody'
31556    }
31557 });
31558 </code></pre>
31559      * @param {Object} regions An object containing ContentPanel configs by region name
31560      */
31561     batchAdd : function(regions){
31562         this.beginUpdate();
31563         for(var rname in regions){
31564             var lr = this.regions[rname];
31565             if(lr){
31566                 this.addTypedPanels(lr, regions[rname]);
31567             }
31568         }
31569         this.endUpdate();
31570     },
31571
31572     // private
31573     addTypedPanels : function(lr, ps){
31574         if(typeof ps == 'string'){
31575             lr.add(new Roo.ContentPanel(ps));
31576         }
31577         else if(ps instanceof Array){
31578             for(var i =0, len = ps.length; i < len; i++){
31579                 this.addTypedPanels(lr, ps[i]);
31580             }
31581         }
31582         else if(!ps.events){ // raw config?
31583             var el = ps.el;
31584             delete ps.el; // prevent conflict
31585             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31586         }
31587         else {  // panel object assumed!
31588             lr.add(ps);
31589         }
31590     },
31591     /**
31592      * Adds a xtype elements to the layout.
31593      * <pre><code>
31594
31595 layout.addxtype({
31596        xtype : 'ContentPanel',
31597        region: 'west',
31598        items: [ .... ]
31599    }
31600 );
31601
31602 layout.addxtype({
31603         xtype : 'NestedLayoutPanel',
31604         region: 'west',
31605         layout: {
31606            center: { },
31607            west: { }   
31608         },
31609         items : [ ... list of content panels or nested layout panels.. ]
31610    }
31611 );
31612 </code></pre>
31613      * @param {Object} cfg Xtype definition of item to add.
31614      */
31615     addxtype : function(cfg)
31616     {
31617         // basically accepts a pannel...
31618         // can accept a layout region..!?!?
31619         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31620         
31621         if (!cfg.xtype.match(/Panel$/)) {
31622             return false;
31623         }
31624         var ret = false;
31625         
31626         if (typeof(cfg.region) == 'undefined') {
31627             Roo.log("Failed to add Panel, region was not set");
31628             Roo.log(cfg);
31629             return false;
31630         }
31631         var region = cfg.region;
31632         delete cfg.region;
31633         
31634           
31635         var xitems = [];
31636         if (cfg.items) {
31637             xitems = cfg.items;
31638             delete cfg.items;
31639         }
31640         var nb = false;
31641         
31642         switch(cfg.xtype) 
31643         {
31644             case 'ContentPanel':  // ContentPanel (el, cfg)
31645             case 'ScrollPanel':  // ContentPanel (el, cfg)
31646                 if(cfg.autoCreate) {
31647                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31648                 } else {
31649                     var el = this.el.createChild();
31650                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31651                 }
31652                 
31653                 this.add(region, ret);
31654                 break;
31655             
31656             
31657             case 'TreePanel': // our new panel!
31658                 cfg.el = this.el.createChild();
31659                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31660                 this.add(region, ret);
31661                 break;
31662             
31663             case 'NestedLayoutPanel': 
31664                 // create a new Layout (which is  a Border Layout...
31665                 var el = this.el.createChild();
31666                 var clayout = cfg.layout;
31667                 delete cfg.layout;
31668                 clayout.items   = clayout.items  || [];
31669                 // replace this exitems with the clayout ones..
31670                 xitems = clayout.items;
31671                  
31672                 
31673                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31674                     cfg.background = false;
31675                 }
31676                 var layout = new Roo.BorderLayout(el, clayout);
31677                 
31678                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31679                 //console.log('adding nested layout panel '  + cfg.toSource());
31680                 this.add(region, ret);
31681                 nb = {}; /// find first...
31682                 break;
31683                 
31684             case 'GridPanel': 
31685             
31686                 // needs grid and region
31687                 
31688                 //var el = this.getRegion(region).el.createChild();
31689                 var el = this.el.createChild();
31690                 // create the grid first...
31691                 
31692                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31693                 delete cfg.grid;
31694                 if (region == 'center' && this.active ) {
31695                     cfg.background = false;
31696                 }
31697                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31698                 
31699                 this.add(region, ret);
31700                 if (cfg.background) {
31701                     ret.on('activate', function(gp) {
31702                         if (!gp.grid.rendered) {
31703                             gp.grid.render();
31704                         }
31705                     });
31706                 } else {
31707                     grid.render();
31708                 }
31709                 break;
31710            
31711                
31712                 
31713                 
31714             default: 
31715                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31716                 return null;
31717              // GridPanel (grid, cfg)
31718             
31719         }
31720         this.beginUpdate();
31721         // add children..
31722         var region = '';
31723         var abn = {};
31724         Roo.each(xitems, function(i)  {
31725             region = nb && i.region ? i.region : false;
31726             
31727             var add = ret.addxtype(i);
31728            
31729             if (region) {
31730                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31731                 if (!i.background) {
31732                     abn[region] = nb[region] ;
31733                 }
31734             }
31735             
31736         });
31737         this.endUpdate();
31738
31739         // make the last non-background panel active..
31740         //if (nb) { Roo.log(abn); }
31741         if (nb) {
31742             
31743             for(var r in abn) {
31744                 region = this.getRegion(r);
31745                 if (region) {
31746                     // tried using nb[r], but it does not work..
31747                      
31748                     region.showPanel(abn[r]);
31749                    
31750                 }
31751             }
31752         }
31753         return ret;
31754         
31755     }
31756 });
31757
31758 /**
31759  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31760  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31761  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31762  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31763  * <pre><code>
31764 // shorthand
31765 var CP = Roo.ContentPanel;
31766
31767 var layout = Roo.BorderLayout.create({
31768     north: {
31769         initialSize: 25,
31770         titlebar: false,
31771         panels: [new CP("north", "North")]
31772     },
31773     west: {
31774         split:true,
31775         initialSize: 200,
31776         minSize: 175,
31777         maxSize: 400,
31778         titlebar: true,
31779         collapsible: true,
31780         panels: [new CP("west", {title: "West"})]
31781     },
31782     east: {
31783         split:true,
31784         initialSize: 202,
31785         minSize: 175,
31786         maxSize: 400,
31787         titlebar: true,
31788         collapsible: true,
31789         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31790     },
31791     south: {
31792         split:true,
31793         initialSize: 100,
31794         minSize: 100,
31795         maxSize: 200,
31796         titlebar: true,
31797         collapsible: true,
31798         panels: [new CP("south", {title: "South", closable: true})]
31799     },
31800     center: {
31801         titlebar: true,
31802         autoScroll:true,
31803         resizeTabs: true,
31804         minTabWidth: 50,
31805         preferredTabWidth: 150,
31806         panels: [
31807             new CP("center1", {title: "Close Me", closable: true}),
31808             new CP("center2", {title: "Center Panel", closable: false})
31809         ]
31810     }
31811 }, document.body);
31812
31813 layout.getRegion("center").showPanel("center1");
31814 </code></pre>
31815  * @param config
31816  * @param targetEl
31817  */
31818 Roo.BorderLayout.create = function(config, targetEl){
31819     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31820     layout.beginUpdate();
31821     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31822     for(var j = 0, jlen = regions.length; j < jlen; j++){
31823         var lr = regions[j];
31824         if(layout.regions[lr] && config[lr].panels){
31825             var r = layout.regions[lr];
31826             var ps = config[lr].panels;
31827             layout.addTypedPanels(r, ps);
31828         }
31829     }
31830     layout.endUpdate();
31831     return layout;
31832 };
31833
31834 // private
31835 Roo.BorderLayout.RegionFactory = {
31836     // private
31837     validRegions : ["north","south","east","west","center"],
31838
31839     // private
31840     create : function(target, mgr, config){
31841         target = target.toLowerCase();
31842         if(config.lightweight || config.basic){
31843             return new Roo.BasicLayoutRegion(mgr, config, target);
31844         }
31845         switch(target){
31846             case "north":
31847                 return new Roo.NorthLayoutRegion(mgr, config);
31848             case "south":
31849                 return new Roo.SouthLayoutRegion(mgr, config);
31850             case "east":
31851                 return new Roo.EastLayoutRegion(mgr, config);
31852             case "west":
31853                 return new Roo.WestLayoutRegion(mgr, config);
31854             case "center":
31855                 return new Roo.CenterLayoutRegion(mgr, config);
31856         }
31857         throw 'Layout region "'+target+'" not supported.';
31858     }
31859 };/*
31860  * Based on:
31861  * Ext JS Library 1.1.1
31862  * Copyright(c) 2006-2007, Ext JS, LLC.
31863  *
31864  * Originally Released Under LGPL - original licence link has changed is not relivant.
31865  *
31866  * Fork - LGPL
31867  * <script type="text/javascript">
31868  */
31869  
31870 /**
31871  * @class Roo.BasicLayoutRegion
31872  * @extends Roo.util.Observable
31873  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31874  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31875  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31876  */
31877 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31878     this.mgr = mgr;
31879     this.position  = pos;
31880     this.events = {
31881         /**
31882          * @scope Roo.BasicLayoutRegion
31883          */
31884         
31885         /**
31886          * @event beforeremove
31887          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31888          * @param {Roo.LayoutRegion} this
31889          * @param {Roo.ContentPanel} panel The panel
31890          * @param {Object} e The cancel event object
31891          */
31892         "beforeremove" : true,
31893         /**
31894          * @event invalidated
31895          * Fires when the layout for this region is changed.
31896          * @param {Roo.LayoutRegion} this
31897          */
31898         "invalidated" : true,
31899         /**
31900          * @event visibilitychange
31901          * Fires when this region is shown or hidden 
31902          * @param {Roo.LayoutRegion} this
31903          * @param {Boolean} visibility true or false
31904          */
31905         "visibilitychange" : true,
31906         /**
31907          * @event paneladded
31908          * Fires when a panel is added. 
31909          * @param {Roo.LayoutRegion} this
31910          * @param {Roo.ContentPanel} panel The panel
31911          */
31912         "paneladded" : true,
31913         /**
31914          * @event panelremoved
31915          * Fires when a panel is removed. 
31916          * @param {Roo.LayoutRegion} this
31917          * @param {Roo.ContentPanel} panel The panel
31918          */
31919         "panelremoved" : true,
31920         /**
31921          * @event collapsed
31922          * Fires when this region is collapsed.
31923          * @param {Roo.LayoutRegion} this
31924          */
31925         "collapsed" : true,
31926         /**
31927          * @event expanded
31928          * Fires when this region is expanded.
31929          * @param {Roo.LayoutRegion} this
31930          */
31931         "expanded" : true,
31932         /**
31933          * @event slideshow
31934          * Fires when this region is slid into view.
31935          * @param {Roo.LayoutRegion} this
31936          */
31937         "slideshow" : true,
31938         /**
31939          * @event slidehide
31940          * Fires when this region slides out of view. 
31941          * @param {Roo.LayoutRegion} this
31942          */
31943         "slidehide" : true,
31944         /**
31945          * @event panelactivated
31946          * Fires when a panel is activated. 
31947          * @param {Roo.LayoutRegion} this
31948          * @param {Roo.ContentPanel} panel The activated panel
31949          */
31950         "panelactivated" : true,
31951         /**
31952          * @event resized
31953          * Fires when the user resizes this region. 
31954          * @param {Roo.LayoutRegion} this
31955          * @param {Number} newSize The new size (width for east/west, height for north/south)
31956          */
31957         "resized" : true
31958     };
31959     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31960     this.panels = new Roo.util.MixedCollection();
31961     this.panels.getKey = this.getPanelId.createDelegate(this);
31962     this.box = null;
31963     this.activePanel = null;
31964     // ensure listeners are added...
31965     
31966     if (config.listeners || config.events) {
31967         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31968             listeners : config.listeners || {},
31969             events : config.events || {}
31970         });
31971     }
31972     
31973     if(skipConfig !== true){
31974         this.applyConfig(config);
31975     }
31976 };
31977
31978 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31979     getPanelId : function(p){
31980         return p.getId();
31981     },
31982     
31983     applyConfig : function(config){
31984         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31985         this.config = config;
31986         
31987     },
31988     
31989     /**
31990      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31991      * the width, for horizontal (north, south) the height.
31992      * @param {Number} newSize The new width or height
31993      */
31994     resizeTo : function(newSize){
31995         var el = this.el ? this.el :
31996                  (this.activePanel ? this.activePanel.getEl() : null);
31997         if(el){
31998             switch(this.position){
31999                 case "east":
32000                 case "west":
32001                     el.setWidth(newSize);
32002                     this.fireEvent("resized", this, newSize);
32003                 break;
32004                 case "north":
32005                 case "south":
32006                     el.setHeight(newSize);
32007                     this.fireEvent("resized", this, newSize);
32008                 break;                
32009             }
32010         }
32011     },
32012     
32013     getBox : function(){
32014         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32015     },
32016     
32017     getMargins : function(){
32018         return this.margins;
32019     },
32020     
32021     updateBox : function(box){
32022         this.box = box;
32023         var el = this.activePanel.getEl();
32024         el.dom.style.left = box.x + "px";
32025         el.dom.style.top = box.y + "px";
32026         this.activePanel.setSize(box.width, box.height);
32027     },
32028     
32029     /**
32030      * Returns the container element for this region.
32031      * @return {Roo.Element}
32032      */
32033     getEl : function(){
32034         return this.activePanel;
32035     },
32036     
32037     /**
32038      * Returns true if this region is currently visible.
32039      * @return {Boolean}
32040      */
32041     isVisible : function(){
32042         return this.activePanel ? true : false;
32043     },
32044     
32045     setActivePanel : function(panel){
32046         panel = this.getPanel(panel);
32047         if(this.activePanel && this.activePanel != panel){
32048             this.activePanel.setActiveState(false);
32049             this.activePanel.getEl().setLeftTop(-10000,-10000);
32050         }
32051         this.activePanel = panel;
32052         panel.setActiveState(true);
32053         if(this.box){
32054             panel.setSize(this.box.width, this.box.height);
32055         }
32056         this.fireEvent("panelactivated", this, panel);
32057         this.fireEvent("invalidated");
32058     },
32059     
32060     /**
32061      * Show the specified panel.
32062      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32063      * @return {Roo.ContentPanel} The shown panel or null
32064      */
32065     showPanel : function(panel){
32066         if(panel = this.getPanel(panel)){
32067             this.setActivePanel(panel);
32068         }
32069         return panel;
32070     },
32071     
32072     /**
32073      * Get the active panel for this region.
32074      * @return {Roo.ContentPanel} The active panel or null
32075      */
32076     getActivePanel : function(){
32077         return this.activePanel;
32078     },
32079     
32080     /**
32081      * Add the passed ContentPanel(s)
32082      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32083      * @return {Roo.ContentPanel} The panel added (if only one was added)
32084      */
32085     add : function(panel){
32086         if(arguments.length > 1){
32087             for(var i = 0, len = arguments.length; i < len; i++) {
32088                 this.add(arguments[i]);
32089             }
32090             return null;
32091         }
32092         if(this.hasPanel(panel)){
32093             this.showPanel(panel);
32094             return panel;
32095         }
32096         var el = panel.getEl();
32097         if(el.dom.parentNode != this.mgr.el.dom){
32098             this.mgr.el.dom.appendChild(el.dom);
32099         }
32100         if(panel.setRegion){
32101             panel.setRegion(this);
32102         }
32103         this.panels.add(panel);
32104         el.setStyle("position", "absolute");
32105         if(!panel.background){
32106             this.setActivePanel(panel);
32107             if(this.config.initialSize && this.panels.getCount()==1){
32108                 this.resizeTo(this.config.initialSize);
32109             }
32110         }
32111         this.fireEvent("paneladded", this, panel);
32112         return panel;
32113     },
32114     
32115     /**
32116      * Returns true if the panel is in this region.
32117      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32118      * @return {Boolean}
32119      */
32120     hasPanel : function(panel){
32121         if(typeof panel == "object"){ // must be panel obj
32122             panel = panel.getId();
32123         }
32124         return this.getPanel(panel) ? true : false;
32125     },
32126     
32127     /**
32128      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32129      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32130      * @param {Boolean} preservePanel Overrides the config preservePanel option
32131      * @return {Roo.ContentPanel} The panel that was removed
32132      */
32133     remove : function(panel, preservePanel){
32134         panel = this.getPanel(panel);
32135         if(!panel){
32136             return null;
32137         }
32138         var e = {};
32139         this.fireEvent("beforeremove", this, panel, e);
32140         if(e.cancel === true){
32141             return null;
32142         }
32143         var panelId = panel.getId();
32144         this.panels.removeKey(panelId);
32145         return panel;
32146     },
32147     
32148     /**
32149      * Returns the panel specified or null if it's not in this region.
32150      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32151      * @return {Roo.ContentPanel}
32152      */
32153     getPanel : function(id){
32154         if(typeof id == "object"){ // must be panel obj
32155             return id;
32156         }
32157         return this.panels.get(id);
32158     },
32159     
32160     /**
32161      * Returns this regions position (north/south/east/west/center).
32162      * @return {String} 
32163      */
32164     getPosition: function(){
32165         return this.position;    
32166     }
32167 });/*
32168  * Based on:
32169  * Ext JS Library 1.1.1
32170  * Copyright(c) 2006-2007, Ext JS, LLC.
32171  *
32172  * Originally Released Under LGPL - original licence link has changed is not relivant.
32173  *
32174  * Fork - LGPL
32175  * <script type="text/javascript">
32176  */
32177  
32178 /**
32179  * @class Roo.LayoutRegion
32180  * @extends Roo.BasicLayoutRegion
32181  * This class represents a region in a layout manager.
32182  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
32183  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
32184  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
32185  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32186  * @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})
32187  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
32188  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
32189  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32190  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32191  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32192  * @cfg {String}    title           The title for the region (overrides panel titles)
32193  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32194  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32195  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32196  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32197  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32198  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32199  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32200  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32201  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32202  * @cfg {Boolean}   showPin         True to show a pin button
32203  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32204  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32205  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32206  * @cfg {Number}    width           For East/West panels
32207  * @cfg {Number}    height          For North/South panels
32208  * @cfg {Boolean}   split           To show the splitter
32209  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32210  */
32211 Roo.LayoutRegion = function(mgr, config, pos){
32212     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
32213     var dh = Roo.DomHelper;
32214     /** This region's container element 
32215     * @type Roo.Element */
32216     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
32217     /** This region's title element 
32218     * @type Roo.Element */
32219
32220     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
32221         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32222         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
32223     ]}, true);
32224     this.titleEl.enableDisplayMode();
32225     /** This region's title text element 
32226     * @type HTMLElement */
32227     this.titleTextEl = this.titleEl.dom.firstChild;
32228     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32229     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
32230     this.closeBtn.enableDisplayMode();
32231     this.closeBtn.on("click", this.closeClicked, this);
32232     this.closeBtn.hide();
32233
32234     this.createBody(config);
32235     this.visible = true;
32236     this.collapsed = false;
32237
32238     if(config.hideWhenEmpty){
32239         this.hide();
32240         this.on("paneladded", this.validateVisibility, this);
32241         this.on("panelremoved", this.validateVisibility, this);
32242     }
32243     this.applyConfig(config);
32244 };
32245
32246 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
32247
32248     createBody : function(){
32249         /** This region's body element 
32250         * @type Roo.Element */
32251         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
32252     },
32253
32254     applyConfig : function(c){
32255         if(c.collapsible && this.position != "center" && !this.collapsedEl){
32256             var dh = Roo.DomHelper;
32257             if(c.titlebar !== false){
32258                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
32259                 this.collapseBtn.on("click", this.collapse, this);
32260                 this.collapseBtn.enableDisplayMode();
32261
32262                 if(c.showPin === true || this.showPin){
32263                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
32264                     this.stickBtn.enableDisplayMode();
32265                     this.stickBtn.on("click", this.expand, this);
32266                     this.stickBtn.hide();
32267                 }
32268             }
32269             /** This region's collapsed element
32270             * @type Roo.Element */
32271             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32272                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32273             ]}, true);
32274             if(c.floatable !== false){
32275                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32276                this.collapsedEl.on("click", this.collapseClick, this);
32277             }
32278
32279             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32280                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32281                    id: "message", unselectable: "on", style:{"float":"left"}});
32282                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32283              }
32284             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32285             this.expandBtn.on("click", this.expand, this);
32286         }
32287         if(this.collapseBtn){
32288             this.collapseBtn.setVisible(c.collapsible == true);
32289         }
32290         this.cmargins = c.cmargins || this.cmargins ||
32291                          (this.position == "west" || this.position == "east" ?
32292                              {top: 0, left: 2, right:2, bottom: 0} :
32293                              {top: 2, left: 0, right:0, bottom: 2});
32294         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32295         this.bottomTabs = c.tabPosition != "top";
32296         this.autoScroll = c.autoScroll || false;
32297         if(this.autoScroll){
32298             this.bodyEl.setStyle("overflow", "auto");
32299         }else{
32300             this.bodyEl.setStyle("overflow", "hidden");
32301         }
32302         //if(c.titlebar !== false){
32303             if((!c.titlebar && !c.title) || c.titlebar === false){
32304                 this.titleEl.hide();
32305             }else{
32306                 this.titleEl.show();
32307                 if(c.title){
32308                     this.titleTextEl.innerHTML = c.title;
32309                 }
32310             }
32311         //}
32312         this.duration = c.duration || .30;
32313         this.slideDuration = c.slideDuration || .45;
32314         this.config = c;
32315         if(c.collapsed){
32316             this.collapse(true);
32317         }
32318         if(c.hidden){
32319             this.hide();
32320         }
32321     },
32322     /**
32323      * Returns true if this region is currently visible.
32324      * @return {Boolean}
32325      */
32326     isVisible : function(){
32327         return this.visible;
32328     },
32329
32330     /**
32331      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32332      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32333      */
32334     setCollapsedTitle : function(title){
32335         title = title || "&#160;";
32336         if(this.collapsedTitleTextEl){
32337             this.collapsedTitleTextEl.innerHTML = title;
32338         }
32339     },
32340
32341     getBox : function(){
32342         var b;
32343         if(!this.collapsed){
32344             b = this.el.getBox(false, true);
32345         }else{
32346             b = this.collapsedEl.getBox(false, true);
32347         }
32348         return b;
32349     },
32350
32351     getMargins : function(){
32352         return this.collapsed ? this.cmargins : this.margins;
32353     },
32354
32355     highlight : function(){
32356         this.el.addClass("x-layout-panel-dragover");
32357     },
32358
32359     unhighlight : function(){
32360         this.el.removeClass("x-layout-panel-dragover");
32361     },
32362
32363     updateBox : function(box){
32364         this.box = box;
32365         if(!this.collapsed){
32366             this.el.dom.style.left = box.x + "px";
32367             this.el.dom.style.top = box.y + "px";
32368             this.updateBody(box.width, box.height);
32369         }else{
32370             this.collapsedEl.dom.style.left = box.x + "px";
32371             this.collapsedEl.dom.style.top = box.y + "px";
32372             this.collapsedEl.setSize(box.width, box.height);
32373         }
32374         if(this.tabs){
32375             this.tabs.autoSizeTabs();
32376         }
32377     },
32378
32379     updateBody : function(w, h){
32380         if(w !== null){
32381             this.el.setWidth(w);
32382             w -= this.el.getBorderWidth("rl");
32383             if(this.config.adjustments){
32384                 w += this.config.adjustments[0];
32385             }
32386         }
32387         if(h !== null){
32388             this.el.setHeight(h);
32389             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32390             h -= this.el.getBorderWidth("tb");
32391             if(this.config.adjustments){
32392                 h += this.config.adjustments[1];
32393             }
32394             this.bodyEl.setHeight(h);
32395             if(this.tabs){
32396                 h = this.tabs.syncHeight(h);
32397             }
32398         }
32399         if(this.panelSize){
32400             w = w !== null ? w : this.panelSize.width;
32401             h = h !== null ? h : this.panelSize.height;
32402         }
32403         if(this.activePanel){
32404             var el = this.activePanel.getEl();
32405             w = w !== null ? w : el.getWidth();
32406             h = h !== null ? h : el.getHeight();
32407             this.panelSize = {width: w, height: h};
32408             this.activePanel.setSize(w, h);
32409         }
32410         if(Roo.isIE && this.tabs){
32411             this.tabs.el.repaint();
32412         }
32413     },
32414
32415     /**
32416      * Returns the container element for this region.
32417      * @return {Roo.Element}
32418      */
32419     getEl : function(){
32420         return this.el;
32421     },
32422
32423     /**
32424      * Hides this region.
32425      */
32426     hide : function(){
32427         if(!this.collapsed){
32428             this.el.dom.style.left = "-2000px";
32429             this.el.hide();
32430         }else{
32431             this.collapsedEl.dom.style.left = "-2000px";
32432             this.collapsedEl.hide();
32433         }
32434         this.visible = false;
32435         this.fireEvent("visibilitychange", this, false);
32436     },
32437
32438     /**
32439      * Shows this region if it was previously hidden.
32440      */
32441     show : function(){
32442         if(!this.collapsed){
32443             this.el.show();
32444         }else{
32445             this.collapsedEl.show();
32446         }
32447         this.visible = true;
32448         this.fireEvent("visibilitychange", this, true);
32449     },
32450
32451     closeClicked : function(){
32452         if(this.activePanel){
32453             this.remove(this.activePanel);
32454         }
32455     },
32456
32457     collapseClick : function(e){
32458         if(this.isSlid){
32459            e.stopPropagation();
32460            this.slideIn();
32461         }else{
32462            e.stopPropagation();
32463            this.slideOut();
32464         }
32465     },
32466
32467     /**
32468      * Collapses this region.
32469      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32470      */
32471     collapse : function(skipAnim){
32472         if(this.collapsed) return;
32473         this.collapsed = true;
32474         if(this.split){
32475             this.split.el.hide();
32476         }
32477         if(this.config.animate && skipAnim !== true){
32478             this.fireEvent("invalidated", this);
32479             this.animateCollapse();
32480         }else{
32481             this.el.setLocation(-20000,-20000);
32482             this.el.hide();
32483             this.collapsedEl.show();
32484             this.fireEvent("collapsed", this);
32485             this.fireEvent("invalidated", this);
32486         }
32487     },
32488
32489     animateCollapse : function(){
32490         // overridden
32491     },
32492
32493     /**
32494      * Expands this region if it was previously collapsed.
32495      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32496      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32497      */
32498     expand : function(e, skipAnim){
32499         if(e) e.stopPropagation();
32500         if(!this.collapsed || this.el.hasActiveFx()) return;
32501         if(this.isSlid){
32502             this.afterSlideIn();
32503             skipAnim = true;
32504         }
32505         this.collapsed = false;
32506         if(this.config.animate && skipAnim !== true){
32507             this.animateExpand();
32508         }else{
32509             this.el.show();
32510             if(this.split){
32511                 this.split.el.show();
32512             }
32513             this.collapsedEl.setLocation(-2000,-2000);
32514             this.collapsedEl.hide();
32515             this.fireEvent("invalidated", this);
32516             this.fireEvent("expanded", this);
32517         }
32518     },
32519
32520     animateExpand : function(){
32521         // overridden
32522     },
32523
32524     initTabs : function()
32525     {
32526         this.bodyEl.setStyle("overflow", "hidden");
32527         var ts = new Roo.TabPanel(
32528                 this.bodyEl.dom,
32529                 {
32530                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32531                     disableTooltips: this.config.disableTabTips,
32532                     toolbar : this.config.toolbar
32533                 }
32534         );
32535         if(this.config.hideTabs){
32536             ts.stripWrap.setDisplayed(false);
32537         }
32538         this.tabs = ts;
32539         ts.resizeTabs = this.config.resizeTabs === true;
32540         ts.minTabWidth = this.config.minTabWidth || 40;
32541         ts.maxTabWidth = this.config.maxTabWidth || 250;
32542         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32543         ts.monitorResize = false;
32544         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32545         ts.bodyEl.addClass('x-layout-tabs-body');
32546         this.panels.each(this.initPanelAsTab, this);
32547     },
32548
32549     initPanelAsTab : function(panel){
32550         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32551                     this.config.closeOnTab && panel.isClosable());
32552         if(panel.tabTip !== undefined){
32553             ti.setTooltip(panel.tabTip);
32554         }
32555         ti.on("activate", function(){
32556               this.setActivePanel(panel);
32557         }, this);
32558         if(this.config.closeOnTab){
32559             ti.on("beforeclose", function(t, e){
32560                 e.cancel = true;
32561                 this.remove(panel);
32562             }, this);
32563         }
32564         return ti;
32565     },
32566
32567     updatePanelTitle : function(panel, title){
32568         if(this.activePanel == panel){
32569             this.updateTitle(title);
32570         }
32571         if(this.tabs){
32572             var ti = this.tabs.getTab(panel.getEl().id);
32573             ti.setText(title);
32574             if(panel.tabTip !== undefined){
32575                 ti.setTooltip(panel.tabTip);
32576             }
32577         }
32578     },
32579
32580     updateTitle : function(title){
32581         if(this.titleTextEl && !this.config.title){
32582             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32583         }
32584     },
32585
32586     setActivePanel : function(panel){
32587         panel = this.getPanel(panel);
32588         if(this.activePanel && this.activePanel != panel){
32589             this.activePanel.setActiveState(false);
32590         }
32591         this.activePanel = panel;
32592         panel.setActiveState(true);
32593         if(this.panelSize){
32594             panel.setSize(this.panelSize.width, this.panelSize.height);
32595         }
32596         if(this.closeBtn){
32597             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32598         }
32599         this.updateTitle(panel.getTitle());
32600         if(this.tabs){
32601             this.fireEvent("invalidated", this);
32602         }
32603         this.fireEvent("panelactivated", this, panel);
32604     },
32605
32606     /**
32607      * Shows the specified panel.
32608      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32609      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32610      */
32611     showPanel : function(panel){
32612         if(panel = this.getPanel(panel)){
32613             if(this.tabs){
32614                 var tab = this.tabs.getTab(panel.getEl().id);
32615                 if(tab.isHidden()){
32616                     this.tabs.unhideTab(tab.id);
32617                 }
32618                 tab.activate();
32619             }else{
32620                 this.setActivePanel(panel);
32621             }
32622         }
32623         return panel;
32624     },
32625
32626     /**
32627      * Get the active panel for this region.
32628      * @return {Roo.ContentPanel} The active panel or null
32629      */
32630     getActivePanel : function(){
32631         return this.activePanel;
32632     },
32633
32634     validateVisibility : function(){
32635         if(this.panels.getCount() < 1){
32636             this.updateTitle("&#160;");
32637             this.closeBtn.hide();
32638             this.hide();
32639         }else{
32640             if(!this.isVisible()){
32641                 this.show();
32642             }
32643         }
32644     },
32645
32646     /**
32647      * Adds the passed ContentPanel(s) to this region.
32648      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32649      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32650      */
32651     add : function(panel){
32652         if(arguments.length > 1){
32653             for(var i = 0, len = arguments.length; i < len; i++) {
32654                 this.add(arguments[i]);
32655             }
32656             return null;
32657         }
32658         if(this.hasPanel(panel)){
32659             this.showPanel(panel);
32660             return panel;
32661         }
32662         panel.setRegion(this);
32663         this.panels.add(panel);
32664         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32665             this.bodyEl.dom.appendChild(panel.getEl().dom);
32666             if(panel.background !== true){
32667                 this.setActivePanel(panel);
32668             }
32669             this.fireEvent("paneladded", this, panel);
32670             return panel;
32671         }
32672         if(!this.tabs){
32673             this.initTabs();
32674         }else{
32675             this.initPanelAsTab(panel);
32676         }
32677         if(panel.background !== true){
32678             this.tabs.activate(panel.getEl().id);
32679         }
32680         this.fireEvent("paneladded", this, panel);
32681         return panel;
32682     },
32683
32684     /**
32685      * Hides the tab for the specified panel.
32686      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32687      */
32688     hidePanel : function(panel){
32689         if(this.tabs && (panel = this.getPanel(panel))){
32690             this.tabs.hideTab(panel.getEl().id);
32691         }
32692     },
32693
32694     /**
32695      * Unhides the tab for a previously hidden panel.
32696      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32697      */
32698     unhidePanel : function(panel){
32699         if(this.tabs && (panel = this.getPanel(panel))){
32700             this.tabs.unhideTab(panel.getEl().id);
32701         }
32702     },
32703
32704     clearPanels : function(){
32705         while(this.panels.getCount() > 0){
32706              this.remove(this.panels.first());
32707         }
32708     },
32709
32710     /**
32711      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32712      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32713      * @param {Boolean} preservePanel Overrides the config preservePanel option
32714      * @return {Roo.ContentPanel} The panel that was removed
32715      */
32716     remove : function(panel, preservePanel){
32717         panel = this.getPanel(panel);
32718         if(!panel){
32719             return null;
32720         }
32721         var e = {};
32722         this.fireEvent("beforeremove", this, panel, e);
32723         if(e.cancel === true){
32724             return null;
32725         }
32726         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32727         var panelId = panel.getId();
32728         this.panels.removeKey(panelId);
32729         if(preservePanel){
32730             document.body.appendChild(panel.getEl().dom);
32731         }
32732         if(this.tabs){
32733             this.tabs.removeTab(panel.getEl().id);
32734         }else if (!preservePanel){
32735             this.bodyEl.dom.removeChild(panel.getEl().dom);
32736         }
32737         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32738             var p = this.panels.first();
32739             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32740             tempEl.appendChild(p.getEl().dom);
32741             this.bodyEl.update("");
32742             this.bodyEl.dom.appendChild(p.getEl().dom);
32743             tempEl = null;
32744             this.updateTitle(p.getTitle());
32745             this.tabs = null;
32746             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32747             this.setActivePanel(p);
32748         }
32749         panel.setRegion(null);
32750         if(this.activePanel == panel){
32751             this.activePanel = null;
32752         }
32753         if(this.config.autoDestroy !== false && preservePanel !== true){
32754             try{panel.destroy();}catch(e){}
32755         }
32756         this.fireEvent("panelremoved", this, panel);
32757         return panel;
32758     },
32759
32760     /**
32761      * Returns the TabPanel component used by this region
32762      * @return {Roo.TabPanel}
32763      */
32764     getTabs : function(){
32765         return this.tabs;
32766     },
32767
32768     createTool : function(parentEl, className){
32769         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32770             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32771         btn.addClassOnOver("x-layout-tools-button-over");
32772         return btn;
32773     }
32774 });/*
32775  * Based on:
32776  * Ext JS Library 1.1.1
32777  * Copyright(c) 2006-2007, Ext JS, LLC.
32778  *
32779  * Originally Released Under LGPL - original licence link has changed is not relivant.
32780  *
32781  * Fork - LGPL
32782  * <script type="text/javascript">
32783  */
32784  
32785
32786
32787 /**
32788  * @class Roo.SplitLayoutRegion
32789  * @extends Roo.LayoutRegion
32790  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32791  */
32792 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32793     this.cursor = cursor;
32794     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32795 };
32796
32797 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32798     splitTip : "Drag to resize.",
32799     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32800     useSplitTips : false,
32801
32802     applyConfig : function(config){
32803         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32804         if(config.split){
32805             if(!this.split){
32806                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32807                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32808                 /** The SplitBar for this region 
32809                 * @type Roo.SplitBar */
32810                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32811                 this.split.on("moved", this.onSplitMove, this);
32812                 this.split.useShim = config.useShim === true;
32813                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32814                 if(this.useSplitTips){
32815                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32816                 }
32817                 if(config.collapsible){
32818                     this.split.el.on("dblclick", this.collapse,  this);
32819                 }
32820             }
32821             if(typeof config.minSize != "undefined"){
32822                 this.split.minSize = config.minSize;
32823             }
32824             if(typeof config.maxSize != "undefined"){
32825                 this.split.maxSize = config.maxSize;
32826             }
32827             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32828                 this.hideSplitter();
32829             }
32830         }
32831     },
32832
32833     getHMaxSize : function(){
32834          var cmax = this.config.maxSize || 10000;
32835          var center = this.mgr.getRegion("center");
32836          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32837     },
32838
32839     getVMaxSize : function(){
32840          var cmax = this.config.maxSize || 10000;
32841          var center = this.mgr.getRegion("center");
32842          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32843     },
32844
32845     onSplitMove : function(split, newSize){
32846         this.fireEvent("resized", this, newSize);
32847     },
32848     
32849     /** 
32850      * Returns the {@link Roo.SplitBar} for this region.
32851      * @return {Roo.SplitBar}
32852      */
32853     getSplitBar : function(){
32854         return this.split;
32855     },
32856     
32857     hide : function(){
32858         this.hideSplitter();
32859         Roo.SplitLayoutRegion.superclass.hide.call(this);
32860     },
32861
32862     hideSplitter : function(){
32863         if(this.split){
32864             this.split.el.setLocation(-2000,-2000);
32865             this.split.el.hide();
32866         }
32867     },
32868
32869     show : function(){
32870         if(this.split){
32871             this.split.el.show();
32872         }
32873         Roo.SplitLayoutRegion.superclass.show.call(this);
32874     },
32875     
32876     beforeSlide: function(){
32877         if(Roo.isGecko){// firefox overflow auto bug workaround
32878             this.bodyEl.clip();
32879             if(this.tabs) this.tabs.bodyEl.clip();
32880             if(this.activePanel){
32881                 this.activePanel.getEl().clip();
32882                 
32883                 if(this.activePanel.beforeSlide){
32884                     this.activePanel.beforeSlide();
32885                 }
32886             }
32887         }
32888     },
32889     
32890     afterSlide : function(){
32891         if(Roo.isGecko){// firefox overflow auto bug workaround
32892             this.bodyEl.unclip();
32893             if(this.tabs) this.tabs.bodyEl.unclip();
32894             if(this.activePanel){
32895                 this.activePanel.getEl().unclip();
32896                 if(this.activePanel.afterSlide){
32897                     this.activePanel.afterSlide();
32898                 }
32899             }
32900         }
32901     },
32902
32903     initAutoHide : function(){
32904         if(this.autoHide !== false){
32905             if(!this.autoHideHd){
32906                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32907                 this.autoHideHd = {
32908                     "mouseout": function(e){
32909                         if(!e.within(this.el, true)){
32910                             st.delay(500);
32911                         }
32912                     },
32913                     "mouseover" : function(e){
32914                         st.cancel();
32915                     },
32916                     scope : this
32917                 };
32918             }
32919             this.el.on(this.autoHideHd);
32920         }
32921     },
32922
32923     clearAutoHide : function(){
32924         if(this.autoHide !== false){
32925             this.el.un("mouseout", this.autoHideHd.mouseout);
32926             this.el.un("mouseover", this.autoHideHd.mouseover);
32927         }
32928     },
32929
32930     clearMonitor : function(){
32931         Roo.get(document).un("click", this.slideInIf, this);
32932     },
32933
32934     // these names are backwards but not changed for compat
32935     slideOut : function(){
32936         if(this.isSlid || this.el.hasActiveFx()){
32937             return;
32938         }
32939         this.isSlid = true;
32940         if(this.collapseBtn){
32941             this.collapseBtn.hide();
32942         }
32943         this.closeBtnState = this.closeBtn.getStyle('display');
32944         this.closeBtn.hide();
32945         if(this.stickBtn){
32946             this.stickBtn.show();
32947         }
32948         this.el.show();
32949         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32950         this.beforeSlide();
32951         this.el.setStyle("z-index", 10001);
32952         this.el.slideIn(this.getSlideAnchor(), {
32953             callback: function(){
32954                 this.afterSlide();
32955                 this.initAutoHide();
32956                 Roo.get(document).on("click", this.slideInIf, this);
32957                 this.fireEvent("slideshow", this);
32958             },
32959             scope: this,
32960             block: true
32961         });
32962     },
32963
32964     afterSlideIn : function(){
32965         this.clearAutoHide();
32966         this.isSlid = false;
32967         this.clearMonitor();
32968         this.el.setStyle("z-index", "");
32969         if(this.collapseBtn){
32970             this.collapseBtn.show();
32971         }
32972         this.closeBtn.setStyle('display', this.closeBtnState);
32973         if(this.stickBtn){
32974             this.stickBtn.hide();
32975         }
32976         this.fireEvent("slidehide", this);
32977     },
32978
32979     slideIn : function(cb){
32980         if(!this.isSlid || this.el.hasActiveFx()){
32981             Roo.callback(cb);
32982             return;
32983         }
32984         this.isSlid = false;
32985         this.beforeSlide();
32986         this.el.slideOut(this.getSlideAnchor(), {
32987             callback: function(){
32988                 this.el.setLeftTop(-10000, -10000);
32989                 this.afterSlide();
32990                 this.afterSlideIn();
32991                 Roo.callback(cb);
32992             },
32993             scope: this,
32994             block: true
32995         });
32996     },
32997     
32998     slideInIf : function(e){
32999         if(!e.within(this.el)){
33000             this.slideIn();
33001         }
33002     },
33003
33004     animateCollapse : function(){
33005         this.beforeSlide();
33006         this.el.setStyle("z-index", 20000);
33007         var anchor = this.getSlideAnchor();
33008         this.el.slideOut(anchor, {
33009             callback : function(){
33010                 this.el.setStyle("z-index", "");
33011                 this.collapsedEl.slideIn(anchor, {duration:.3});
33012                 this.afterSlide();
33013                 this.el.setLocation(-10000,-10000);
33014                 this.el.hide();
33015                 this.fireEvent("collapsed", this);
33016             },
33017             scope: this,
33018             block: true
33019         });
33020     },
33021
33022     animateExpand : function(){
33023         this.beforeSlide();
33024         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33025         this.el.setStyle("z-index", 20000);
33026         this.collapsedEl.hide({
33027             duration:.1
33028         });
33029         this.el.slideIn(this.getSlideAnchor(), {
33030             callback : function(){
33031                 this.el.setStyle("z-index", "");
33032                 this.afterSlide();
33033                 if(this.split){
33034                     this.split.el.show();
33035                 }
33036                 this.fireEvent("invalidated", this);
33037                 this.fireEvent("expanded", this);
33038             },
33039             scope: this,
33040             block: true
33041         });
33042     },
33043
33044     anchors : {
33045         "west" : "left",
33046         "east" : "right",
33047         "north" : "top",
33048         "south" : "bottom"
33049     },
33050
33051     sanchors : {
33052         "west" : "l",
33053         "east" : "r",
33054         "north" : "t",
33055         "south" : "b"
33056     },
33057
33058     canchors : {
33059         "west" : "tl-tr",
33060         "east" : "tr-tl",
33061         "north" : "tl-bl",
33062         "south" : "bl-tl"
33063     },
33064
33065     getAnchor : function(){
33066         return this.anchors[this.position];
33067     },
33068
33069     getCollapseAnchor : function(){
33070         return this.canchors[this.position];
33071     },
33072
33073     getSlideAnchor : function(){
33074         return this.sanchors[this.position];
33075     },
33076
33077     getAlignAdj : function(){
33078         var cm = this.cmargins;
33079         switch(this.position){
33080             case "west":
33081                 return [0, 0];
33082             break;
33083             case "east":
33084                 return [0, 0];
33085             break;
33086             case "north":
33087                 return [0, 0];
33088             break;
33089             case "south":
33090                 return [0, 0];
33091             break;
33092         }
33093     },
33094
33095     getExpandAdj : function(){
33096         var c = this.collapsedEl, cm = this.cmargins;
33097         switch(this.position){
33098             case "west":
33099                 return [-(cm.right+c.getWidth()+cm.left), 0];
33100             break;
33101             case "east":
33102                 return [cm.right+c.getWidth()+cm.left, 0];
33103             break;
33104             case "north":
33105                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33106             break;
33107             case "south":
33108                 return [0, cm.top+cm.bottom+c.getHeight()];
33109             break;
33110         }
33111     }
33112 });/*
33113  * Based on:
33114  * Ext JS Library 1.1.1
33115  * Copyright(c) 2006-2007, Ext JS, LLC.
33116  *
33117  * Originally Released Under LGPL - original licence link has changed is not relivant.
33118  *
33119  * Fork - LGPL
33120  * <script type="text/javascript">
33121  */
33122 /*
33123  * These classes are private internal classes
33124  */
33125 Roo.CenterLayoutRegion = function(mgr, config){
33126     Roo.LayoutRegion.call(this, mgr, config, "center");
33127     this.visible = true;
33128     this.minWidth = config.minWidth || 20;
33129     this.minHeight = config.minHeight || 20;
33130 };
33131
33132 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
33133     hide : function(){
33134         // center panel can't be hidden
33135     },
33136     
33137     show : function(){
33138         // center panel can't be hidden
33139     },
33140     
33141     getMinWidth: function(){
33142         return this.minWidth;
33143     },
33144     
33145     getMinHeight: function(){
33146         return this.minHeight;
33147     }
33148 });
33149
33150
33151 Roo.NorthLayoutRegion = function(mgr, config){
33152     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
33153     if(this.split){
33154         this.split.placement = Roo.SplitBar.TOP;
33155         this.split.orientation = Roo.SplitBar.VERTICAL;
33156         this.split.el.addClass("x-layout-split-v");
33157     }
33158     var size = config.initialSize || config.height;
33159     if(typeof size != "undefined"){
33160         this.el.setHeight(size);
33161     }
33162 };
33163 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
33164     orientation: Roo.SplitBar.VERTICAL,
33165     getBox : function(){
33166         if(this.collapsed){
33167             return this.collapsedEl.getBox();
33168         }
33169         var box = this.el.getBox();
33170         if(this.split){
33171             box.height += this.split.el.getHeight();
33172         }
33173         return box;
33174     },
33175     
33176     updateBox : function(box){
33177         if(this.split && !this.collapsed){
33178             box.height -= this.split.el.getHeight();
33179             this.split.el.setLeft(box.x);
33180             this.split.el.setTop(box.y+box.height);
33181             this.split.el.setWidth(box.width);
33182         }
33183         if(this.collapsed){
33184             this.updateBody(box.width, null);
33185         }
33186         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33187     }
33188 });
33189
33190 Roo.SouthLayoutRegion = function(mgr, config){
33191     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
33192     if(this.split){
33193         this.split.placement = Roo.SplitBar.BOTTOM;
33194         this.split.orientation = Roo.SplitBar.VERTICAL;
33195         this.split.el.addClass("x-layout-split-v");
33196     }
33197     var size = config.initialSize || config.height;
33198     if(typeof size != "undefined"){
33199         this.el.setHeight(size);
33200     }
33201 };
33202 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
33203     orientation: Roo.SplitBar.VERTICAL,
33204     getBox : function(){
33205         if(this.collapsed){
33206             return this.collapsedEl.getBox();
33207         }
33208         var box = this.el.getBox();
33209         if(this.split){
33210             var sh = this.split.el.getHeight();
33211             box.height += sh;
33212             box.y -= sh;
33213         }
33214         return box;
33215     },
33216     
33217     updateBox : function(box){
33218         if(this.split && !this.collapsed){
33219             var sh = this.split.el.getHeight();
33220             box.height -= sh;
33221             box.y += sh;
33222             this.split.el.setLeft(box.x);
33223             this.split.el.setTop(box.y-sh);
33224             this.split.el.setWidth(box.width);
33225         }
33226         if(this.collapsed){
33227             this.updateBody(box.width, null);
33228         }
33229         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33230     }
33231 });
33232
33233 Roo.EastLayoutRegion = function(mgr, config){
33234     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
33235     if(this.split){
33236         this.split.placement = Roo.SplitBar.RIGHT;
33237         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33238         this.split.el.addClass("x-layout-split-h");
33239     }
33240     var size = config.initialSize || config.width;
33241     if(typeof size != "undefined"){
33242         this.el.setWidth(size);
33243     }
33244 };
33245 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
33246     orientation: Roo.SplitBar.HORIZONTAL,
33247     getBox : function(){
33248         if(this.collapsed){
33249             return this.collapsedEl.getBox();
33250         }
33251         var box = this.el.getBox();
33252         if(this.split){
33253             var sw = this.split.el.getWidth();
33254             box.width += sw;
33255             box.x -= sw;
33256         }
33257         return box;
33258     },
33259
33260     updateBox : function(box){
33261         if(this.split && !this.collapsed){
33262             var sw = this.split.el.getWidth();
33263             box.width -= sw;
33264             this.split.el.setLeft(box.x);
33265             this.split.el.setTop(box.y);
33266             this.split.el.setHeight(box.height);
33267             box.x += sw;
33268         }
33269         if(this.collapsed){
33270             this.updateBody(null, box.height);
33271         }
33272         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33273     }
33274 });
33275
33276 Roo.WestLayoutRegion = function(mgr, config){
33277     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
33278     if(this.split){
33279         this.split.placement = Roo.SplitBar.LEFT;
33280         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33281         this.split.el.addClass("x-layout-split-h");
33282     }
33283     var size = config.initialSize || config.width;
33284     if(typeof size != "undefined"){
33285         this.el.setWidth(size);
33286     }
33287 };
33288 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
33289     orientation: Roo.SplitBar.HORIZONTAL,
33290     getBox : function(){
33291         if(this.collapsed){
33292             return this.collapsedEl.getBox();
33293         }
33294         var box = this.el.getBox();
33295         if(this.split){
33296             box.width += this.split.el.getWidth();
33297         }
33298         return box;
33299     },
33300     
33301     updateBox : function(box){
33302         if(this.split && !this.collapsed){
33303             var sw = this.split.el.getWidth();
33304             box.width -= sw;
33305             this.split.el.setLeft(box.x+box.width);
33306             this.split.el.setTop(box.y);
33307             this.split.el.setHeight(box.height);
33308         }
33309         if(this.collapsed){
33310             this.updateBody(null, box.height);
33311         }
33312         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33313     }
33314 });
33315 /*
33316  * Based on:
33317  * Ext JS Library 1.1.1
33318  * Copyright(c) 2006-2007, Ext JS, LLC.
33319  *
33320  * Originally Released Under LGPL - original licence link has changed is not relivant.
33321  *
33322  * Fork - LGPL
33323  * <script type="text/javascript">
33324  */
33325  
33326  
33327 /*
33328  * Private internal class for reading and applying state
33329  */
33330 Roo.LayoutStateManager = function(layout){
33331      // default empty state
33332      this.state = {
33333         north: {},
33334         south: {},
33335         east: {},
33336         west: {}       
33337     };
33338 };
33339
33340 Roo.LayoutStateManager.prototype = {
33341     init : function(layout, provider){
33342         this.provider = provider;
33343         var state = provider.get(layout.id+"-layout-state");
33344         if(state){
33345             var wasUpdating = layout.isUpdating();
33346             if(!wasUpdating){
33347                 layout.beginUpdate();
33348             }
33349             for(var key in state){
33350                 if(typeof state[key] != "function"){
33351                     var rstate = state[key];
33352                     var r = layout.getRegion(key);
33353                     if(r && rstate){
33354                         if(rstate.size){
33355                             r.resizeTo(rstate.size);
33356                         }
33357                         if(rstate.collapsed == true){
33358                             r.collapse(true);
33359                         }else{
33360                             r.expand(null, true);
33361                         }
33362                     }
33363                 }
33364             }
33365             if(!wasUpdating){
33366                 layout.endUpdate();
33367             }
33368             this.state = state; 
33369         }
33370         this.layout = layout;
33371         layout.on("regionresized", this.onRegionResized, this);
33372         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33373         layout.on("regionexpanded", this.onRegionExpanded, this);
33374     },
33375     
33376     storeState : function(){
33377         this.provider.set(this.layout.id+"-layout-state", this.state);
33378     },
33379     
33380     onRegionResized : function(region, newSize){
33381         this.state[region.getPosition()].size = newSize;
33382         this.storeState();
33383     },
33384     
33385     onRegionCollapsed : function(region){
33386         this.state[region.getPosition()].collapsed = true;
33387         this.storeState();
33388     },
33389     
33390     onRegionExpanded : function(region){
33391         this.state[region.getPosition()].collapsed = false;
33392         this.storeState();
33393     }
33394 };/*
33395  * Based on:
33396  * Ext JS Library 1.1.1
33397  * Copyright(c) 2006-2007, Ext JS, LLC.
33398  *
33399  * Originally Released Under LGPL - original licence link has changed is not relivant.
33400  *
33401  * Fork - LGPL
33402  * <script type="text/javascript">
33403  */
33404 /**
33405  * @class Roo.ContentPanel
33406  * @extends Roo.util.Observable
33407  * A basic ContentPanel element.
33408  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33409  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33410  * @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
33411  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33412  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33413  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33414  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33415  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33416  * @cfg {String} title          The title for this panel
33417  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33418  * @cfg {String} url            Calls {@link #setUrl} with this value
33419  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33420  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33421  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33422  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33423
33424  * @constructor
33425  * Create a new ContentPanel.
33426  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33427  * @param {String/Object} config A string to set only the title or a config object
33428  * @param {String} content (optional) Set the HTML content for this panel
33429  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33430  */
33431 Roo.ContentPanel = function(el, config, content){
33432     
33433      
33434     /*
33435     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33436         config = el;
33437         el = Roo.id();
33438     }
33439     if (config && config.parentLayout) { 
33440         el = config.parentLayout.el.createChild(); 
33441     }
33442     */
33443     if(el.autoCreate){ // xtype is available if this is called from factory
33444         config = el;
33445         el = Roo.id();
33446     }
33447     this.el = Roo.get(el);
33448     if(!this.el && config && config.autoCreate){
33449         if(typeof config.autoCreate == "object"){
33450             if(!config.autoCreate.id){
33451                 config.autoCreate.id = config.id||el;
33452             }
33453             this.el = Roo.DomHelper.append(document.body,
33454                         config.autoCreate, true);
33455         }else{
33456             this.el = Roo.DomHelper.append(document.body,
33457                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33458         }
33459     }
33460     this.closable = false;
33461     this.loaded = false;
33462     this.active = false;
33463     if(typeof config == "string"){
33464         this.title = config;
33465     }else{
33466         Roo.apply(this, config);
33467     }
33468     
33469     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33470         this.wrapEl = this.el.wrap();
33471         this.toolbar.container = this.el.insertSibling(false, 'before');
33472         this.toolbar = new Roo.Toolbar(this.toolbar);
33473     }
33474     
33475     // xtype created footer. - not sure if will work as we normally have to render first..
33476     if (this.footer && !this.footer.el && this.footer.xtype) {
33477         if (!this.wrapEl) {
33478             this.wrapEl = this.el.wrap();
33479         }
33480     
33481         this.footer.container = this.wrapEl.createChild();
33482          
33483         this.footer = Roo.factory(this.footer, Roo);
33484         
33485     }
33486     
33487     if(this.resizeEl){
33488         this.resizeEl = Roo.get(this.resizeEl, true);
33489     }else{
33490         this.resizeEl = this.el;
33491     }
33492     // handle view.xtype
33493     
33494     if (this.view && typeof(this.view.xtype) != 'undefined') {
33495         this.view.el = this.el.appendChild(document.createElement("div"));
33496         this.view = Roo.factory(this.view);
33497         this.view.render && this.view.render(false, ''); // render blank..
33498     }
33499     
33500     
33501     
33502     this.addEvents({
33503         /**
33504          * @event activate
33505          * Fires when this panel is activated. 
33506          * @param {Roo.ContentPanel} this
33507          */
33508         "activate" : true,
33509         /**
33510          * @event deactivate
33511          * Fires when this panel is activated. 
33512          * @param {Roo.ContentPanel} this
33513          */
33514         "deactivate" : true,
33515
33516         /**
33517          * @event resize
33518          * Fires when this panel is resized if fitToFrame is true.
33519          * @param {Roo.ContentPanel} this
33520          * @param {Number} width The width after any component adjustments
33521          * @param {Number} height The height after any component adjustments
33522          */
33523         "resize" : true,
33524         
33525          /**
33526          * @event render
33527          * Fires when this tab is created
33528          * @param {Roo.ContentPanel} this
33529          */
33530         "render" : true
33531         
33532         
33533         
33534     });
33535     if(this.autoScroll){
33536         this.resizeEl.setStyle("overflow", "auto");
33537     } else {
33538         // fix randome scrolling
33539         this.el.on('scroll', function() {
33540             Roo.log('fix random scolling');
33541             this.scrollTo('top',0); 
33542         });
33543     }
33544     content = content || this.content;
33545     if(content){
33546         this.setContent(content);
33547     }
33548     if(config && config.url){
33549         this.setUrl(this.url, this.params, this.loadOnce);
33550     }
33551     
33552     
33553     
33554     Roo.ContentPanel.superclass.constructor.call(this);
33555     
33556     this.fireEvent('render', this);
33557 };
33558
33559 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33560     tabTip:'',
33561     setRegion : function(region){
33562         this.region = region;
33563         if(region){
33564            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33565         }else{
33566            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33567         } 
33568     },
33569     
33570     /**
33571      * Returns the toolbar for this Panel if one was configured. 
33572      * @return {Roo.Toolbar} 
33573      */
33574     getToolbar : function(){
33575         return this.toolbar;
33576     },
33577     
33578     setActiveState : function(active){
33579         this.active = active;
33580         if(!active){
33581             this.fireEvent("deactivate", this);
33582         }else{
33583             this.fireEvent("activate", this);
33584         }
33585     },
33586     /**
33587      * Updates this panel's element
33588      * @param {String} content The new content
33589      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33590     */
33591     setContent : function(content, loadScripts){
33592         this.el.update(content, loadScripts);
33593     },
33594
33595     ignoreResize : function(w, h){
33596         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33597             return true;
33598         }else{
33599             this.lastSize = {width: w, height: h};
33600             return false;
33601         }
33602     },
33603     /**
33604      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33605      * @return {Roo.UpdateManager} The UpdateManager
33606      */
33607     getUpdateManager : function(){
33608         return this.el.getUpdateManager();
33609     },
33610      /**
33611      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33612      * @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:
33613 <pre><code>
33614 panel.load({
33615     url: "your-url.php",
33616     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33617     callback: yourFunction,
33618     scope: yourObject, //(optional scope)
33619     discardUrl: false,
33620     nocache: false,
33621     text: "Loading...",
33622     timeout: 30,
33623     scripts: false
33624 });
33625 </code></pre>
33626      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33627      * 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.
33628      * @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}
33629      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33630      * @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.
33631      * @return {Roo.ContentPanel} this
33632      */
33633     load : function(){
33634         var um = this.el.getUpdateManager();
33635         um.update.apply(um, arguments);
33636         return this;
33637     },
33638
33639
33640     /**
33641      * 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.
33642      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33643      * @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)
33644      * @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)
33645      * @return {Roo.UpdateManager} The UpdateManager
33646      */
33647     setUrl : function(url, params, loadOnce){
33648         if(this.refreshDelegate){
33649             this.removeListener("activate", this.refreshDelegate);
33650         }
33651         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33652         this.on("activate", this.refreshDelegate);
33653         return this.el.getUpdateManager();
33654     },
33655     
33656     _handleRefresh : function(url, params, loadOnce){
33657         if(!loadOnce || !this.loaded){
33658             var updater = this.el.getUpdateManager();
33659             updater.update(url, params, this._setLoaded.createDelegate(this));
33660         }
33661     },
33662     
33663     _setLoaded : function(){
33664         this.loaded = true;
33665     }, 
33666     
33667     /**
33668      * Returns this panel's id
33669      * @return {String} 
33670      */
33671     getId : function(){
33672         return this.el.id;
33673     },
33674     
33675     /** 
33676      * Returns this panel's element - used by regiosn to add.
33677      * @return {Roo.Element} 
33678      */
33679     getEl : function(){
33680         return this.wrapEl || this.el;
33681     },
33682     
33683     adjustForComponents : function(width, height)
33684     {
33685         //Roo.log('adjustForComponents ');
33686         if(this.resizeEl != this.el){
33687             width -= this.el.getFrameWidth('lr');
33688             height -= this.el.getFrameWidth('tb');
33689         }
33690         if(this.toolbar){
33691             var te = this.toolbar.getEl();
33692             height -= te.getHeight();
33693             te.setWidth(width);
33694         }
33695         if(this.footer){
33696             var te = this.footer.getEl();
33697             Roo.log("footer:" + te.getHeight());
33698             
33699             height -= te.getHeight();
33700             te.setWidth(width);
33701         }
33702         
33703         
33704         if(this.adjustments){
33705             width += this.adjustments[0];
33706             height += this.adjustments[1];
33707         }
33708         return {"width": width, "height": height};
33709     },
33710     
33711     setSize : function(width, height){
33712         if(this.fitToFrame && !this.ignoreResize(width, height)){
33713             if(this.fitContainer && this.resizeEl != this.el){
33714                 this.el.setSize(width, height);
33715             }
33716             var size = this.adjustForComponents(width, height);
33717             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33718             this.fireEvent('resize', this, size.width, size.height);
33719         }
33720     },
33721     
33722     /**
33723      * Returns this panel's title
33724      * @return {String} 
33725      */
33726     getTitle : function(){
33727         return this.title;
33728     },
33729     
33730     /**
33731      * Set this panel's title
33732      * @param {String} title
33733      */
33734     setTitle : function(title){
33735         this.title = title;
33736         if(this.region){
33737             this.region.updatePanelTitle(this, title);
33738         }
33739     },
33740     
33741     /**
33742      * Returns true is this panel was configured to be closable
33743      * @return {Boolean} 
33744      */
33745     isClosable : function(){
33746         return this.closable;
33747     },
33748     
33749     beforeSlide : function(){
33750         this.el.clip();
33751         this.resizeEl.clip();
33752     },
33753     
33754     afterSlide : function(){
33755         this.el.unclip();
33756         this.resizeEl.unclip();
33757     },
33758     
33759     /**
33760      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33761      *   Will fail silently if the {@link #setUrl} method has not been called.
33762      *   This does not activate the panel, just updates its content.
33763      */
33764     refresh : function(){
33765         if(this.refreshDelegate){
33766            this.loaded = false;
33767            this.refreshDelegate();
33768         }
33769     },
33770     
33771     /**
33772      * Destroys this panel
33773      */
33774     destroy : function(){
33775         this.el.removeAllListeners();
33776         var tempEl = document.createElement("span");
33777         tempEl.appendChild(this.el.dom);
33778         tempEl.innerHTML = "";
33779         this.el.remove();
33780         this.el = null;
33781     },
33782     
33783     /**
33784      * form - if the content panel contains a form - this is a reference to it.
33785      * @type {Roo.form.Form}
33786      */
33787     form : false,
33788     /**
33789      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33790      *    This contains a reference to it.
33791      * @type {Roo.View}
33792      */
33793     view : false,
33794     
33795       /**
33796      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33797      * <pre><code>
33798
33799 layout.addxtype({
33800        xtype : 'Form',
33801        items: [ .... ]
33802    }
33803 );
33804
33805 </code></pre>
33806      * @param {Object} cfg Xtype definition of item to add.
33807      */
33808     
33809     addxtype : function(cfg) {
33810         // add form..
33811         if (cfg.xtype.match(/^Form$/)) {
33812             
33813             var el;
33814             //if (this.footer) {
33815             //    el = this.footer.container.insertSibling(false, 'before');
33816             //} else {
33817                 el = this.el.createChild();
33818             //}
33819
33820             this.form = new  Roo.form.Form(cfg);
33821             
33822             
33823             if ( this.form.allItems.length) this.form.render(el.dom);
33824             return this.form;
33825         }
33826         // should only have one of theses..
33827         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33828             // views..
33829             cfg.el = this.el.appendChild(document.createElement("div"));
33830             // factory?
33831             
33832             var ret = new Roo.factory(cfg);
33833             ret.render && ret.render(false, ''); // render blank..
33834             this.view = ret;
33835             return ret;
33836         }
33837         return false;
33838     }
33839 });
33840
33841 /**
33842  * @class Roo.GridPanel
33843  * @extends Roo.ContentPanel
33844  * @constructor
33845  * Create a new GridPanel.
33846  * @param {Roo.grid.Grid} grid The grid for this panel
33847  * @param {String/Object} config A string to set only the panel's title, or a config object
33848  */
33849 Roo.GridPanel = function(grid, config){
33850     
33851   
33852     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33853         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33854         
33855     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33856     
33857     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33858     
33859     if(this.toolbar){
33860         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33861     }
33862     // xtype created footer. - not sure if will work as we normally have to render first..
33863     if (this.footer && !this.footer.el && this.footer.xtype) {
33864         
33865         this.footer.container = this.grid.getView().getFooterPanel(true);
33866         this.footer.dataSource = this.grid.dataSource;
33867         this.footer = Roo.factory(this.footer, Roo);
33868         
33869     }
33870     
33871     grid.monitorWindowResize = false; // turn off autosizing
33872     grid.autoHeight = false;
33873     grid.autoWidth = false;
33874     this.grid = grid;
33875     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33876 };
33877
33878 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33879     getId : function(){
33880         return this.grid.id;
33881     },
33882     
33883     /**
33884      * Returns the grid for this panel
33885      * @return {Roo.grid.Grid} 
33886      */
33887     getGrid : function(){
33888         return this.grid;    
33889     },
33890     
33891     setSize : function(width, height){
33892         if(!this.ignoreResize(width, height)){
33893             var grid = this.grid;
33894             var size = this.adjustForComponents(width, height);
33895             grid.getGridEl().setSize(size.width, size.height);
33896             grid.autoSize();
33897         }
33898     },
33899     
33900     beforeSlide : function(){
33901         this.grid.getView().scroller.clip();
33902     },
33903     
33904     afterSlide : function(){
33905         this.grid.getView().scroller.unclip();
33906     },
33907     
33908     destroy : function(){
33909         this.grid.destroy();
33910         delete this.grid;
33911         Roo.GridPanel.superclass.destroy.call(this); 
33912     }
33913 });
33914
33915
33916 /**
33917  * @class Roo.NestedLayoutPanel
33918  * @extends Roo.ContentPanel
33919  * @constructor
33920  * Create a new NestedLayoutPanel.
33921  * 
33922  * 
33923  * @param {Roo.BorderLayout} layout The layout for this panel
33924  * @param {String/Object} config A string to set only the title or a config object
33925  */
33926 Roo.NestedLayoutPanel = function(layout, config)
33927 {
33928     // construct with only one argument..
33929     /* FIXME - implement nicer consturctors
33930     if (layout.layout) {
33931         config = layout;
33932         layout = config.layout;
33933         delete config.layout;
33934     }
33935     if (layout.xtype && !layout.getEl) {
33936         // then layout needs constructing..
33937         layout = Roo.factory(layout, Roo);
33938     }
33939     */
33940     
33941     
33942     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33943     
33944     layout.monitorWindowResize = false; // turn off autosizing
33945     this.layout = layout;
33946     this.layout.getEl().addClass("x-layout-nested-layout");
33947     
33948     
33949     
33950     
33951 };
33952
33953 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33954
33955     setSize : function(width, height){
33956         if(!this.ignoreResize(width, height)){
33957             var size = this.adjustForComponents(width, height);
33958             var el = this.layout.getEl();
33959             el.setSize(size.width, size.height);
33960             var touch = el.dom.offsetWidth;
33961             this.layout.layout();
33962             // ie requires a double layout on the first pass
33963             if(Roo.isIE && !this.initialized){
33964                 this.initialized = true;
33965                 this.layout.layout();
33966             }
33967         }
33968     },
33969     
33970     // activate all subpanels if not currently active..
33971     
33972     setActiveState : function(active){
33973         this.active = active;
33974         if(!active){
33975             this.fireEvent("deactivate", this);
33976             return;
33977         }
33978         
33979         this.fireEvent("activate", this);
33980         // not sure if this should happen before or after..
33981         if (!this.layout) {
33982             return; // should not happen..
33983         }
33984         var reg = false;
33985         for (var r in this.layout.regions) {
33986             reg = this.layout.getRegion(r);
33987             if (reg.getActivePanel()) {
33988                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33989                 reg.setActivePanel(reg.getActivePanel());
33990                 continue;
33991             }
33992             if (!reg.panels.length) {
33993                 continue;
33994             }
33995             reg.showPanel(reg.getPanel(0));
33996         }
33997         
33998         
33999         
34000         
34001     },
34002     
34003     /**
34004      * Returns the nested BorderLayout for this panel
34005      * @return {Roo.BorderLayout} 
34006      */
34007     getLayout : function(){
34008         return this.layout;
34009     },
34010     
34011      /**
34012      * Adds a xtype elements to the layout of the nested panel
34013      * <pre><code>
34014
34015 panel.addxtype({
34016        xtype : 'ContentPanel',
34017        region: 'west',
34018        items: [ .... ]
34019    }
34020 );
34021
34022 panel.addxtype({
34023         xtype : 'NestedLayoutPanel',
34024         region: 'west',
34025         layout: {
34026            center: { },
34027            west: { }   
34028         },
34029         items : [ ... list of content panels or nested layout panels.. ]
34030    }
34031 );
34032 </code></pre>
34033      * @param {Object} cfg Xtype definition of item to add.
34034      */
34035     addxtype : function(cfg) {
34036         return this.layout.addxtype(cfg);
34037     
34038     }
34039 });
34040
34041 Roo.ScrollPanel = function(el, config, content){
34042     config = config || {};
34043     config.fitToFrame = true;
34044     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
34045     
34046     this.el.dom.style.overflow = "hidden";
34047     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
34048     this.el.removeClass("x-layout-inactive-content");
34049     this.el.on("mousewheel", this.onWheel, this);
34050
34051     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
34052     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
34053     up.unselectable(); down.unselectable();
34054     up.on("click", this.scrollUp, this);
34055     down.on("click", this.scrollDown, this);
34056     up.addClassOnOver("x-scroller-btn-over");
34057     down.addClassOnOver("x-scroller-btn-over");
34058     up.addClassOnClick("x-scroller-btn-click");
34059     down.addClassOnClick("x-scroller-btn-click");
34060     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
34061
34062     this.resizeEl = this.el;
34063     this.el = wrap; this.up = up; this.down = down;
34064 };
34065
34066 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
34067     increment : 100,
34068     wheelIncrement : 5,
34069     scrollUp : function(){
34070         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
34071     },
34072
34073     scrollDown : function(){
34074         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
34075     },
34076
34077     afterScroll : function(){
34078         var el = this.resizeEl;
34079         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
34080         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34081         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34082     },
34083
34084     setSize : function(){
34085         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
34086         this.afterScroll();
34087     },
34088
34089     onWheel : function(e){
34090         var d = e.getWheelDelta();
34091         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
34092         this.afterScroll();
34093         e.stopEvent();
34094     },
34095
34096     setContent : function(content, loadScripts){
34097         this.resizeEl.update(content, loadScripts);
34098     }
34099
34100 });
34101
34102
34103
34104
34105
34106
34107
34108
34109
34110 /**
34111  * @class Roo.TreePanel
34112  * @extends Roo.ContentPanel
34113  * @constructor
34114  * Create a new TreePanel. - defaults to fit/scoll contents.
34115  * @param {String/Object} config A string to set only the panel's title, or a config object
34116  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
34117  */
34118 Roo.TreePanel = function(config){
34119     var el = config.el;
34120     var tree = config.tree;
34121     delete config.tree; 
34122     delete config.el; // hopefull!
34123     
34124     // wrapper for IE7 strict & safari scroll issue
34125     
34126     var treeEl = el.createChild();
34127     config.resizeEl = treeEl;
34128     
34129     
34130     
34131     Roo.TreePanel.superclass.constructor.call(this, el, config);
34132  
34133  
34134     this.tree = new Roo.tree.TreePanel(treeEl , tree);
34135     //console.log(tree);
34136     this.on('activate', function()
34137     {
34138         if (this.tree.rendered) {
34139             return;
34140         }
34141         //console.log('render tree');
34142         this.tree.render();
34143     });
34144     // this should not be needed.. - it's actually the 'el' that resizes?
34145     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
34146     
34147     //this.on('resize',  function (cp, w, h) {
34148     //        this.tree.innerCt.setWidth(w);
34149     //        this.tree.innerCt.setHeight(h);
34150     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
34151     //});
34152
34153         
34154     
34155 };
34156
34157 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
34158     fitToFrame : true,
34159     autoScroll : true
34160 });
34161
34162
34163
34164
34165
34166
34167
34168
34169
34170
34171
34172 /*
34173  * Based on:
34174  * Ext JS Library 1.1.1
34175  * Copyright(c) 2006-2007, Ext JS, LLC.
34176  *
34177  * Originally Released Under LGPL - original licence link has changed is not relivant.
34178  *
34179  * Fork - LGPL
34180  * <script type="text/javascript">
34181  */
34182  
34183
34184 /**
34185  * @class Roo.ReaderLayout
34186  * @extends Roo.BorderLayout
34187  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
34188  * center region containing two nested regions (a top one for a list view and one for item preview below),
34189  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
34190  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
34191  * expedites the setup of the overall layout and regions for this common application style.
34192  * Example:
34193  <pre><code>
34194 var reader = new Roo.ReaderLayout();
34195 var CP = Roo.ContentPanel;  // shortcut for adding
34196
34197 reader.beginUpdate();
34198 reader.add("north", new CP("north", "North"));
34199 reader.add("west", new CP("west", {title: "West"}));
34200 reader.add("east", new CP("east", {title: "East"}));
34201
34202 reader.regions.listView.add(new CP("listView", "List"));
34203 reader.regions.preview.add(new CP("preview", "Preview"));
34204 reader.endUpdate();
34205 </code></pre>
34206 * @constructor
34207 * Create a new ReaderLayout
34208 * @param {Object} config Configuration options
34209 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
34210 * document.body if omitted)
34211 */
34212 Roo.ReaderLayout = function(config, renderTo){
34213     var c = config || {size:{}};
34214     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
34215         north: c.north !== false ? Roo.apply({
34216             split:false,
34217             initialSize: 32,
34218             titlebar: false
34219         }, c.north) : false,
34220         west: c.west !== false ? Roo.apply({
34221             split:true,
34222             initialSize: 200,
34223             minSize: 175,
34224             maxSize: 400,
34225             titlebar: true,
34226             collapsible: true,
34227             animate: true,
34228             margins:{left:5,right:0,bottom:5,top:5},
34229             cmargins:{left:5,right:5,bottom:5,top:5}
34230         }, c.west) : false,
34231         east: c.east !== false ? Roo.apply({
34232             split:true,
34233             initialSize: 200,
34234             minSize: 175,
34235             maxSize: 400,
34236             titlebar: true,
34237             collapsible: true,
34238             animate: true,
34239             margins:{left:0,right:5,bottom:5,top:5},
34240             cmargins:{left:5,right:5,bottom:5,top:5}
34241         }, c.east) : false,
34242         center: Roo.apply({
34243             tabPosition: 'top',
34244             autoScroll:false,
34245             closeOnTab: true,
34246             titlebar:false,
34247             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
34248         }, c.center)
34249     });
34250
34251     this.el.addClass('x-reader');
34252
34253     this.beginUpdate();
34254
34255     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
34256         south: c.preview !== false ? Roo.apply({
34257             split:true,
34258             initialSize: 200,
34259             minSize: 100,
34260             autoScroll:true,
34261             collapsible:true,
34262             titlebar: true,
34263             cmargins:{top:5,left:0, right:0, bottom:0}
34264         }, c.preview) : false,
34265         center: Roo.apply({
34266             autoScroll:false,
34267             titlebar:false,
34268             minHeight:200
34269         }, c.listView)
34270     });
34271     this.add('center', new Roo.NestedLayoutPanel(inner,
34272             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
34273
34274     this.endUpdate();
34275
34276     this.regions.preview = inner.getRegion('south');
34277     this.regions.listView = inner.getRegion('center');
34278 };
34279
34280 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
34281  * Based on:
34282  * Ext JS Library 1.1.1
34283  * Copyright(c) 2006-2007, Ext JS, LLC.
34284  *
34285  * Originally Released Under LGPL - original licence link has changed is not relivant.
34286  *
34287  * Fork - LGPL
34288  * <script type="text/javascript">
34289  */
34290  
34291 /**
34292  * @class Roo.grid.Grid
34293  * @extends Roo.util.Observable
34294  * This class represents the primary interface of a component based grid control.
34295  * <br><br>Usage:<pre><code>
34296  var grid = new Roo.grid.Grid("my-container-id", {
34297      ds: myDataStore,
34298      cm: myColModel,
34299      selModel: mySelectionModel,
34300      autoSizeColumns: true,
34301      monitorWindowResize: false,
34302      trackMouseOver: true
34303  });
34304  // set any options
34305  grid.render();
34306  * </code></pre>
34307  * <b>Common Problems:</b><br/>
34308  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
34309  * element will correct this<br/>
34310  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
34311  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
34312  * are unpredictable.<br/>
34313  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
34314  * grid to calculate dimensions/offsets.<br/>
34315   * @constructor
34316  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
34317  * The container MUST have some type of size defined for the grid to fill. The container will be
34318  * automatically set to position relative if it isn't already.
34319  * @param {Object} config A config object that sets properties on this grid.
34320  */
34321 Roo.grid.Grid = function(container, config){
34322         // initialize the container
34323         this.container = Roo.get(container);
34324         this.container.update("");
34325         this.container.setStyle("overflow", "hidden");
34326     this.container.addClass('x-grid-container');
34327
34328     this.id = this.container.id;
34329
34330     Roo.apply(this, config);
34331     // check and correct shorthanded configs
34332     if(this.ds){
34333         this.dataSource = this.ds;
34334         delete this.ds;
34335     }
34336     if(this.cm){
34337         this.colModel = this.cm;
34338         delete this.cm;
34339     }
34340     if(this.sm){
34341         this.selModel = this.sm;
34342         delete this.sm;
34343     }
34344
34345     if (this.selModel) {
34346         this.selModel = Roo.factory(this.selModel, Roo.grid);
34347         this.sm = this.selModel;
34348         this.sm.xmodule = this.xmodule || false;
34349     }
34350     if (typeof(this.colModel.config) == 'undefined') {
34351         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34352         this.cm = this.colModel;
34353         this.cm.xmodule = this.xmodule || false;
34354     }
34355     if (this.dataSource) {
34356         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34357         this.ds = this.dataSource;
34358         this.ds.xmodule = this.xmodule || false;
34359          
34360     }
34361     
34362     
34363     
34364     if(this.width){
34365         this.container.setWidth(this.width);
34366     }
34367
34368     if(this.height){
34369         this.container.setHeight(this.height);
34370     }
34371     /** @private */
34372         this.addEvents({
34373         // raw events
34374         /**
34375          * @event click
34376          * The raw click event for the entire grid.
34377          * @param {Roo.EventObject} e
34378          */
34379         "click" : true,
34380         /**
34381          * @event dblclick
34382          * The raw dblclick event for the entire grid.
34383          * @param {Roo.EventObject} e
34384          */
34385         "dblclick" : true,
34386         /**
34387          * @event contextmenu
34388          * The raw contextmenu event for the entire grid.
34389          * @param {Roo.EventObject} e
34390          */
34391         "contextmenu" : true,
34392         /**
34393          * @event mousedown
34394          * The raw mousedown event for the entire grid.
34395          * @param {Roo.EventObject} e
34396          */
34397         "mousedown" : true,
34398         /**
34399          * @event mouseup
34400          * The raw mouseup event for the entire grid.
34401          * @param {Roo.EventObject} e
34402          */
34403         "mouseup" : true,
34404         /**
34405          * @event mouseover
34406          * The raw mouseover event for the entire grid.
34407          * @param {Roo.EventObject} e
34408          */
34409         "mouseover" : true,
34410         /**
34411          * @event mouseout
34412          * The raw mouseout event for the entire grid.
34413          * @param {Roo.EventObject} e
34414          */
34415         "mouseout" : true,
34416         /**
34417          * @event keypress
34418          * The raw keypress event for the entire grid.
34419          * @param {Roo.EventObject} e
34420          */
34421         "keypress" : true,
34422         /**
34423          * @event keydown
34424          * The raw keydown event for the entire grid.
34425          * @param {Roo.EventObject} e
34426          */
34427         "keydown" : true,
34428
34429         // custom events
34430
34431         /**
34432          * @event cellclick
34433          * Fires when a cell is clicked
34434          * @param {Grid} this
34435          * @param {Number} rowIndex
34436          * @param {Number} columnIndex
34437          * @param {Roo.EventObject} e
34438          */
34439         "cellclick" : true,
34440         /**
34441          * @event celldblclick
34442          * Fires when a cell is double clicked
34443          * @param {Grid} this
34444          * @param {Number} rowIndex
34445          * @param {Number} columnIndex
34446          * @param {Roo.EventObject} e
34447          */
34448         "celldblclick" : true,
34449         /**
34450          * @event rowclick
34451          * Fires when a row is clicked
34452          * @param {Grid} this
34453          * @param {Number} rowIndex
34454          * @param {Roo.EventObject} e
34455          */
34456         "rowclick" : true,
34457         /**
34458          * @event rowdblclick
34459          * Fires when a row is double clicked
34460          * @param {Grid} this
34461          * @param {Number} rowIndex
34462          * @param {Roo.EventObject} e
34463          */
34464         "rowdblclick" : true,
34465         /**
34466          * @event headerclick
34467          * Fires when a header is clicked
34468          * @param {Grid} this
34469          * @param {Number} columnIndex
34470          * @param {Roo.EventObject} e
34471          */
34472         "headerclick" : true,
34473         /**
34474          * @event headerdblclick
34475          * Fires when a header cell is double clicked
34476          * @param {Grid} this
34477          * @param {Number} columnIndex
34478          * @param {Roo.EventObject} e
34479          */
34480         "headerdblclick" : true,
34481         /**
34482          * @event rowcontextmenu
34483          * Fires when a row is right clicked
34484          * @param {Grid} this
34485          * @param {Number} rowIndex
34486          * @param {Roo.EventObject} e
34487          */
34488         "rowcontextmenu" : true,
34489         /**
34490          * @event cellcontextmenu
34491          * Fires when a cell is right clicked
34492          * @param {Grid} this
34493          * @param {Number} rowIndex
34494          * @param {Number} cellIndex
34495          * @param {Roo.EventObject} e
34496          */
34497          "cellcontextmenu" : true,
34498         /**
34499          * @event headercontextmenu
34500          * Fires when a header is right clicked
34501          * @param {Grid} this
34502          * @param {Number} columnIndex
34503          * @param {Roo.EventObject} e
34504          */
34505         "headercontextmenu" : true,
34506         /**
34507          * @event bodyscroll
34508          * Fires when the body element is scrolled
34509          * @param {Number} scrollLeft
34510          * @param {Number} scrollTop
34511          */
34512         "bodyscroll" : true,
34513         /**
34514          * @event columnresize
34515          * Fires when the user resizes a column
34516          * @param {Number} columnIndex
34517          * @param {Number} newSize
34518          */
34519         "columnresize" : true,
34520         /**
34521          * @event columnmove
34522          * Fires when the user moves a column
34523          * @param {Number} oldIndex
34524          * @param {Number} newIndex
34525          */
34526         "columnmove" : true,
34527         /**
34528          * @event startdrag
34529          * Fires when row(s) start being dragged
34530          * @param {Grid} this
34531          * @param {Roo.GridDD} dd The drag drop object
34532          * @param {event} e The raw browser event
34533          */
34534         "startdrag" : true,
34535         /**
34536          * @event enddrag
34537          * Fires when a drag operation is complete
34538          * @param {Grid} this
34539          * @param {Roo.GridDD} dd The drag drop object
34540          * @param {event} e The raw browser event
34541          */
34542         "enddrag" : true,
34543         /**
34544          * @event dragdrop
34545          * Fires when dragged row(s) are dropped on a valid DD target
34546          * @param {Grid} this
34547          * @param {Roo.GridDD} dd The drag drop object
34548          * @param {String} targetId The target drag drop object
34549          * @param {event} e The raw browser event
34550          */
34551         "dragdrop" : true,
34552         /**
34553          * @event dragover
34554          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34555          * @param {Grid} this
34556          * @param {Roo.GridDD} dd The drag drop object
34557          * @param {String} targetId The target drag drop object
34558          * @param {event} e The raw browser event
34559          */
34560         "dragover" : true,
34561         /**
34562          * @event dragenter
34563          *  Fires when the dragged row(s) first cross another DD target while being dragged
34564          * @param {Grid} this
34565          * @param {Roo.GridDD} dd The drag drop object
34566          * @param {String} targetId The target drag drop object
34567          * @param {event} e The raw browser event
34568          */
34569         "dragenter" : true,
34570         /**
34571          * @event dragout
34572          * Fires when the dragged row(s) leave another DD target while being dragged
34573          * @param {Grid} this
34574          * @param {Roo.GridDD} dd The drag drop object
34575          * @param {String} targetId The target drag drop object
34576          * @param {event} e The raw browser event
34577          */
34578         "dragout" : true,
34579         /**
34580          * @event rowclass
34581          * Fires when a row is rendered, so you can change add a style to it.
34582          * @param {GridView} gridview   The grid view
34583          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
34584          */
34585         'rowclass' : true,
34586
34587         /**
34588          * @event render
34589          * Fires when the grid is rendered
34590          * @param {Grid} grid
34591          */
34592         'render' : true
34593     });
34594
34595     Roo.grid.Grid.superclass.constructor.call(this);
34596 };
34597 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34598     
34599     /**
34600      * @cfg {String} ddGroup - drag drop group.
34601      */
34602
34603     /**
34604      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34605      */
34606     minColumnWidth : 25,
34607
34608     /**
34609      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34610      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34611      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34612      */
34613     autoSizeColumns : false,
34614
34615     /**
34616      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34617      */
34618     autoSizeHeaders : true,
34619
34620     /**
34621      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34622      */
34623     monitorWindowResize : true,
34624
34625     /**
34626      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34627      * rows measured to get a columns size. Default is 0 (all rows).
34628      */
34629     maxRowsToMeasure : 0,
34630
34631     /**
34632      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34633      */
34634     trackMouseOver : true,
34635
34636     /**
34637     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34638     */
34639     
34640     /**
34641     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34642     */
34643     enableDragDrop : false,
34644     
34645     /**
34646     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34647     */
34648     enableColumnMove : true,
34649     
34650     /**
34651     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34652     */
34653     enableColumnHide : true,
34654     
34655     /**
34656     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34657     */
34658     enableRowHeightSync : false,
34659     
34660     /**
34661     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34662     */
34663     stripeRows : true,
34664     
34665     /**
34666     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34667     */
34668     autoHeight : false,
34669
34670     /**
34671      * @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.
34672      */
34673     autoExpandColumn : false,
34674
34675     /**
34676     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34677     * Default is 50.
34678     */
34679     autoExpandMin : 50,
34680
34681     /**
34682     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34683     */
34684     autoExpandMax : 1000,
34685
34686     /**
34687     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34688     */
34689     view : null,
34690
34691     /**
34692     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34693     */
34694     loadMask : false,
34695     /**
34696     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
34697     */
34698     dropTarget: false,
34699     
34700    
34701     
34702     // private
34703     rendered : false,
34704
34705     /**
34706     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34707     * of a fixed width. Default is false.
34708     */
34709     /**
34710     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34711     */
34712     /**
34713      * Called once after all setup has been completed and the grid is ready to be rendered.
34714      * @return {Roo.grid.Grid} this
34715      */
34716     render : function()
34717     {
34718         var c = this.container;
34719         // try to detect autoHeight/width mode
34720         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34721             this.autoHeight = true;
34722         }
34723         var view = this.getView();
34724         view.init(this);
34725
34726         c.on("click", this.onClick, this);
34727         c.on("dblclick", this.onDblClick, this);
34728         c.on("contextmenu", this.onContextMenu, this);
34729         c.on("keydown", this.onKeyDown, this);
34730
34731         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34732
34733         this.getSelectionModel().init(this);
34734
34735         view.render();
34736
34737         if(this.loadMask){
34738             this.loadMask = new Roo.LoadMask(this.container,
34739                     Roo.apply({store:this.dataSource}, this.loadMask));
34740         }
34741         
34742         
34743         if (this.toolbar && this.toolbar.xtype) {
34744             this.toolbar.container = this.getView().getHeaderPanel(true);
34745             this.toolbar = new Roo.Toolbar(this.toolbar);
34746         }
34747         if (this.footer && this.footer.xtype) {
34748             this.footer.dataSource = this.getDataSource();
34749             this.footer.container = this.getView().getFooterPanel(true);
34750             this.footer = Roo.factory(this.footer, Roo);
34751         }
34752         if (this.dropTarget && this.dropTarget.xtype) {
34753             delete this.dropTarget.xtype;
34754             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34755         }
34756         
34757         
34758         this.rendered = true;
34759         this.fireEvent('render', this);
34760         return this;
34761     },
34762
34763         /**
34764          * Reconfigures the grid to use a different Store and Column Model.
34765          * The View will be bound to the new objects and refreshed.
34766          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34767          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34768          */
34769     reconfigure : function(dataSource, colModel){
34770         if(this.loadMask){
34771             this.loadMask.destroy();
34772             this.loadMask = new Roo.LoadMask(this.container,
34773                     Roo.apply({store:dataSource}, this.loadMask));
34774         }
34775         this.view.bind(dataSource, colModel);
34776         this.dataSource = dataSource;
34777         this.colModel = colModel;
34778         this.view.refresh(true);
34779     },
34780
34781     // private
34782     onKeyDown : function(e){
34783         this.fireEvent("keydown", e);
34784     },
34785
34786     /**
34787      * Destroy this grid.
34788      * @param {Boolean} removeEl True to remove the element
34789      */
34790     destroy : function(removeEl, keepListeners){
34791         if(this.loadMask){
34792             this.loadMask.destroy();
34793         }
34794         var c = this.container;
34795         c.removeAllListeners();
34796         this.view.destroy();
34797         this.colModel.purgeListeners();
34798         if(!keepListeners){
34799             this.purgeListeners();
34800         }
34801         c.update("");
34802         if(removeEl === true){
34803             c.remove();
34804         }
34805     },
34806
34807     // private
34808     processEvent : function(name, e){
34809         this.fireEvent(name, e);
34810         var t = e.getTarget();
34811         var v = this.view;
34812         var header = v.findHeaderIndex(t);
34813         if(header !== false){
34814             this.fireEvent("header" + name, this, header, e);
34815         }else{
34816             var row = v.findRowIndex(t);
34817             var cell = v.findCellIndex(t);
34818             if(row !== false){
34819                 this.fireEvent("row" + name, this, row, e);
34820                 if(cell !== false){
34821                     this.fireEvent("cell" + name, this, row, cell, e);
34822                 }
34823             }
34824         }
34825     },
34826
34827     // private
34828     onClick : function(e){
34829         this.processEvent("click", e);
34830     },
34831
34832     // private
34833     onContextMenu : function(e, t){
34834         this.processEvent("contextmenu", e);
34835     },
34836
34837     // private
34838     onDblClick : function(e){
34839         this.processEvent("dblclick", e);
34840     },
34841
34842     // private
34843     walkCells : function(row, col, step, fn, scope){
34844         var cm = this.colModel, clen = cm.getColumnCount();
34845         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34846         if(step < 0){
34847             if(col < 0){
34848                 row--;
34849                 first = false;
34850             }
34851             while(row >= 0){
34852                 if(!first){
34853                     col = clen-1;
34854                 }
34855                 first = false;
34856                 while(col >= 0){
34857                     if(fn.call(scope || this, row, col, cm) === true){
34858                         return [row, col];
34859                     }
34860                     col--;
34861                 }
34862                 row--;
34863             }
34864         } else {
34865             if(col >= clen){
34866                 row++;
34867                 first = false;
34868             }
34869             while(row < rlen){
34870                 if(!first){
34871                     col = 0;
34872                 }
34873                 first = false;
34874                 while(col < clen){
34875                     if(fn.call(scope || this, row, col, cm) === true){
34876                         return [row, col];
34877                     }
34878                     col++;
34879                 }
34880                 row++;
34881             }
34882         }
34883         return null;
34884     },
34885
34886     // private
34887     getSelections : function(){
34888         return this.selModel.getSelections();
34889     },
34890
34891     /**
34892      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34893      * but if manual update is required this method will initiate it.
34894      */
34895     autoSize : function(){
34896         if(this.rendered){
34897             this.view.layout();
34898             if(this.view.adjustForScroll){
34899                 this.view.adjustForScroll();
34900             }
34901         }
34902     },
34903
34904     /**
34905      * Returns the grid's underlying element.
34906      * @return {Element} The element
34907      */
34908     getGridEl : function(){
34909         return this.container;
34910     },
34911
34912     // private for compatibility, overridden by editor grid
34913     stopEditing : function(){},
34914
34915     /**
34916      * Returns the grid's SelectionModel.
34917      * @return {SelectionModel}
34918      */
34919     getSelectionModel : function(){
34920         if(!this.selModel){
34921             this.selModel = new Roo.grid.RowSelectionModel();
34922         }
34923         return this.selModel;
34924     },
34925
34926     /**
34927      * Returns the grid's DataSource.
34928      * @return {DataSource}
34929      */
34930     getDataSource : function(){
34931         return this.dataSource;
34932     },
34933
34934     /**
34935      * Returns the grid's ColumnModel.
34936      * @return {ColumnModel}
34937      */
34938     getColumnModel : function(){
34939         return this.colModel;
34940     },
34941
34942     /**
34943      * Returns the grid's GridView object.
34944      * @return {GridView}
34945      */
34946     getView : function(){
34947         if(!this.view){
34948             this.view = new Roo.grid.GridView(this.viewConfig);
34949         }
34950         return this.view;
34951     },
34952     /**
34953      * Called to get grid's drag proxy text, by default returns this.ddText.
34954      * @return {String}
34955      */
34956     getDragDropText : function(){
34957         var count = this.selModel.getCount();
34958         return String.format(this.ddText, count, count == 1 ? '' : 's');
34959     }
34960 });
34961 /**
34962  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34963  * %0 is replaced with the number of selected rows.
34964  * @type String
34965  */
34966 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34967  * Based on:
34968  * Ext JS Library 1.1.1
34969  * Copyright(c) 2006-2007, Ext JS, LLC.
34970  *
34971  * Originally Released Under LGPL - original licence link has changed is not relivant.
34972  *
34973  * Fork - LGPL
34974  * <script type="text/javascript">
34975  */
34976  
34977 Roo.grid.AbstractGridView = function(){
34978         this.grid = null;
34979         
34980         this.events = {
34981             "beforerowremoved" : true,
34982             "beforerowsinserted" : true,
34983             "beforerefresh" : true,
34984             "rowremoved" : true,
34985             "rowsinserted" : true,
34986             "rowupdated" : true,
34987             "refresh" : true
34988         };
34989     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34990 };
34991
34992 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34993     rowClass : "x-grid-row",
34994     cellClass : "x-grid-cell",
34995     tdClass : "x-grid-td",
34996     hdClass : "x-grid-hd",
34997     splitClass : "x-grid-hd-split",
34998     
34999         init: function(grid){
35000         this.grid = grid;
35001                 var cid = this.grid.getGridEl().id;
35002         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
35003         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
35004         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
35005         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
35006         },
35007         
35008         getColumnRenderers : function(){
35009         var renderers = [];
35010         var cm = this.grid.colModel;
35011         var colCount = cm.getColumnCount();
35012         for(var i = 0; i < colCount; i++){
35013             renderers[i] = cm.getRenderer(i);
35014         }
35015         return renderers;
35016     },
35017     
35018     getColumnIds : function(){
35019         var ids = [];
35020         var cm = this.grid.colModel;
35021         var colCount = cm.getColumnCount();
35022         for(var i = 0; i < colCount; i++){
35023             ids[i] = cm.getColumnId(i);
35024         }
35025         return ids;
35026     },
35027     
35028     getDataIndexes : function(){
35029         if(!this.indexMap){
35030             this.indexMap = this.buildIndexMap();
35031         }
35032         return this.indexMap.colToData;
35033     },
35034     
35035     getColumnIndexByDataIndex : function(dataIndex){
35036         if(!this.indexMap){
35037             this.indexMap = this.buildIndexMap();
35038         }
35039         return this.indexMap.dataToCol[dataIndex];
35040     },
35041     
35042     /**
35043      * Set a css style for a column dynamically. 
35044      * @param {Number} colIndex The index of the column
35045      * @param {String} name The css property name
35046      * @param {String} value The css value
35047      */
35048     setCSSStyle : function(colIndex, name, value){
35049         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
35050         Roo.util.CSS.updateRule(selector, name, value);
35051     },
35052     
35053     generateRules : function(cm){
35054         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
35055         Roo.util.CSS.removeStyleSheet(rulesId);
35056         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35057             var cid = cm.getColumnId(i);
35058             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
35059                          this.tdSelector, cid, " {\n}\n",
35060                          this.hdSelector, cid, " {\n}\n",
35061                          this.splitSelector, cid, " {\n}\n");
35062         }
35063         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35064     }
35065 });/*
35066  * Based on:
35067  * Ext JS Library 1.1.1
35068  * Copyright(c) 2006-2007, Ext JS, LLC.
35069  *
35070  * Originally Released Under LGPL - original licence link has changed is not relivant.
35071  *
35072  * Fork - LGPL
35073  * <script type="text/javascript">
35074  */
35075
35076 // private
35077 // This is a support class used internally by the Grid components
35078 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
35079     this.grid = grid;
35080     this.view = grid.getView();
35081     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35082     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
35083     if(hd2){
35084         this.setHandleElId(Roo.id(hd));
35085         this.setOuterHandleElId(Roo.id(hd2));
35086     }
35087     this.scroll = false;
35088 };
35089 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
35090     maxDragWidth: 120,
35091     getDragData : function(e){
35092         var t = Roo.lib.Event.getTarget(e);
35093         var h = this.view.findHeaderCell(t);
35094         if(h){
35095             return {ddel: h.firstChild, header:h};
35096         }
35097         return false;
35098     },
35099
35100     onInitDrag : function(e){
35101         this.view.headersDisabled = true;
35102         var clone = this.dragData.ddel.cloneNode(true);
35103         clone.id = Roo.id();
35104         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
35105         this.proxy.update(clone);
35106         return true;
35107     },
35108
35109     afterValidDrop : function(){
35110         var v = this.view;
35111         setTimeout(function(){
35112             v.headersDisabled = false;
35113         }, 50);
35114     },
35115
35116     afterInvalidDrop : function(){
35117         var v = this.view;
35118         setTimeout(function(){
35119             v.headersDisabled = false;
35120         }, 50);
35121     }
35122 });
35123 /*
35124  * Based on:
35125  * Ext JS Library 1.1.1
35126  * Copyright(c) 2006-2007, Ext JS, LLC.
35127  *
35128  * Originally Released Under LGPL - original licence link has changed is not relivant.
35129  *
35130  * Fork - LGPL
35131  * <script type="text/javascript">
35132  */
35133 // private
35134 // This is a support class used internally by the Grid components
35135 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
35136     this.grid = grid;
35137     this.view = grid.getView();
35138     // split the proxies so they don't interfere with mouse events
35139     this.proxyTop = Roo.DomHelper.append(document.body, {
35140         cls:"col-move-top", html:"&#160;"
35141     }, true);
35142     this.proxyBottom = Roo.DomHelper.append(document.body, {
35143         cls:"col-move-bottom", html:"&#160;"
35144     }, true);
35145     this.proxyTop.hide = this.proxyBottom.hide = function(){
35146         this.setLeftTop(-100,-100);
35147         this.setStyle("visibility", "hidden");
35148     };
35149     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35150     // temporarily disabled
35151     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
35152     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
35153 };
35154 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
35155     proxyOffsets : [-4, -9],
35156     fly: Roo.Element.fly,
35157
35158     getTargetFromEvent : function(e){
35159         var t = Roo.lib.Event.getTarget(e);
35160         var cindex = this.view.findCellIndex(t);
35161         if(cindex !== false){
35162             return this.view.getHeaderCell(cindex);
35163         }
35164         return null;
35165     },
35166
35167     nextVisible : function(h){
35168         var v = this.view, cm = this.grid.colModel;
35169         h = h.nextSibling;
35170         while(h){
35171             if(!cm.isHidden(v.getCellIndex(h))){
35172                 return h;
35173             }
35174             h = h.nextSibling;
35175         }
35176         return null;
35177     },
35178
35179     prevVisible : function(h){
35180         var v = this.view, cm = this.grid.colModel;
35181         h = h.prevSibling;
35182         while(h){
35183             if(!cm.isHidden(v.getCellIndex(h))){
35184                 return h;
35185             }
35186             h = h.prevSibling;
35187         }
35188         return null;
35189     },
35190
35191     positionIndicator : function(h, n, e){
35192         var x = Roo.lib.Event.getPageX(e);
35193         var r = Roo.lib.Dom.getRegion(n.firstChild);
35194         var px, pt, py = r.top + this.proxyOffsets[1];
35195         if((r.right - x) <= (r.right-r.left)/2){
35196             px = r.right+this.view.borderWidth;
35197             pt = "after";
35198         }else{
35199             px = r.left;
35200             pt = "before";
35201         }
35202         var oldIndex = this.view.getCellIndex(h);
35203         var newIndex = this.view.getCellIndex(n);
35204
35205         if(this.grid.colModel.isFixed(newIndex)){
35206             return false;
35207         }
35208
35209         var locked = this.grid.colModel.isLocked(newIndex);
35210
35211         if(pt == "after"){
35212             newIndex++;
35213         }
35214         if(oldIndex < newIndex){
35215             newIndex--;
35216         }
35217         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
35218             return false;
35219         }
35220         px +=  this.proxyOffsets[0];
35221         this.proxyTop.setLeftTop(px, py);
35222         this.proxyTop.show();
35223         if(!this.bottomOffset){
35224             this.bottomOffset = this.view.mainHd.getHeight();
35225         }
35226         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
35227         this.proxyBottom.show();
35228         return pt;
35229     },
35230
35231     onNodeEnter : function(n, dd, e, data){
35232         if(data.header != n){
35233             this.positionIndicator(data.header, n, e);
35234         }
35235     },
35236
35237     onNodeOver : function(n, dd, e, data){
35238         var result = false;
35239         if(data.header != n){
35240             result = this.positionIndicator(data.header, n, e);
35241         }
35242         if(!result){
35243             this.proxyTop.hide();
35244             this.proxyBottom.hide();
35245         }
35246         return result ? this.dropAllowed : this.dropNotAllowed;
35247     },
35248
35249     onNodeOut : function(n, dd, e, data){
35250         this.proxyTop.hide();
35251         this.proxyBottom.hide();
35252     },
35253
35254     onNodeDrop : function(n, dd, e, data){
35255         var h = data.header;
35256         if(h != n){
35257             var cm = this.grid.colModel;
35258             var x = Roo.lib.Event.getPageX(e);
35259             var r = Roo.lib.Dom.getRegion(n.firstChild);
35260             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
35261             var oldIndex = this.view.getCellIndex(h);
35262             var newIndex = this.view.getCellIndex(n);
35263             var locked = cm.isLocked(newIndex);
35264             if(pt == "after"){
35265                 newIndex++;
35266             }
35267             if(oldIndex < newIndex){
35268                 newIndex--;
35269             }
35270             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
35271                 return false;
35272             }
35273             cm.setLocked(oldIndex, locked, true);
35274             cm.moveColumn(oldIndex, newIndex);
35275             this.grid.fireEvent("columnmove", oldIndex, newIndex);
35276             return true;
35277         }
35278         return false;
35279     }
35280 });
35281 /*
35282  * Based on:
35283  * Ext JS Library 1.1.1
35284  * Copyright(c) 2006-2007, Ext JS, LLC.
35285  *
35286  * Originally Released Under LGPL - original licence link has changed is not relivant.
35287  *
35288  * Fork - LGPL
35289  * <script type="text/javascript">
35290  */
35291   
35292 /**
35293  * @class Roo.grid.GridView
35294  * @extends Roo.util.Observable
35295  *
35296  * @constructor
35297  * @param {Object} config
35298  */
35299 Roo.grid.GridView = function(config){
35300     Roo.grid.GridView.superclass.constructor.call(this);
35301     this.el = null;
35302
35303     Roo.apply(this, config);
35304 };
35305
35306 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
35307
35308     unselectable :  'unselectable="on"',
35309     unselectableCls :  'x-unselectable',
35310     
35311     
35312     rowClass : "x-grid-row",
35313
35314     cellClass : "x-grid-col",
35315
35316     tdClass : "x-grid-td",
35317
35318     hdClass : "x-grid-hd",
35319
35320     splitClass : "x-grid-split",
35321
35322     sortClasses : ["sort-asc", "sort-desc"],
35323
35324     enableMoveAnim : false,
35325
35326     hlColor: "C3DAF9",
35327
35328     dh : Roo.DomHelper,
35329
35330     fly : Roo.Element.fly,
35331
35332     css : Roo.util.CSS,
35333
35334     borderWidth: 1,
35335
35336     splitOffset: 3,
35337
35338     scrollIncrement : 22,
35339
35340     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35341
35342     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35343
35344     bind : function(ds, cm){
35345         if(this.ds){
35346             this.ds.un("load", this.onLoad, this);
35347             this.ds.un("datachanged", this.onDataChange, this);
35348             this.ds.un("add", this.onAdd, this);
35349             this.ds.un("remove", this.onRemove, this);
35350             this.ds.un("update", this.onUpdate, this);
35351             this.ds.un("clear", this.onClear, this);
35352         }
35353         if(ds){
35354             ds.on("load", this.onLoad, this);
35355             ds.on("datachanged", this.onDataChange, this);
35356             ds.on("add", this.onAdd, this);
35357             ds.on("remove", this.onRemove, this);
35358             ds.on("update", this.onUpdate, this);
35359             ds.on("clear", this.onClear, this);
35360         }
35361         this.ds = ds;
35362
35363         if(this.cm){
35364             this.cm.un("widthchange", this.onColWidthChange, this);
35365             this.cm.un("headerchange", this.onHeaderChange, this);
35366             this.cm.un("hiddenchange", this.onHiddenChange, this);
35367             this.cm.un("columnmoved", this.onColumnMove, this);
35368             this.cm.un("columnlockchange", this.onColumnLock, this);
35369         }
35370         if(cm){
35371             this.generateRules(cm);
35372             cm.on("widthchange", this.onColWidthChange, this);
35373             cm.on("headerchange", this.onHeaderChange, this);
35374             cm.on("hiddenchange", this.onHiddenChange, this);
35375             cm.on("columnmoved", this.onColumnMove, this);
35376             cm.on("columnlockchange", this.onColumnLock, this);
35377         }
35378         this.cm = cm;
35379     },
35380
35381     init: function(grid){
35382         Roo.grid.GridView.superclass.init.call(this, grid);
35383
35384         this.bind(grid.dataSource, grid.colModel);
35385
35386         grid.on("headerclick", this.handleHeaderClick, this);
35387
35388         if(grid.trackMouseOver){
35389             grid.on("mouseover", this.onRowOver, this);
35390             grid.on("mouseout", this.onRowOut, this);
35391         }
35392         grid.cancelTextSelection = function(){};
35393         this.gridId = grid.id;
35394
35395         var tpls = this.templates || {};
35396
35397         if(!tpls.master){
35398             tpls.master = new Roo.Template(
35399                '<div class="x-grid" hidefocus="true">',
35400                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35401                   '<div class="x-grid-topbar"></div>',
35402                   '<div class="x-grid-scroller"><div></div></div>',
35403                   '<div class="x-grid-locked">',
35404                       '<div class="x-grid-header">{lockedHeader}</div>',
35405                       '<div class="x-grid-body">{lockedBody}</div>',
35406                   "</div>",
35407                   '<div class="x-grid-viewport">',
35408                       '<div class="x-grid-header">{header}</div>',
35409                       '<div class="x-grid-body">{body}</div>',
35410                   "</div>",
35411                   '<div class="x-grid-bottombar"></div>',
35412                  
35413                   '<div class="x-grid-resize-proxy">&#160;</div>',
35414                "</div>"
35415             );
35416             tpls.master.disableformats = true;
35417         }
35418
35419         if(!tpls.header){
35420             tpls.header = new Roo.Template(
35421                '<table border="0" cellspacing="0" cellpadding="0">',
35422                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35423                "</table>{splits}"
35424             );
35425             tpls.header.disableformats = true;
35426         }
35427         tpls.header.compile();
35428
35429         if(!tpls.hcell){
35430             tpls.hcell = new Roo.Template(
35431                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35432                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35433                 "</div></td>"
35434              );
35435              tpls.hcell.disableFormats = true;
35436         }
35437         tpls.hcell.compile();
35438
35439         if(!tpls.hsplit){
35440             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
35441                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
35442             tpls.hsplit.disableFormats = true;
35443         }
35444         tpls.hsplit.compile();
35445
35446         if(!tpls.body){
35447             tpls.body = new Roo.Template(
35448                '<table border="0" cellspacing="0" cellpadding="0">',
35449                "<tbody>{rows}</tbody>",
35450                "</table>"
35451             );
35452             tpls.body.disableFormats = true;
35453         }
35454         tpls.body.compile();
35455
35456         if(!tpls.row){
35457             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35458             tpls.row.disableFormats = true;
35459         }
35460         tpls.row.compile();
35461
35462         if(!tpls.cell){
35463             tpls.cell = new Roo.Template(
35464                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35465                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
35466                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
35467                 "</td>"
35468             );
35469             tpls.cell.disableFormats = true;
35470         }
35471         tpls.cell.compile();
35472
35473         this.templates = tpls;
35474     },
35475
35476     // remap these for backwards compat
35477     onColWidthChange : function(){
35478         this.updateColumns.apply(this, arguments);
35479     },
35480     onHeaderChange : function(){
35481         this.updateHeaders.apply(this, arguments);
35482     }, 
35483     onHiddenChange : function(){
35484         this.handleHiddenChange.apply(this, arguments);
35485     },
35486     onColumnMove : function(){
35487         this.handleColumnMove.apply(this, arguments);
35488     },
35489     onColumnLock : function(){
35490         this.handleLockChange.apply(this, arguments);
35491     },
35492
35493     onDataChange : function(){
35494         this.refresh();
35495         this.updateHeaderSortState();
35496     },
35497
35498     onClear : function(){
35499         this.refresh();
35500     },
35501
35502     onUpdate : function(ds, record){
35503         this.refreshRow(record);
35504     },
35505
35506     refreshRow : function(record){
35507         var ds = this.ds, index;
35508         if(typeof record == 'number'){
35509             index = record;
35510             record = ds.getAt(index);
35511         }else{
35512             index = ds.indexOf(record);
35513         }
35514         this.insertRows(ds, index, index, true);
35515         this.onRemove(ds, record, index+1, true);
35516         this.syncRowHeights(index, index);
35517         this.layout();
35518         this.fireEvent("rowupdated", this, index, record);
35519     },
35520
35521     onAdd : function(ds, records, index){
35522         this.insertRows(ds, index, index + (records.length-1));
35523     },
35524
35525     onRemove : function(ds, record, index, isUpdate){
35526         if(isUpdate !== true){
35527             this.fireEvent("beforerowremoved", this, index, record);
35528         }
35529         var bt = this.getBodyTable(), lt = this.getLockedTable();
35530         if(bt.rows[index]){
35531             bt.firstChild.removeChild(bt.rows[index]);
35532         }
35533         if(lt.rows[index]){
35534             lt.firstChild.removeChild(lt.rows[index]);
35535         }
35536         if(isUpdate !== true){
35537             this.stripeRows(index);
35538             this.syncRowHeights(index, index);
35539             this.layout();
35540             this.fireEvent("rowremoved", this, index, record);
35541         }
35542     },
35543
35544     onLoad : function(){
35545         this.scrollToTop();
35546     },
35547
35548     /**
35549      * Scrolls the grid to the top
35550      */
35551     scrollToTop : function(){
35552         if(this.scroller){
35553             this.scroller.dom.scrollTop = 0;
35554             this.syncScroll();
35555         }
35556     },
35557
35558     /**
35559      * Gets a panel in the header of the grid that can be used for toolbars etc.
35560      * After modifying the contents of this panel a call to grid.autoSize() may be
35561      * required to register any changes in size.
35562      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35563      * @return Roo.Element
35564      */
35565     getHeaderPanel : function(doShow){
35566         if(doShow){
35567             this.headerPanel.show();
35568         }
35569         return this.headerPanel;
35570     },
35571
35572     /**
35573      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35574      * After modifying the contents of this panel a call to grid.autoSize() may be
35575      * required to register any changes in size.
35576      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35577      * @return Roo.Element
35578      */
35579     getFooterPanel : function(doShow){
35580         if(doShow){
35581             this.footerPanel.show();
35582         }
35583         return this.footerPanel;
35584     },
35585
35586     initElements : function(){
35587         var E = Roo.Element;
35588         var el = this.grid.getGridEl().dom.firstChild;
35589         var cs = el.childNodes;
35590
35591         this.el = new E(el);
35592         
35593          this.focusEl = new E(el.firstChild);
35594         this.focusEl.swallowEvent("click", true);
35595         
35596         this.headerPanel = new E(cs[1]);
35597         this.headerPanel.enableDisplayMode("block");
35598
35599         this.scroller = new E(cs[2]);
35600         this.scrollSizer = new E(this.scroller.dom.firstChild);
35601
35602         this.lockedWrap = new E(cs[3]);
35603         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35604         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35605
35606         this.mainWrap = new E(cs[4]);
35607         this.mainHd = new E(this.mainWrap.dom.firstChild);
35608         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35609
35610         this.footerPanel = new E(cs[5]);
35611         this.footerPanel.enableDisplayMode("block");
35612
35613         this.resizeProxy = new E(cs[6]);
35614
35615         this.headerSelector = String.format(
35616            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35617            this.lockedHd.id, this.mainHd.id
35618         );
35619
35620         this.splitterSelector = String.format(
35621            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35622            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35623         );
35624     },
35625     idToCssName : function(s)
35626     {
35627         return s.replace(/[^a-z0-9]+/ig, '-');
35628     },
35629
35630     getHeaderCell : function(index){
35631         return Roo.DomQuery.select(this.headerSelector)[index];
35632     },
35633
35634     getHeaderCellMeasure : function(index){
35635         return this.getHeaderCell(index).firstChild;
35636     },
35637
35638     getHeaderCellText : function(index){
35639         return this.getHeaderCell(index).firstChild.firstChild;
35640     },
35641
35642     getLockedTable : function(){
35643         return this.lockedBody.dom.firstChild;
35644     },
35645
35646     getBodyTable : function(){
35647         return this.mainBody.dom.firstChild;
35648     },
35649
35650     getLockedRow : function(index){
35651         return this.getLockedTable().rows[index];
35652     },
35653
35654     getRow : function(index){
35655         return this.getBodyTable().rows[index];
35656     },
35657
35658     getRowComposite : function(index){
35659         if(!this.rowEl){
35660             this.rowEl = new Roo.CompositeElementLite();
35661         }
35662         var els = [], lrow, mrow;
35663         if(lrow = this.getLockedRow(index)){
35664             els.push(lrow);
35665         }
35666         if(mrow = this.getRow(index)){
35667             els.push(mrow);
35668         }
35669         this.rowEl.elements = els;
35670         return this.rowEl;
35671     },
35672     /**
35673      * Gets the 'td' of the cell
35674      * 
35675      * @param {Integer} rowIndex row to select
35676      * @param {Integer} colIndex column to select
35677      * 
35678      * @return {Object} 
35679      */
35680     getCell : function(rowIndex, colIndex){
35681         var locked = this.cm.getLockedCount();
35682         var source;
35683         if(colIndex < locked){
35684             source = this.lockedBody.dom.firstChild;
35685         }else{
35686             source = this.mainBody.dom.firstChild;
35687             colIndex -= locked;
35688         }
35689         return source.rows[rowIndex].childNodes[colIndex];
35690     },
35691
35692     getCellText : function(rowIndex, colIndex){
35693         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35694     },
35695
35696     getCellBox : function(cell){
35697         var b = this.fly(cell).getBox();
35698         if(Roo.isOpera){ // opera fails to report the Y
35699             b.y = cell.offsetTop + this.mainBody.getY();
35700         }
35701         return b;
35702     },
35703
35704     getCellIndex : function(cell){
35705         var id = String(cell.className).match(this.cellRE);
35706         if(id){
35707             return parseInt(id[1], 10);
35708         }
35709         return 0;
35710     },
35711
35712     findHeaderIndex : function(n){
35713         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35714         return r ? this.getCellIndex(r) : false;
35715     },
35716
35717     findHeaderCell : function(n){
35718         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35719         return r ? r : false;
35720     },
35721
35722     findRowIndex : function(n){
35723         if(!n){
35724             return false;
35725         }
35726         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35727         return r ? r.rowIndex : false;
35728     },
35729
35730     findCellIndex : function(node){
35731         var stop = this.el.dom;
35732         while(node && node != stop){
35733             if(this.findRE.test(node.className)){
35734                 return this.getCellIndex(node);
35735             }
35736             node = node.parentNode;
35737         }
35738         return false;
35739     },
35740
35741     getColumnId : function(index){
35742         return this.cm.getColumnId(index);
35743     },
35744
35745     getSplitters : function()
35746     {
35747         if(this.splitterSelector){
35748            return Roo.DomQuery.select(this.splitterSelector);
35749         }else{
35750             return null;
35751       }
35752     },
35753
35754     getSplitter : function(index){
35755         return this.getSplitters()[index];
35756     },
35757
35758     onRowOver : function(e, t){
35759         var row;
35760         if((row = this.findRowIndex(t)) !== false){
35761             this.getRowComposite(row).addClass("x-grid-row-over");
35762         }
35763     },
35764
35765     onRowOut : function(e, t){
35766         var row;
35767         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35768             this.getRowComposite(row).removeClass("x-grid-row-over");
35769         }
35770     },
35771
35772     renderHeaders : function(){
35773         var cm = this.cm;
35774         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35775         var cb = [], lb = [], sb = [], lsb = [], p = {};
35776         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35777             p.cellId = "x-grid-hd-0-" + i;
35778             p.splitId = "x-grid-csplit-0-" + i;
35779             p.id = cm.getColumnId(i);
35780             p.title = cm.getColumnTooltip(i) || "";
35781             p.value = cm.getColumnHeader(i) || "";
35782             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35783             if(!cm.isLocked(i)){
35784                 cb[cb.length] = ct.apply(p);
35785                 sb[sb.length] = st.apply(p);
35786             }else{
35787                 lb[lb.length] = ct.apply(p);
35788                 lsb[lsb.length] = st.apply(p);
35789             }
35790         }
35791         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35792                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35793     },
35794
35795     updateHeaders : function(){
35796         var html = this.renderHeaders();
35797         this.lockedHd.update(html[0]);
35798         this.mainHd.update(html[1]);
35799     },
35800
35801     /**
35802      * Focuses the specified row.
35803      * @param {Number} row The row index
35804      */
35805     focusRow : function(row)
35806     {
35807         //Roo.log('GridView.focusRow');
35808         var x = this.scroller.dom.scrollLeft;
35809         this.focusCell(row, 0, false);
35810         this.scroller.dom.scrollLeft = x;
35811     },
35812
35813     /**
35814      * Focuses the specified cell.
35815      * @param {Number} row The row index
35816      * @param {Number} col The column index
35817      * @param {Boolean} hscroll false to disable horizontal scrolling
35818      */
35819     focusCell : function(row, col, hscroll)
35820     {
35821         //Roo.log('GridView.focusCell');
35822         var el = this.ensureVisible(row, col, hscroll);
35823         this.focusEl.alignTo(el, "tl-tl");
35824         if(Roo.isGecko){
35825             this.focusEl.focus();
35826         }else{
35827             this.focusEl.focus.defer(1, this.focusEl);
35828         }
35829     },
35830
35831     /**
35832      * Scrolls the specified cell into view
35833      * @param {Number} row The row index
35834      * @param {Number} col The column index
35835      * @param {Boolean} hscroll false to disable horizontal scrolling
35836      */
35837     ensureVisible : function(row, col, hscroll)
35838     {
35839         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35840         //return null; //disable for testing.
35841         if(typeof row != "number"){
35842             row = row.rowIndex;
35843         }
35844         if(row < 0 && row >= this.ds.getCount()){
35845             return  null;
35846         }
35847         col = (col !== undefined ? col : 0);
35848         var cm = this.grid.colModel;
35849         while(cm.isHidden(col)){
35850             col++;
35851         }
35852
35853         var el = this.getCell(row, col);
35854         if(!el){
35855             return null;
35856         }
35857         var c = this.scroller.dom;
35858
35859         var ctop = parseInt(el.offsetTop, 10);
35860         var cleft = parseInt(el.offsetLeft, 10);
35861         var cbot = ctop + el.offsetHeight;
35862         var cright = cleft + el.offsetWidth;
35863         
35864         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35865         var stop = parseInt(c.scrollTop, 10);
35866         var sleft = parseInt(c.scrollLeft, 10);
35867         var sbot = stop + ch;
35868         var sright = sleft + c.clientWidth;
35869         /*
35870         Roo.log('GridView.ensureVisible:' +
35871                 ' ctop:' + ctop +
35872                 ' c.clientHeight:' + c.clientHeight +
35873                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35874                 ' stop:' + stop +
35875                 ' cbot:' + cbot +
35876                 ' sbot:' + sbot +
35877                 ' ch:' + ch  
35878                 );
35879         */
35880         if(ctop < stop){
35881              c.scrollTop = ctop;
35882             //Roo.log("set scrolltop to ctop DISABLE?");
35883         }else if(cbot > sbot){
35884             //Roo.log("set scrolltop to cbot-ch");
35885             c.scrollTop = cbot-ch;
35886         }
35887         
35888         if(hscroll !== false){
35889             if(cleft < sleft){
35890                 c.scrollLeft = cleft;
35891             }else if(cright > sright){
35892                 c.scrollLeft = cright-c.clientWidth;
35893             }
35894         }
35895          
35896         return el;
35897     },
35898
35899     updateColumns : function(){
35900         this.grid.stopEditing();
35901         var cm = this.grid.colModel, colIds = this.getColumnIds();
35902         //var totalWidth = cm.getTotalWidth();
35903         var pos = 0;
35904         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35905             //if(cm.isHidden(i)) continue;
35906             var w = cm.getColumnWidth(i);
35907             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35908             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35909         }
35910         this.updateSplitters();
35911     },
35912
35913     generateRules : function(cm){
35914         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35915         Roo.util.CSS.removeStyleSheet(rulesId);
35916         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35917             var cid = cm.getColumnId(i);
35918             var align = '';
35919             if(cm.config[i].align){
35920                 align = 'text-align:'+cm.config[i].align+';';
35921             }
35922             var hidden = '';
35923             if(cm.isHidden(i)){
35924                 hidden = 'display:none;';
35925             }
35926             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35927             ruleBuf.push(
35928                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35929                     this.hdSelector, cid, " {\n", align, width, "}\n",
35930                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35931                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35932         }
35933         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35934     },
35935
35936     updateSplitters : function(){
35937         var cm = this.cm, s = this.getSplitters();
35938         if(s){ // splitters not created yet
35939             var pos = 0, locked = true;
35940             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35941                 if(cm.isHidden(i)) continue;
35942                 var w = cm.getColumnWidth(i); // make sure it's a number
35943                 if(!cm.isLocked(i) && locked){
35944                     pos = 0;
35945                     locked = false;
35946                 }
35947                 pos += w;
35948                 s[i].style.left = (pos-this.splitOffset) + "px";
35949             }
35950         }
35951     },
35952
35953     handleHiddenChange : function(colModel, colIndex, hidden){
35954         if(hidden){
35955             this.hideColumn(colIndex);
35956         }else{
35957             this.unhideColumn(colIndex);
35958         }
35959     },
35960
35961     hideColumn : function(colIndex){
35962         var cid = this.getColumnId(colIndex);
35963         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35964         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35965         if(Roo.isSafari){
35966             this.updateHeaders();
35967         }
35968         this.updateSplitters();
35969         this.layout();
35970     },
35971
35972     unhideColumn : function(colIndex){
35973         var cid = this.getColumnId(colIndex);
35974         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35975         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35976
35977         if(Roo.isSafari){
35978             this.updateHeaders();
35979         }
35980         this.updateSplitters();
35981         this.layout();
35982     },
35983
35984     insertRows : function(dm, firstRow, lastRow, isUpdate){
35985         if(firstRow == 0 && lastRow == dm.getCount()-1){
35986             this.refresh();
35987         }else{
35988             if(!isUpdate){
35989                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35990             }
35991             var s = this.getScrollState();
35992             var markup = this.renderRows(firstRow, lastRow);
35993             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35994             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35995             this.restoreScroll(s);
35996             if(!isUpdate){
35997                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35998                 this.syncRowHeights(firstRow, lastRow);
35999                 this.stripeRows(firstRow);
36000                 this.layout();
36001             }
36002         }
36003     },
36004
36005     bufferRows : function(markup, target, index){
36006         var before = null, trows = target.rows, tbody = target.tBodies[0];
36007         if(index < trows.length){
36008             before = trows[index];
36009         }
36010         var b = document.createElement("div");
36011         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
36012         var rows = b.firstChild.rows;
36013         for(var i = 0, len = rows.length; i < len; i++){
36014             if(before){
36015                 tbody.insertBefore(rows[0], before);
36016             }else{
36017                 tbody.appendChild(rows[0]);
36018             }
36019         }
36020         b.innerHTML = "";
36021         b = null;
36022     },
36023
36024     deleteRows : function(dm, firstRow, lastRow){
36025         if(dm.getRowCount()<1){
36026             this.fireEvent("beforerefresh", this);
36027             this.mainBody.update("");
36028             this.lockedBody.update("");
36029             this.fireEvent("refresh", this);
36030         }else{
36031             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
36032             var bt = this.getBodyTable();
36033             var tbody = bt.firstChild;
36034             var rows = bt.rows;
36035             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
36036                 tbody.removeChild(rows[firstRow]);
36037             }
36038             this.stripeRows(firstRow);
36039             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
36040         }
36041     },
36042
36043     updateRows : function(dataSource, firstRow, lastRow){
36044         var s = this.getScrollState();
36045         this.refresh();
36046         this.restoreScroll(s);
36047     },
36048
36049     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
36050         if(!noRefresh){
36051            this.refresh();
36052         }
36053         this.updateHeaderSortState();
36054     },
36055
36056     getScrollState : function(){
36057         
36058         var sb = this.scroller.dom;
36059         return {left: sb.scrollLeft, top: sb.scrollTop};
36060     },
36061
36062     stripeRows : function(startRow){
36063         if(!this.grid.stripeRows || this.ds.getCount() < 1){
36064             return;
36065         }
36066         startRow = startRow || 0;
36067         var rows = this.getBodyTable().rows;
36068         var lrows = this.getLockedTable().rows;
36069         var cls = ' x-grid-row-alt ';
36070         for(var i = startRow, len = rows.length; i < len; i++){
36071             var row = rows[i], lrow = lrows[i];
36072             var isAlt = ((i+1) % 2 == 0);
36073             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
36074             if(isAlt == hasAlt){
36075                 continue;
36076             }
36077             if(isAlt){
36078                 row.className += " x-grid-row-alt";
36079             }else{
36080                 row.className = row.className.replace("x-grid-row-alt", "");
36081             }
36082             if(lrow){
36083                 lrow.className = row.className;
36084             }
36085         }
36086     },
36087
36088     restoreScroll : function(state){
36089         //Roo.log('GridView.restoreScroll');
36090         var sb = this.scroller.dom;
36091         sb.scrollLeft = state.left;
36092         sb.scrollTop = state.top;
36093         this.syncScroll();
36094     },
36095
36096     syncScroll : function(){
36097         //Roo.log('GridView.syncScroll');
36098         var sb = this.scroller.dom;
36099         var sh = this.mainHd.dom;
36100         var bs = this.mainBody.dom;
36101         var lv = this.lockedBody.dom;
36102         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
36103         lv.scrollTop = bs.scrollTop = sb.scrollTop;
36104     },
36105
36106     handleScroll : function(e){
36107         this.syncScroll();
36108         var sb = this.scroller.dom;
36109         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
36110         e.stopEvent();
36111     },
36112
36113     handleWheel : function(e){
36114         var d = e.getWheelDelta();
36115         this.scroller.dom.scrollTop -= d*22;
36116         // set this here to prevent jumpy scrolling on large tables
36117         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
36118         e.stopEvent();
36119     },
36120
36121     renderRows : function(startRow, endRow){
36122         // pull in all the crap needed to render rows
36123         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
36124         var colCount = cm.getColumnCount();
36125
36126         if(ds.getCount() < 1){
36127             return ["", ""];
36128         }
36129
36130         // build a map for all the columns
36131         var cs = [];
36132         for(var i = 0; i < colCount; i++){
36133             var name = cm.getDataIndex(i);
36134             cs[i] = {
36135                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
36136                 renderer : cm.getRenderer(i),
36137                 id : cm.getColumnId(i),
36138                 locked : cm.isLocked(i)
36139             };
36140         }
36141
36142         startRow = startRow || 0;
36143         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
36144
36145         // records to render
36146         var rs = ds.getRange(startRow, endRow);
36147
36148         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
36149     },
36150
36151     // As much as I hate to duplicate code, this was branched because FireFox really hates
36152     // [].join("") on strings. The performance difference was substantial enough to
36153     // branch this function
36154     doRender : Roo.isGecko ?
36155             function(cs, rs, ds, startRow, colCount, stripe){
36156                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36157                 // buffers
36158                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36159                 
36160                 var hasListener = this.grid.hasListener('rowclass');
36161                 var rowcfg = {};
36162                 for(var j = 0, len = rs.length; j < len; j++){
36163                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
36164                     for(var i = 0; i < colCount; i++){
36165                         c = cs[i];
36166                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36167                         p.id = c.id;
36168                         p.css = p.attr = "";
36169                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36170                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36171                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36172                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36173                         }
36174                         var markup = ct.apply(p);
36175                         if(!c.locked){
36176                             cb+= markup;
36177                         }else{
36178                             lcb+= markup;
36179                         }
36180                     }
36181                     var alt = [];
36182                     if(stripe && ((rowIndex+1) % 2 == 0)){
36183                         alt.push("x-grid-row-alt")
36184                     }
36185                     if(r.dirty){
36186                         alt.push(  " x-grid-dirty-row");
36187                     }
36188                     rp.cells = lcb;
36189                     if(this.getRowClass){
36190                         alt.push(this.getRowClass(r, rowIndex));
36191                     }
36192                     if (hasListener) {
36193                         rowcfg = {
36194                              
36195                             record: r,
36196                             rowIndex : rowIndex,
36197                             rowClass : ''
36198                         }
36199                         this.grid.fireEvent('rowclass', this, rowcfg);
36200                         alt.push(rowcfg.rowClass);
36201                     }
36202                     rp.alt = alt.join(" ");
36203                     lbuf+= rt.apply(rp);
36204                     rp.cells = cb;
36205                     buf+=  rt.apply(rp);
36206                 }
36207                 return [lbuf, buf];
36208             } :
36209             function(cs, rs, ds, startRow, colCount, stripe){
36210                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36211                 // buffers
36212                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36213                 var hasListener = this.grid.hasListener('rowclass');
36214  
36215                 var rowcfg = {};
36216                 for(var j = 0, len = rs.length; j < len; j++){
36217                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
36218                     for(var i = 0; i < colCount; i++){
36219                         c = cs[i];
36220                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36221                         p.id = c.id;
36222                         p.css = p.attr = "";
36223                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36224                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36225                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36226                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36227                         }
36228                         
36229                         var markup = ct.apply(p);
36230                         if(!c.locked){
36231                             cb[cb.length] = markup;
36232                         }else{
36233                             lcb[lcb.length] = markup;
36234                         }
36235                     }
36236                     var alt = [];
36237                     if(stripe && ((rowIndex+1) % 2 == 0)){
36238                         alt.push( "x-grid-row-alt");
36239                     }
36240                     if(r.dirty){
36241                         alt.push(" x-grid-dirty-row");
36242                     }
36243                     rp.cells = lcb;
36244                     if(this.getRowClass){
36245                         alt.push( this.getRowClass(r, rowIndex));
36246                     }
36247                     if (hasListener) {
36248                         rowcfg = {
36249                              
36250                             record: r,
36251                             rowIndex : rowIndex,
36252                             rowClass : ''
36253                         }
36254                         this.grid.fireEvent('rowclass', this, rowcfg);
36255                         alt.push(rowcfg.rowClass);
36256                     }
36257                     rp.alt = alt.join(" ");
36258                     rp.cells = lcb.join("");
36259                     lbuf[lbuf.length] = rt.apply(rp);
36260                     rp.cells = cb.join("");
36261                     buf[buf.length] =  rt.apply(rp);
36262                 }
36263                 return [lbuf.join(""), buf.join("")];
36264             },
36265
36266     renderBody : function(){
36267         var markup = this.renderRows();
36268         var bt = this.templates.body;
36269         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
36270     },
36271
36272     /**
36273      * Refreshes the grid
36274      * @param {Boolean} headersToo
36275      */
36276     refresh : function(headersToo){
36277         this.fireEvent("beforerefresh", this);
36278         this.grid.stopEditing();
36279         var result = this.renderBody();
36280         this.lockedBody.update(result[0]);
36281         this.mainBody.update(result[1]);
36282         if(headersToo === true){
36283             this.updateHeaders();
36284             this.updateColumns();
36285             this.updateSplitters();
36286             this.updateHeaderSortState();
36287         }
36288         this.syncRowHeights();
36289         this.layout();
36290         this.fireEvent("refresh", this);
36291     },
36292
36293     handleColumnMove : function(cm, oldIndex, newIndex){
36294         this.indexMap = null;
36295         var s = this.getScrollState();
36296         this.refresh(true);
36297         this.restoreScroll(s);
36298         this.afterMove(newIndex);
36299     },
36300
36301     afterMove : function(colIndex){
36302         if(this.enableMoveAnim && Roo.enableFx){
36303             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
36304         }
36305         // if multisort - fix sortOrder, and reload..
36306         if (this.grid.dataSource.multiSort) {
36307             // the we can call sort again..
36308             var dm = this.grid.dataSource;
36309             var cm = this.grid.colModel;
36310             var so = [];
36311             for(var i = 0; i < cm.config.length; i++ ) {
36312                 
36313                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
36314                     continue; // dont' bother, it's not in sort list or being set.
36315                 }
36316                 
36317                 so.push(cm.config[i].dataIndex);
36318             };
36319             dm.sortOrder = so;
36320             dm.load(dm.lastOptions);
36321             
36322             
36323         }
36324         
36325     },
36326
36327     updateCell : function(dm, rowIndex, dataIndex){
36328         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36329         if(typeof colIndex == "undefined"){ // not present in grid
36330             return;
36331         }
36332         var cm = this.grid.colModel;
36333         var cell = this.getCell(rowIndex, colIndex);
36334         var cellText = this.getCellText(rowIndex, colIndex);
36335
36336         var p = {
36337             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36338             id : cm.getColumnId(colIndex),
36339             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36340         };
36341         var renderer = cm.getRenderer(colIndex);
36342         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36343         if(typeof val == "undefined" || val === "") val = "&#160;";
36344         cellText.innerHTML = val;
36345         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36346         this.syncRowHeights(rowIndex, rowIndex);
36347     },
36348
36349     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36350         var maxWidth = 0;
36351         if(this.grid.autoSizeHeaders){
36352             var h = this.getHeaderCellMeasure(colIndex);
36353             maxWidth = Math.max(maxWidth, h.scrollWidth);
36354         }
36355         var tb, index;
36356         if(this.cm.isLocked(colIndex)){
36357             tb = this.getLockedTable();
36358             index = colIndex;
36359         }else{
36360             tb = this.getBodyTable();
36361             index = colIndex - this.cm.getLockedCount();
36362         }
36363         if(tb && tb.rows){
36364             var rows = tb.rows;
36365             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36366             for(var i = 0; i < stopIndex; i++){
36367                 var cell = rows[i].childNodes[index].firstChild;
36368                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36369             }
36370         }
36371         return maxWidth + /*margin for error in IE*/ 5;
36372     },
36373     /**
36374      * Autofit a column to its content.
36375      * @param {Number} colIndex
36376      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36377      */
36378      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36379          if(this.cm.isHidden(colIndex)){
36380              return; // can't calc a hidden column
36381          }
36382         if(forceMinSize){
36383             var cid = this.cm.getColumnId(colIndex);
36384             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36385            if(this.grid.autoSizeHeaders){
36386                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36387            }
36388         }
36389         var newWidth = this.calcColumnWidth(colIndex);
36390         this.cm.setColumnWidth(colIndex,
36391             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36392         if(!suppressEvent){
36393             this.grid.fireEvent("columnresize", colIndex, newWidth);
36394         }
36395     },
36396
36397     /**
36398      * Autofits all columns to their content and then expands to fit any extra space in the grid
36399      */
36400      autoSizeColumns : function(){
36401         var cm = this.grid.colModel;
36402         var colCount = cm.getColumnCount();
36403         for(var i = 0; i < colCount; i++){
36404             this.autoSizeColumn(i, true, true);
36405         }
36406         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36407             this.fitColumns();
36408         }else{
36409             this.updateColumns();
36410             this.layout();
36411         }
36412     },
36413
36414     /**
36415      * Autofits all columns to the grid's width proportionate with their current size
36416      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36417      */
36418     fitColumns : function(reserveScrollSpace){
36419         var cm = this.grid.colModel;
36420         var colCount = cm.getColumnCount();
36421         var cols = [];
36422         var width = 0;
36423         var i, w;
36424         for (i = 0; i < colCount; i++){
36425             if(!cm.isHidden(i) && !cm.isFixed(i)){
36426                 w = cm.getColumnWidth(i);
36427                 cols.push(i);
36428                 cols.push(w);
36429                 width += w;
36430             }
36431         }
36432         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36433         if(reserveScrollSpace){
36434             avail -= 17;
36435         }
36436         var frac = (avail - cm.getTotalWidth())/width;
36437         while (cols.length){
36438             w = cols.pop();
36439             i = cols.pop();
36440             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36441         }
36442         this.updateColumns();
36443         this.layout();
36444     },
36445
36446     onRowSelect : function(rowIndex){
36447         var row = this.getRowComposite(rowIndex);
36448         row.addClass("x-grid-row-selected");
36449     },
36450
36451     onRowDeselect : function(rowIndex){
36452         var row = this.getRowComposite(rowIndex);
36453         row.removeClass("x-grid-row-selected");
36454     },
36455
36456     onCellSelect : function(row, col){
36457         var cell = this.getCell(row, col);
36458         if(cell){
36459             Roo.fly(cell).addClass("x-grid-cell-selected");
36460         }
36461     },
36462
36463     onCellDeselect : function(row, col){
36464         var cell = this.getCell(row, col);
36465         if(cell){
36466             Roo.fly(cell).removeClass("x-grid-cell-selected");
36467         }
36468     },
36469
36470     updateHeaderSortState : function(){
36471         
36472         // sort state can be single { field: xxx, direction : yyy}
36473         // or   { xxx=>ASC , yyy : DESC ..... }
36474         
36475         var mstate = {};
36476         if (!this.ds.multiSort) { 
36477             var state = this.ds.getSortState();
36478             if(!state){
36479                 return;
36480             }
36481             mstate[state.field] = state.direction;
36482             // FIXME... - this is not used here.. but might be elsewhere..
36483             this.sortState = state;
36484             
36485         } else {
36486             mstate = this.ds.sortToggle;
36487         }
36488         //remove existing sort classes..
36489         
36490         var sc = this.sortClasses;
36491         var hds = this.el.select(this.headerSelector).removeClass(sc);
36492         
36493         for(var f in mstate) {
36494         
36495             var sortColumn = this.cm.findColumnIndex(f);
36496             
36497             if(sortColumn != -1){
36498                 var sortDir = mstate[f];        
36499                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36500             }
36501         }
36502         
36503          
36504         
36505     },
36506
36507
36508     handleHeaderClick : function(g, index){
36509         if(this.headersDisabled){
36510             return;
36511         }
36512         var dm = g.dataSource, cm = g.colModel;
36513         if(!cm.isSortable(index)){
36514             return;
36515         }
36516         g.stopEditing();
36517         
36518         if (dm.multiSort) {
36519             // update the sortOrder
36520             var so = [];
36521             for(var i = 0; i < cm.config.length; i++ ) {
36522                 
36523                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36524                     continue; // dont' bother, it's not in sort list or being set.
36525                 }
36526                 
36527                 so.push(cm.config[i].dataIndex);
36528             };
36529             dm.sortOrder = so;
36530         }
36531         
36532         
36533         dm.sort(cm.getDataIndex(index));
36534     },
36535
36536
36537     destroy : function(){
36538         if(this.colMenu){
36539             this.colMenu.removeAll();
36540             Roo.menu.MenuMgr.unregister(this.colMenu);
36541             this.colMenu.getEl().remove();
36542             delete this.colMenu;
36543         }
36544         if(this.hmenu){
36545             this.hmenu.removeAll();
36546             Roo.menu.MenuMgr.unregister(this.hmenu);
36547             this.hmenu.getEl().remove();
36548             delete this.hmenu;
36549         }
36550         if(this.grid.enableColumnMove){
36551             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36552             if(dds){
36553                 for(var dd in dds){
36554                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36555                         var elid = dds[dd].dragElId;
36556                         dds[dd].unreg();
36557                         Roo.get(elid).remove();
36558                     } else if(dds[dd].config.isTarget){
36559                         dds[dd].proxyTop.remove();
36560                         dds[dd].proxyBottom.remove();
36561                         dds[dd].unreg();
36562                     }
36563                     if(Roo.dd.DDM.locationCache[dd]){
36564                         delete Roo.dd.DDM.locationCache[dd];
36565                     }
36566                 }
36567                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36568             }
36569         }
36570         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36571         this.bind(null, null);
36572         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36573     },
36574
36575     handleLockChange : function(){
36576         this.refresh(true);
36577     },
36578
36579     onDenyColumnLock : function(){
36580
36581     },
36582
36583     onDenyColumnHide : function(){
36584
36585     },
36586
36587     handleHdMenuClick : function(item){
36588         var index = this.hdCtxIndex;
36589         var cm = this.cm, ds = this.ds;
36590         switch(item.id){
36591             case "asc":
36592                 ds.sort(cm.getDataIndex(index), "ASC");
36593                 break;
36594             case "desc":
36595                 ds.sort(cm.getDataIndex(index), "DESC");
36596                 break;
36597             case "lock":
36598                 var lc = cm.getLockedCount();
36599                 if(cm.getColumnCount(true) <= lc+1){
36600                     this.onDenyColumnLock();
36601                     return;
36602                 }
36603                 if(lc != index){
36604                     cm.setLocked(index, true, true);
36605                     cm.moveColumn(index, lc);
36606                     this.grid.fireEvent("columnmove", index, lc);
36607                 }else{
36608                     cm.setLocked(index, true);
36609                 }
36610             break;
36611             case "unlock":
36612                 var lc = cm.getLockedCount();
36613                 if((lc-1) != index){
36614                     cm.setLocked(index, false, true);
36615                     cm.moveColumn(index, lc-1);
36616                     this.grid.fireEvent("columnmove", index, lc-1);
36617                 }else{
36618                     cm.setLocked(index, false);
36619                 }
36620             break;
36621             default:
36622                 index = cm.getIndexById(item.id.substr(4));
36623                 if(index != -1){
36624                     if(item.checked && cm.getColumnCount(true) <= 1){
36625                         this.onDenyColumnHide();
36626                         return false;
36627                     }
36628                     cm.setHidden(index, item.checked);
36629                 }
36630         }
36631         return true;
36632     },
36633
36634     beforeColMenuShow : function(){
36635         var cm = this.cm,  colCount = cm.getColumnCount();
36636         this.colMenu.removeAll();
36637         for(var i = 0; i < colCount; i++){
36638             this.colMenu.add(new Roo.menu.CheckItem({
36639                 id: "col-"+cm.getColumnId(i),
36640                 text: cm.getColumnHeader(i),
36641                 checked: !cm.isHidden(i),
36642                 hideOnClick:false
36643             }));
36644         }
36645     },
36646
36647     handleHdCtx : function(g, index, e){
36648         e.stopEvent();
36649         var hd = this.getHeaderCell(index);
36650         this.hdCtxIndex = index;
36651         var ms = this.hmenu.items, cm = this.cm;
36652         ms.get("asc").setDisabled(!cm.isSortable(index));
36653         ms.get("desc").setDisabled(!cm.isSortable(index));
36654         if(this.grid.enableColLock !== false){
36655             ms.get("lock").setDisabled(cm.isLocked(index));
36656             ms.get("unlock").setDisabled(!cm.isLocked(index));
36657         }
36658         this.hmenu.show(hd, "tl-bl");
36659     },
36660
36661     handleHdOver : function(e){
36662         var hd = this.findHeaderCell(e.getTarget());
36663         if(hd && !this.headersDisabled){
36664             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36665                this.fly(hd).addClass("x-grid-hd-over");
36666             }
36667         }
36668     },
36669
36670     handleHdOut : function(e){
36671         var hd = this.findHeaderCell(e.getTarget());
36672         if(hd){
36673             this.fly(hd).removeClass("x-grid-hd-over");
36674         }
36675     },
36676
36677     handleSplitDblClick : function(e, t){
36678         var i = this.getCellIndex(t);
36679         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36680             this.autoSizeColumn(i, true);
36681             this.layout();
36682         }
36683     },
36684
36685     render : function(){
36686
36687         var cm = this.cm;
36688         var colCount = cm.getColumnCount();
36689
36690         if(this.grid.monitorWindowResize === true){
36691             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36692         }
36693         var header = this.renderHeaders();
36694         var body = this.templates.body.apply({rows:""});
36695         var html = this.templates.master.apply({
36696             lockedBody: body,
36697             body: body,
36698             lockedHeader: header[0],
36699             header: header[1]
36700         });
36701
36702         //this.updateColumns();
36703
36704         this.grid.getGridEl().dom.innerHTML = html;
36705
36706         this.initElements();
36707         
36708         // a kludge to fix the random scolling effect in webkit
36709         this.el.on("scroll", function() {
36710             this.el.dom.scrollTop=0; // hopefully not recursive..
36711         },this);
36712
36713         this.scroller.on("scroll", this.handleScroll, this);
36714         this.lockedBody.on("mousewheel", this.handleWheel, this);
36715         this.mainBody.on("mousewheel", this.handleWheel, this);
36716
36717         this.mainHd.on("mouseover", this.handleHdOver, this);
36718         this.mainHd.on("mouseout", this.handleHdOut, this);
36719         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36720                 {delegate: "."+this.splitClass});
36721
36722         this.lockedHd.on("mouseover", this.handleHdOver, this);
36723         this.lockedHd.on("mouseout", this.handleHdOut, this);
36724         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36725                 {delegate: "."+this.splitClass});
36726
36727         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36728             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36729         }
36730
36731         this.updateSplitters();
36732
36733         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36734             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36735             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36736         }
36737
36738         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36739             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36740             this.hmenu.add(
36741                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36742                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36743             );
36744             if(this.grid.enableColLock !== false){
36745                 this.hmenu.add('-',
36746                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36747                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36748                 );
36749             }
36750             if(this.grid.enableColumnHide !== false){
36751
36752                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36753                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36754                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36755
36756                 this.hmenu.add('-',
36757                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36758                 );
36759             }
36760             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36761
36762             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36763         }
36764
36765         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36766             this.dd = new Roo.grid.GridDragZone(this.grid, {
36767                 ddGroup : this.grid.ddGroup || 'GridDD'
36768             });
36769             
36770         }
36771
36772         /*
36773         for(var i = 0; i < colCount; i++){
36774             if(cm.isHidden(i)){
36775                 this.hideColumn(i);
36776             }
36777             if(cm.config[i].align){
36778                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36779                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36780             }
36781         }*/
36782         
36783         this.updateHeaderSortState();
36784
36785         this.beforeInitialResize();
36786         this.layout(true);
36787
36788         // two part rendering gives faster view to the user
36789         this.renderPhase2.defer(1, this);
36790     },
36791
36792     renderPhase2 : function(){
36793         // render the rows now
36794         this.refresh();
36795         if(this.grid.autoSizeColumns){
36796             this.autoSizeColumns();
36797         }
36798     },
36799
36800     beforeInitialResize : function(){
36801
36802     },
36803
36804     onColumnSplitterMoved : function(i, w){
36805         this.userResized = true;
36806         var cm = this.grid.colModel;
36807         cm.setColumnWidth(i, w, true);
36808         var cid = cm.getColumnId(i);
36809         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36810         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36811         this.updateSplitters();
36812         this.layout();
36813         this.grid.fireEvent("columnresize", i, w);
36814     },
36815
36816     syncRowHeights : function(startIndex, endIndex){
36817         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36818             startIndex = startIndex || 0;
36819             var mrows = this.getBodyTable().rows;
36820             var lrows = this.getLockedTable().rows;
36821             var len = mrows.length-1;
36822             endIndex = Math.min(endIndex || len, len);
36823             for(var i = startIndex; i <= endIndex; i++){
36824                 var m = mrows[i], l = lrows[i];
36825                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36826                 m.style.height = l.style.height = h + "px";
36827             }
36828         }
36829     },
36830
36831     layout : function(initialRender, is2ndPass){
36832         var g = this.grid;
36833         var auto = g.autoHeight;
36834         var scrollOffset = 16;
36835         var c = g.getGridEl(), cm = this.cm,
36836                 expandCol = g.autoExpandColumn,
36837                 gv = this;
36838         //c.beginMeasure();
36839
36840         if(!c.dom.offsetWidth){ // display:none?
36841             if(initialRender){
36842                 this.lockedWrap.show();
36843                 this.mainWrap.show();
36844             }
36845             return;
36846         }
36847
36848         var hasLock = this.cm.isLocked(0);
36849
36850         var tbh = this.headerPanel.getHeight();
36851         var bbh = this.footerPanel.getHeight();
36852
36853         if(auto){
36854             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36855             var newHeight = ch + c.getBorderWidth("tb");
36856             if(g.maxHeight){
36857                 newHeight = Math.min(g.maxHeight, newHeight);
36858             }
36859             c.setHeight(newHeight);
36860         }
36861
36862         if(g.autoWidth){
36863             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36864         }
36865
36866         var s = this.scroller;
36867
36868         var csize = c.getSize(true);
36869
36870         this.el.setSize(csize.width, csize.height);
36871
36872         this.headerPanel.setWidth(csize.width);
36873         this.footerPanel.setWidth(csize.width);
36874
36875         var hdHeight = this.mainHd.getHeight();
36876         var vw = csize.width;
36877         var vh = csize.height - (tbh + bbh);
36878
36879         s.setSize(vw, vh);
36880
36881         var bt = this.getBodyTable();
36882         var ltWidth = hasLock ?
36883                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36884
36885         var scrollHeight = bt.offsetHeight;
36886         var scrollWidth = ltWidth + bt.offsetWidth;
36887         var vscroll = false, hscroll = false;
36888
36889         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36890
36891         var lw = this.lockedWrap, mw = this.mainWrap;
36892         var lb = this.lockedBody, mb = this.mainBody;
36893
36894         setTimeout(function(){
36895             var t = s.dom.offsetTop;
36896             var w = s.dom.clientWidth,
36897                 h = s.dom.clientHeight;
36898
36899             lw.setTop(t);
36900             lw.setSize(ltWidth, h);
36901
36902             mw.setLeftTop(ltWidth, t);
36903             mw.setSize(w-ltWidth, h);
36904
36905             lb.setHeight(h-hdHeight);
36906             mb.setHeight(h-hdHeight);
36907
36908             if(is2ndPass !== true && !gv.userResized && expandCol){
36909                 // high speed resize without full column calculation
36910                 
36911                 var ci = cm.getIndexById(expandCol);
36912                 if (ci < 0) {
36913                     ci = cm.findColumnIndex(expandCol);
36914                 }
36915                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36916                 var expandId = cm.getColumnId(ci);
36917                 var  tw = cm.getTotalWidth(false);
36918                 var currentWidth = cm.getColumnWidth(ci);
36919                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36920                 if(currentWidth != cw){
36921                     cm.setColumnWidth(ci, cw, true);
36922                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36923                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36924                     gv.updateSplitters();
36925                     gv.layout(false, true);
36926                 }
36927             }
36928
36929             if(initialRender){
36930                 lw.show();
36931                 mw.show();
36932             }
36933             //c.endMeasure();
36934         }, 10);
36935     },
36936
36937     onWindowResize : function(){
36938         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36939             return;
36940         }
36941         this.layout();
36942     },
36943
36944     appendFooter : function(parentEl){
36945         return null;
36946     },
36947
36948     sortAscText : "Sort Ascending",
36949     sortDescText : "Sort Descending",
36950     lockText : "Lock Column",
36951     unlockText : "Unlock Column",
36952     columnsText : "Columns"
36953 });
36954
36955
36956 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36957     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36958     this.proxy.el.addClass('x-grid3-col-dd');
36959 };
36960
36961 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36962     handleMouseDown : function(e){
36963
36964     },
36965
36966     callHandleMouseDown : function(e){
36967         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36968     }
36969 });
36970 /*
36971  * Based on:
36972  * Ext JS Library 1.1.1
36973  * Copyright(c) 2006-2007, Ext JS, LLC.
36974  *
36975  * Originally Released Under LGPL - original licence link has changed is not relivant.
36976  *
36977  * Fork - LGPL
36978  * <script type="text/javascript">
36979  */
36980  
36981 // private
36982 // This is a support class used internally by the Grid components
36983 Roo.grid.SplitDragZone = function(grid, hd, hd2){
36984     this.grid = grid;
36985     this.view = grid.getView();
36986     this.proxy = this.view.resizeProxy;
36987     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
36988         "gridSplitters" + this.grid.getGridEl().id, {
36989         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
36990     });
36991     this.setHandleElId(Roo.id(hd));
36992     this.setOuterHandleElId(Roo.id(hd2));
36993     this.scroll = false;
36994 };
36995 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
36996     fly: Roo.Element.fly,
36997
36998     b4StartDrag : function(x, y){
36999         this.view.headersDisabled = true;
37000         this.proxy.setHeight(this.view.mainWrap.getHeight());
37001         var w = this.cm.getColumnWidth(this.cellIndex);
37002         var minw = Math.max(w-this.grid.minColumnWidth, 0);
37003         this.resetConstraints();
37004         this.setXConstraint(minw, 1000);
37005         this.setYConstraint(0, 0);
37006         this.minX = x - minw;
37007         this.maxX = x + 1000;
37008         this.startPos = x;
37009         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
37010     },
37011
37012
37013     handleMouseDown : function(e){
37014         ev = Roo.EventObject.setEvent(e);
37015         var t = this.fly(ev.getTarget());
37016         if(t.hasClass("x-grid-split")){
37017             this.cellIndex = this.view.getCellIndex(t.dom);
37018             this.split = t.dom;
37019             this.cm = this.grid.colModel;
37020             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
37021                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
37022             }
37023         }
37024     },
37025
37026     endDrag : function(e){
37027         this.view.headersDisabled = false;
37028         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
37029         var diff = endX - this.startPos;
37030         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
37031     },
37032
37033     autoOffset : function(){
37034         this.setDelta(0,0);
37035     }
37036 });/*
37037  * Based on:
37038  * Ext JS Library 1.1.1
37039  * Copyright(c) 2006-2007, Ext JS, LLC.
37040  *
37041  * Originally Released Under LGPL - original licence link has changed is not relivant.
37042  *
37043  * Fork - LGPL
37044  * <script type="text/javascript">
37045  */
37046  
37047 // private
37048 // This is a support class used internally by the Grid components
37049 Roo.grid.GridDragZone = function(grid, config){
37050     this.view = grid.getView();
37051     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
37052     if(this.view.lockedBody){
37053         this.setHandleElId(Roo.id(this.view.mainBody.dom));
37054         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
37055     }
37056     this.scroll = false;
37057     this.grid = grid;
37058     this.ddel = document.createElement('div');
37059     this.ddel.className = 'x-grid-dd-wrap';
37060 };
37061
37062 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
37063     ddGroup : "GridDD",
37064
37065     getDragData : function(e){
37066         var t = Roo.lib.Event.getTarget(e);
37067         var rowIndex = this.view.findRowIndex(t);
37068         var sm = this.grid.selModel;
37069             
37070         //Roo.log(rowIndex);
37071         
37072         if (sm.getSelectedCell) {
37073             // cell selection..
37074             if (!sm.getSelectedCell()) {
37075                 return false;
37076             }
37077             if (rowIndex != sm.getSelectedCell()[0]) {
37078                 return false;
37079             }
37080         
37081         }
37082         
37083         if(rowIndex !== false){
37084             
37085             // if editorgrid.. 
37086             
37087             
37088             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
37089                
37090             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
37091               //  
37092             //}
37093             if (e.hasModifier()){
37094                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
37095             }
37096             
37097             Roo.log("getDragData");
37098             
37099             return {
37100                 grid: this.grid,
37101                 ddel: this.ddel,
37102                 rowIndex: rowIndex,
37103                 selections:sm.getSelections ? sm.getSelections() : (
37104                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
37105                 )
37106             };
37107         }
37108         return false;
37109     },
37110
37111     onInitDrag : function(e){
37112         var data = this.dragData;
37113         this.ddel.innerHTML = this.grid.getDragDropText();
37114         this.proxy.update(this.ddel);
37115         // fire start drag?
37116     },
37117
37118     afterRepair : function(){
37119         this.dragging = false;
37120     },
37121
37122     getRepairXY : function(e, data){
37123         return false;
37124     },
37125
37126     onEndDrag : function(data, e){
37127         // fire end drag?
37128     },
37129
37130     onValidDrop : function(dd, e, id){
37131         // fire drag drop?
37132         this.hideProxy();
37133     },
37134
37135     beforeInvalidDrop : function(e, id){
37136
37137     }
37138 });/*
37139  * Based on:
37140  * Ext JS Library 1.1.1
37141  * Copyright(c) 2006-2007, Ext JS, LLC.
37142  *
37143  * Originally Released Under LGPL - original licence link has changed is not relivant.
37144  *
37145  * Fork - LGPL
37146  * <script type="text/javascript">
37147  */
37148  
37149
37150 /**
37151  * @class Roo.grid.ColumnModel
37152  * @extends Roo.util.Observable
37153  * This is the default implementation of a ColumnModel used by the Grid. It defines
37154  * the columns in the grid.
37155  * <br>Usage:<br>
37156  <pre><code>
37157  var colModel = new Roo.grid.ColumnModel([
37158         {header: "Ticker", width: 60, sortable: true, locked: true},
37159         {header: "Company Name", width: 150, sortable: true},
37160         {header: "Market Cap.", width: 100, sortable: true},
37161         {header: "$ Sales", width: 100, sortable: true, renderer: money},
37162         {header: "Employees", width: 100, sortable: true, resizable: false}
37163  ]);
37164  </code></pre>
37165  * <p>
37166  
37167  * The config options listed for this class are options which may appear in each
37168  * individual column definition.
37169  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
37170  * @constructor
37171  * @param {Object} config An Array of column config objects. See this class's
37172  * config objects for details.
37173 */
37174 Roo.grid.ColumnModel = function(config){
37175         /**
37176      * The config passed into the constructor
37177      */
37178     this.config = config;
37179     this.lookup = {};
37180
37181     // if no id, create one
37182     // if the column does not have a dataIndex mapping,
37183     // map it to the order it is in the config
37184     for(var i = 0, len = config.length; i < len; i++){
37185         var c = config[i];
37186         if(typeof c.dataIndex == "undefined"){
37187             c.dataIndex = i;
37188         }
37189         if(typeof c.renderer == "string"){
37190             c.renderer = Roo.util.Format[c.renderer];
37191         }
37192         if(typeof c.id == "undefined"){
37193             c.id = Roo.id();
37194         }
37195         if(c.editor && c.editor.xtype){
37196             c.editor  = Roo.factory(c.editor, Roo.grid);
37197         }
37198         if(c.editor && c.editor.isFormField){
37199             c.editor = new Roo.grid.GridEditor(c.editor);
37200         }
37201         this.lookup[c.id] = c;
37202     }
37203
37204     /**
37205      * The width of columns which have no width specified (defaults to 100)
37206      * @type Number
37207      */
37208     this.defaultWidth = 100;
37209
37210     /**
37211      * Default sortable of columns which have no sortable specified (defaults to false)
37212      * @type Boolean
37213      */
37214     this.defaultSortable = false;
37215
37216     this.addEvents({
37217         /**
37218              * @event widthchange
37219              * Fires when the width of a column changes.
37220              * @param {ColumnModel} this
37221              * @param {Number} columnIndex The column index
37222              * @param {Number} newWidth The new width
37223              */
37224             "widthchange": true,
37225         /**
37226              * @event headerchange
37227              * Fires when the text of a header changes.
37228              * @param {ColumnModel} this
37229              * @param {Number} columnIndex The column index
37230              * @param {Number} newText The new header text
37231              */
37232             "headerchange": true,
37233         /**
37234              * @event hiddenchange
37235              * Fires when a column is hidden or "unhidden".
37236              * @param {ColumnModel} this
37237              * @param {Number} columnIndex The column index
37238              * @param {Boolean} hidden true if hidden, false otherwise
37239              */
37240             "hiddenchange": true,
37241             /**
37242          * @event columnmoved
37243          * Fires when a column is moved.
37244          * @param {ColumnModel} this
37245          * @param {Number} oldIndex
37246          * @param {Number} newIndex
37247          */
37248         "columnmoved" : true,
37249         /**
37250          * @event columlockchange
37251          * Fires when a column's locked state is changed
37252          * @param {ColumnModel} this
37253          * @param {Number} colIndex
37254          * @param {Boolean} locked true if locked
37255          */
37256         "columnlockchange" : true
37257     });
37258     Roo.grid.ColumnModel.superclass.constructor.call(this);
37259 };
37260 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
37261     /**
37262      * @cfg {String} header The header text to display in the Grid view.
37263      */
37264     /**
37265      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
37266      * {@link Roo.data.Record} definition from which to draw the column's value. If not
37267      * specified, the column's index is used as an index into the Record's data Array.
37268      */
37269     /**
37270      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
37271      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
37272      */
37273     /**
37274      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
37275      * Defaults to the value of the {@link #defaultSortable} property.
37276      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
37277      */
37278     /**
37279      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
37280      */
37281     /**
37282      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
37283      */
37284     /**
37285      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
37286      */
37287     /**
37288      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
37289      */
37290     /**
37291      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
37292      * given the cell's data value. See {@link #setRenderer}. If not specified, the
37293      * default renderer uses the raw data value.
37294      */
37295        /**
37296      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
37297      */
37298     /**
37299      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
37300      */
37301
37302     /**
37303      * Returns the id of the column at the specified index.
37304      * @param {Number} index The column index
37305      * @return {String} the id
37306      */
37307     getColumnId : function(index){
37308         return this.config[index].id;
37309     },
37310
37311     /**
37312      * Returns the column for a specified id.
37313      * @param {String} id The column id
37314      * @return {Object} the column
37315      */
37316     getColumnById : function(id){
37317         return this.lookup[id];
37318     },
37319
37320     
37321     /**
37322      * Returns the column for a specified dataIndex.
37323      * @param {String} dataIndex The column dataIndex
37324      * @return {Object|Boolean} the column or false if not found
37325      */
37326     getColumnByDataIndex: function(dataIndex){
37327         var index = this.findColumnIndex(dataIndex);
37328         return index > -1 ? this.config[index] : false;
37329     },
37330     
37331     /**
37332      * Returns the index for a specified column id.
37333      * @param {String} id The column id
37334      * @return {Number} the index, or -1 if not found
37335      */
37336     getIndexById : function(id){
37337         for(var i = 0, len = this.config.length; i < len; i++){
37338             if(this.config[i].id == id){
37339                 return i;
37340             }
37341         }
37342         return -1;
37343     },
37344     
37345     /**
37346      * Returns the index for a specified column dataIndex.
37347      * @param {String} dataIndex The column dataIndex
37348      * @return {Number} the index, or -1 if not found
37349      */
37350     
37351     findColumnIndex : function(dataIndex){
37352         for(var i = 0, len = this.config.length; i < len; i++){
37353             if(this.config[i].dataIndex == dataIndex){
37354                 return i;
37355             }
37356         }
37357         return -1;
37358     },
37359     
37360     
37361     moveColumn : function(oldIndex, newIndex){
37362         var c = this.config[oldIndex];
37363         this.config.splice(oldIndex, 1);
37364         this.config.splice(newIndex, 0, c);
37365         this.dataMap = null;
37366         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37367     },
37368
37369     isLocked : function(colIndex){
37370         return this.config[colIndex].locked === true;
37371     },
37372
37373     setLocked : function(colIndex, value, suppressEvent){
37374         if(this.isLocked(colIndex) == value){
37375             return;
37376         }
37377         this.config[colIndex].locked = value;
37378         if(!suppressEvent){
37379             this.fireEvent("columnlockchange", this, colIndex, value);
37380         }
37381     },
37382
37383     getTotalLockedWidth : function(){
37384         var totalWidth = 0;
37385         for(var i = 0; i < this.config.length; i++){
37386             if(this.isLocked(i) && !this.isHidden(i)){
37387                 this.totalWidth += this.getColumnWidth(i);
37388             }
37389         }
37390         return totalWidth;
37391     },
37392
37393     getLockedCount : function(){
37394         for(var i = 0, len = this.config.length; i < len; i++){
37395             if(!this.isLocked(i)){
37396                 return i;
37397             }
37398         }
37399     },
37400
37401     /**
37402      * Returns the number of columns.
37403      * @return {Number}
37404      */
37405     getColumnCount : function(visibleOnly){
37406         if(visibleOnly === true){
37407             var c = 0;
37408             for(var i = 0, len = this.config.length; i < len; i++){
37409                 if(!this.isHidden(i)){
37410                     c++;
37411                 }
37412             }
37413             return c;
37414         }
37415         return this.config.length;
37416     },
37417
37418     /**
37419      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37420      * @param {Function} fn
37421      * @param {Object} scope (optional)
37422      * @return {Array} result
37423      */
37424     getColumnsBy : function(fn, scope){
37425         var r = [];
37426         for(var i = 0, len = this.config.length; i < len; i++){
37427             var c = this.config[i];
37428             if(fn.call(scope||this, c, i) === true){
37429                 r[r.length] = c;
37430             }
37431         }
37432         return r;
37433     },
37434
37435     /**
37436      * Returns true if the specified column is sortable.
37437      * @param {Number} col The column index
37438      * @return {Boolean}
37439      */
37440     isSortable : function(col){
37441         if(typeof this.config[col].sortable == "undefined"){
37442             return this.defaultSortable;
37443         }
37444         return this.config[col].sortable;
37445     },
37446
37447     /**
37448      * Returns the rendering (formatting) function defined for the column.
37449      * @param {Number} col The column index.
37450      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37451      */
37452     getRenderer : function(col){
37453         if(!this.config[col].renderer){
37454             return Roo.grid.ColumnModel.defaultRenderer;
37455         }
37456         return this.config[col].renderer;
37457     },
37458
37459     /**
37460      * Sets the rendering (formatting) function for a column.
37461      * @param {Number} col The column index
37462      * @param {Function} fn The function to use to process the cell's raw data
37463      * to return HTML markup for the grid view. The render function is called with
37464      * the following parameters:<ul>
37465      * <li>Data value.</li>
37466      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37467      * <li>css A CSS style string to apply to the table cell.</li>
37468      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37469      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37470      * <li>Row index</li>
37471      * <li>Column index</li>
37472      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37473      */
37474     setRenderer : function(col, fn){
37475         this.config[col].renderer = fn;
37476     },
37477
37478     /**
37479      * Returns the width for the specified column.
37480      * @param {Number} col The column index
37481      * @return {Number}
37482      */
37483     getColumnWidth : function(col){
37484         return this.config[col].width * 1 || this.defaultWidth;
37485     },
37486
37487     /**
37488      * Sets the width for a column.
37489      * @param {Number} col The column index
37490      * @param {Number} width The new width
37491      */
37492     setColumnWidth : function(col, width, suppressEvent){
37493         this.config[col].width = width;
37494         this.totalWidth = null;
37495         if(!suppressEvent){
37496              this.fireEvent("widthchange", this, col, width);
37497         }
37498     },
37499
37500     /**
37501      * Returns the total width of all columns.
37502      * @param {Boolean} includeHidden True to include hidden column widths
37503      * @return {Number}
37504      */
37505     getTotalWidth : function(includeHidden){
37506         if(!this.totalWidth){
37507             this.totalWidth = 0;
37508             for(var i = 0, len = this.config.length; i < len; i++){
37509                 if(includeHidden || !this.isHidden(i)){
37510                     this.totalWidth += this.getColumnWidth(i);
37511                 }
37512             }
37513         }
37514         return this.totalWidth;
37515     },
37516
37517     /**
37518      * Returns the header for the specified column.
37519      * @param {Number} col The column index
37520      * @return {String}
37521      */
37522     getColumnHeader : function(col){
37523         return this.config[col].header;
37524     },
37525
37526     /**
37527      * Sets the header for a column.
37528      * @param {Number} col The column index
37529      * @param {String} header The new header
37530      */
37531     setColumnHeader : function(col, header){
37532         this.config[col].header = header;
37533         this.fireEvent("headerchange", this, col, header);
37534     },
37535
37536     /**
37537      * Returns the tooltip for the specified column.
37538      * @param {Number} col The column index
37539      * @return {String}
37540      */
37541     getColumnTooltip : function(col){
37542             return this.config[col].tooltip;
37543     },
37544     /**
37545      * Sets the tooltip for a column.
37546      * @param {Number} col The column index
37547      * @param {String} tooltip The new tooltip
37548      */
37549     setColumnTooltip : function(col, tooltip){
37550             this.config[col].tooltip = tooltip;
37551     },
37552
37553     /**
37554      * Returns the dataIndex for the specified column.
37555      * @param {Number} col The column index
37556      * @return {Number}
37557      */
37558     getDataIndex : function(col){
37559         return this.config[col].dataIndex;
37560     },
37561
37562     /**
37563      * Sets the dataIndex for a column.
37564      * @param {Number} col The column index
37565      * @param {Number} dataIndex The new dataIndex
37566      */
37567     setDataIndex : function(col, dataIndex){
37568         this.config[col].dataIndex = dataIndex;
37569     },
37570
37571     
37572     
37573     /**
37574      * Returns true if the cell is editable.
37575      * @param {Number} colIndex The column index
37576      * @param {Number} rowIndex The row index
37577      * @return {Boolean}
37578      */
37579     isCellEditable : function(colIndex, rowIndex){
37580         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
37581     },
37582
37583     /**
37584      * Returns the editor defined for the cell/column.
37585      * return false or null to disable editing.
37586      * @param {Number} colIndex The column index
37587      * @param {Number} rowIndex The row index
37588      * @return {Object}
37589      */
37590     getCellEditor : function(colIndex, rowIndex){
37591         return this.config[colIndex].editor;
37592     },
37593
37594     /**
37595      * Sets if a column is editable.
37596      * @param {Number} col The column index
37597      * @param {Boolean} editable True if the column is editable
37598      */
37599     setEditable : function(col, editable){
37600         this.config[col].editable = editable;
37601     },
37602
37603
37604     /**
37605      * Returns true if the column is hidden.
37606      * @param {Number} colIndex The column index
37607      * @return {Boolean}
37608      */
37609     isHidden : function(colIndex){
37610         return this.config[colIndex].hidden;
37611     },
37612
37613
37614     /**
37615      * Returns true if the column width cannot be changed
37616      */
37617     isFixed : function(colIndex){
37618         return this.config[colIndex].fixed;
37619     },
37620
37621     /**
37622      * Returns true if the column can be resized
37623      * @return {Boolean}
37624      */
37625     isResizable : function(colIndex){
37626         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
37627     },
37628     /**
37629      * Sets if a column is hidden.
37630      * @param {Number} colIndex The column index
37631      * @param {Boolean} hidden True if the column is hidden
37632      */
37633     setHidden : function(colIndex, hidden){
37634         this.config[colIndex].hidden = hidden;
37635         this.totalWidth = null;
37636         this.fireEvent("hiddenchange", this, colIndex, hidden);
37637     },
37638
37639     /**
37640      * Sets the editor for a column.
37641      * @param {Number} col The column index
37642      * @param {Object} editor The editor object
37643      */
37644     setEditor : function(col, editor){
37645         this.config[col].editor = editor;
37646     }
37647 });
37648
37649 Roo.grid.ColumnModel.defaultRenderer = function(value){
37650         if(typeof value == "string" && value.length < 1){
37651             return "&#160;";
37652         }
37653         return value;
37654 };
37655
37656 // Alias for backwards compatibility
37657 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
37658 /*
37659  * Based on:
37660  * Ext JS Library 1.1.1
37661  * Copyright(c) 2006-2007, Ext JS, LLC.
37662  *
37663  * Originally Released Under LGPL - original licence link has changed is not relivant.
37664  *
37665  * Fork - LGPL
37666  * <script type="text/javascript">
37667  */
37668
37669 /**
37670  * @class Roo.grid.AbstractSelectionModel
37671  * @extends Roo.util.Observable
37672  * Abstract base class for grid SelectionModels.  It provides the interface that should be
37673  * implemented by descendant classes.  This class should not be directly instantiated.
37674  * @constructor
37675  */
37676 Roo.grid.AbstractSelectionModel = function(){
37677     this.locked = false;
37678     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
37679 };
37680
37681 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
37682     /** @ignore Called by the grid automatically. Do not call directly. */
37683     init : function(grid){
37684         this.grid = grid;
37685         this.initEvents();
37686     },
37687
37688     /**
37689      * Locks the selections.
37690      */
37691     lock : function(){
37692         this.locked = true;
37693     },
37694
37695     /**
37696      * Unlocks the selections.
37697      */
37698     unlock : function(){
37699         this.locked = false;
37700     },
37701
37702     /**
37703      * Returns true if the selections are locked.
37704      * @return {Boolean}
37705      */
37706     isLocked : function(){
37707         return this.locked;
37708     }
37709 });/*
37710  * Based on:
37711  * Ext JS Library 1.1.1
37712  * Copyright(c) 2006-2007, Ext JS, LLC.
37713  *
37714  * Originally Released Under LGPL - original licence link has changed is not relivant.
37715  *
37716  * Fork - LGPL
37717  * <script type="text/javascript">
37718  */
37719 /**
37720  * @extends Roo.grid.AbstractSelectionModel
37721  * @class Roo.grid.RowSelectionModel
37722  * The default SelectionModel used by {@link Roo.grid.Grid}.
37723  * It supports multiple selections and keyboard selection/navigation. 
37724  * @constructor
37725  * @param {Object} config
37726  */
37727 Roo.grid.RowSelectionModel = function(config){
37728     Roo.apply(this, config);
37729     this.selections = new Roo.util.MixedCollection(false, function(o){
37730         return o.id;
37731     });
37732
37733     this.last = false;
37734     this.lastActive = false;
37735
37736     this.addEvents({
37737         /**
37738              * @event selectionchange
37739              * Fires when the selection changes
37740              * @param {SelectionModel} this
37741              */
37742             "selectionchange" : true,
37743         /**
37744              * @event afterselectionchange
37745              * Fires after the selection changes (eg. by key press or clicking)
37746              * @param {SelectionModel} this
37747              */
37748             "afterselectionchange" : true,
37749         /**
37750              * @event beforerowselect
37751              * Fires when a row is selected being selected, return false to cancel.
37752              * @param {SelectionModel} this
37753              * @param {Number} rowIndex The selected index
37754              * @param {Boolean} keepExisting False if other selections will be cleared
37755              */
37756             "beforerowselect" : true,
37757         /**
37758              * @event rowselect
37759              * Fires when a row is selected.
37760              * @param {SelectionModel} this
37761              * @param {Number} rowIndex The selected index
37762              * @param {Roo.data.Record} r The record
37763              */
37764             "rowselect" : true,
37765         /**
37766              * @event rowdeselect
37767              * Fires when a row is deselected.
37768              * @param {SelectionModel} this
37769              * @param {Number} rowIndex The selected index
37770              */
37771         "rowdeselect" : true
37772     });
37773     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
37774     this.locked = false;
37775 };
37776
37777 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
37778     /**
37779      * @cfg {Boolean} singleSelect
37780      * True to allow selection of only one row at a time (defaults to false)
37781      */
37782     singleSelect : false,
37783
37784     // private
37785     initEvents : function(){
37786
37787         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
37788             this.grid.on("mousedown", this.handleMouseDown, this);
37789         }else{ // allow click to work like normal
37790             this.grid.on("rowclick", this.handleDragableRowClick, this);
37791         }
37792
37793         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
37794             "up" : function(e){
37795                 if(!e.shiftKey){
37796                     this.selectPrevious(e.shiftKey);
37797                 }else if(this.last !== false && this.lastActive !== false){
37798                     var last = this.last;
37799                     this.selectRange(this.last,  this.lastActive-1);
37800                     this.grid.getView().focusRow(this.lastActive);
37801                     if(last !== false){
37802                         this.last = last;
37803                     }
37804                 }else{
37805                     this.selectFirstRow();
37806                 }
37807                 this.fireEvent("afterselectionchange", this);
37808             },
37809             "down" : function(e){
37810                 if(!e.shiftKey){
37811                     this.selectNext(e.shiftKey);
37812                 }else if(this.last !== false && this.lastActive !== false){
37813                     var last = this.last;
37814                     this.selectRange(this.last,  this.lastActive+1);
37815                     this.grid.getView().focusRow(this.lastActive);
37816                     if(last !== false){
37817                         this.last = last;
37818                     }
37819                 }else{
37820                     this.selectFirstRow();
37821                 }
37822                 this.fireEvent("afterselectionchange", this);
37823             },
37824             scope: this
37825         });
37826
37827         var view = this.grid.view;
37828         view.on("refresh", this.onRefresh, this);
37829         view.on("rowupdated", this.onRowUpdated, this);
37830         view.on("rowremoved", this.onRemove, this);
37831     },
37832
37833     // private
37834     onRefresh : function(){
37835         var ds = this.grid.dataSource, i, v = this.grid.view;
37836         var s = this.selections;
37837         s.each(function(r){
37838             if((i = ds.indexOfId(r.id)) != -1){
37839                 v.onRowSelect(i);
37840             }else{
37841                 s.remove(r);
37842             }
37843         });
37844     },
37845
37846     // private
37847     onRemove : function(v, index, r){
37848         this.selections.remove(r);
37849     },
37850
37851     // private
37852     onRowUpdated : function(v, index, r){
37853         if(this.isSelected(r)){
37854             v.onRowSelect(index);
37855         }
37856     },
37857
37858     /**
37859      * Select records.
37860      * @param {Array} records The records to select
37861      * @param {Boolean} keepExisting (optional) True to keep existing selections
37862      */
37863     selectRecords : function(records, keepExisting){
37864         if(!keepExisting){
37865             this.clearSelections();
37866         }
37867         var ds = this.grid.dataSource;
37868         for(var i = 0, len = records.length; i < len; i++){
37869             this.selectRow(ds.indexOf(records[i]), true);
37870         }
37871     },
37872
37873     /**
37874      * Gets the number of selected rows.
37875      * @return {Number}
37876      */
37877     getCount : function(){
37878         return this.selections.length;
37879     },
37880
37881     /**
37882      * Selects the first row in the grid.
37883      */
37884     selectFirstRow : function(){
37885         this.selectRow(0);
37886     },
37887
37888     /**
37889      * Select the last row.
37890      * @param {Boolean} keepExisting (optional) True to keep existing selections
37891      */
37892     selectLastRow : function(keepExisting){
37893         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
37894     },
37895
37896     /**
37897      * Selects the row immediately following the last selected row.
37898      * @param {Boolean} keepExisting (optional) True to keep existing selections
37899      */
37900     selectNext : function(keepExisting){
37901         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
37902             this.selectRow(this.last+1, keepExisting);
37903             this.grid.getView().focusRow(this.last);
37904         }
37905     },
37906
37907     /**
37908      * Selects the row that precedes the last selected row.
37909      * @param {Boolean} keepExisting (optional) True to keep existing selections
37910      */
37911     selectPrevious : function(keepExisting){
37912         if(this.last){
37913             this.selectRow(this.last-1, keepExisting);
37914             this.grid.getView().focusRow(this.last);
37915         }
37916     },
37917
37918     /**
37919      * Returns the selected records
37920      * @return {Array} Array of selected records
37921      */
37922     getSelections : function(){
37923         return [].concat(this.selections.items);
37924     },
37925
37926     /**
37927      * Returns the first selected record.
37928      * @return {Record}
37929      */
37930     getSelected : function(){
37931         return this.selections.itemAt(0);
37932     },
37933
37934
37935     /**
37936      * Clears all selections.
37937      */
37938     clearSelections : function(fast){
37939         if(this.locked) return;
37940         if(fast !== true){
37941             var ds = this.grid.dataSource;
37942             var s = this.selections;
37943             s.each(function(r){
37944                 this.deselectRow(ds.indexOfId(r.id));
37945             }, this);
37946             s.clear();
37947         }else{
37948             this.selections.clear();
37949         }
37950         this.last = false;
37951     },
37952
37953
37954     /**
37955      * Selects all rows.
37956      */
37957     selectAll : function(){
37958         if(this.locked) return;
37959         this.selections.clear();
37960         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
37961             this.selectRow(i, true);
37962         }
37963     },
37964
37965     /**
37966      * Returns True if there is a selection.
37967      * @return {Boolean}
37968      */
37969     hasSelection : function(){
37970         return this.selections.length > 0;
37971     },
37972
37973     /**
37974      * Returns True if the specified row is selected.
37975      * @param {Number/Record} record The record or index of the record to check
37976      * @return {Boolean}
37977      */
37978     isSelected : function(index){
37979         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
37980         return (r && this.selections.key(r.id) ? true : false);
37981     },
37982
37983     /**
37984      * Returns True if the specified record id is selected.
37985      * @param {String} id The id of record to check
37986      * @return {Boolean}
37987      */
37988     isIdSelected : function(id){
37989         return (this.selections.key(id) ? true : false);
37990     },
37991
37992     // private
37993     handleMouseDown : function(e, t){
37994         var view = this.grid.getView(), rowIndex;
37995         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
37996             return;
37997         };
37998         if(e.shiftKey && this.last !== false){
37999             var last = this.last;
38000             this.selectRange(last, rowIndex, e.ctrlKey);
38001             this.last = last; // reset the last
38002             view.focusRow(rowIndex);
38003         }else{
38004             var isSelected = this.isSelected(rowIndex);
38005             if(e.button !== 0 && isSelected){
38006                 view.focusRow(rowIndex);
38007             }else if(e.ctrlKey && isSelected){
38008                 this.deselectRow(rowIndex);
38009             }else if(!isSelected){
38010                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
38011                 view.focusRow(rowIndex);
38012             }
38013         }
38014         this.fireEvent("afterselectionchange", this);
38015     },
38016     // private
38017     handleDragableRowClick :  function(grid, rowIndex, e) 
38018     {
38019         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
38020             this.selectRow(rowIndex, false);
38021             grid.view.focusRow(rowIndex);
38022              this.fireEvent("afterselectionchange", this);
38023         }
38024     },
38025     
38026     /**
38027      * Selects multiple rows.
38028      * @param {Array} rows Array of the indexes of the row to select
38029      * @param {Boolean} keepExisting (optional) True to keep existing selections
38030      */
38031     selectRows : function(rows, keepExisting){
38032         if(!keepExisting){
38033             this.clearSelections();
38034         }
38035         for(var i = 0, len = rows.length; i < len; i++){
38036             this.selectRow(rows[i], true);
38037         }
38038     },
38039
38040     /**
38041      * Selects a range of rows. All rows in between startRow and endRow are also selected.
38042      * @param {Number} startRow The index of the first row in the range
38043      * @param {Number} endRow The index of the last row in the range
38044      * @param {Boolean} keepExisting (optional) True to retain existing selections
38045      */
38046     selectRange : function(startRow, endRow, keepExisting){
38047         if(this.locked) return;
38048         if(!keepExisting){
38049             this.clearSelections();
38050         }
38051         if(startRow <= endRow){
38052             for(var i = startRow; i <= endRow; i++){
38053                 this.selectRow(i, true);
38054             }
38055         }else{
38056             for(var i = startRow; i >= endRow; i--){
38057                 this.selectRow(i, true);
38058             }
38059         }
38060     },
38061
38062     /**
38063      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
38064      * @param {Number} startRow The index of the first row in the range
38065      * @param {Number} endRow The index of the last row in the range
38066      */
38067     deselectRange : function(startRow, endRow, preventViewNotify){
38068         if(this.locked) return;
38069         for(var i = startRow; i <= endRow; i++){
38070             this.deselectRow(i, preventViewNotify);
38071         }
38072     },
38073
38074     /**
38075      * Selects a row.
38076      * @param {Number} row The index of the row to select
38077      * @param {Boolean} keepExisting (optional) True to keep existing selections
38078      */
38079     selectRow : function(index, keepExisting, preventViewNotify){
38080         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
38081         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
38082             if(!keepExisting || this.singleSelect){
38083                 this.clearSelections();
38084             }
38085             var r = this.grid.dataSource.getAt(index);
38086             this.selections.add(r);
38087             this.last = this.lastActive = index;
38088             if(!preventViewNotify){
38089                 this.grid.getView().onRowSelect(index);
38090             }
38091             this.fireEvent("rowselect", this, index, r);
38092             this.fireEvent("selectionchange", this);
38093         }
38094     },
38095
38096     /**
38097      * Deselects a row.
38098      * @param {Number} row The index of the row to deselect
38099      */
38100     deselectRow : function(index, preventViewNotify){
38101         if(this.locked) return;
38102         if(this.last == index){
38103             this.last = false;
38104         }
38105         if(this.lastActive == index){
38106             this.lastActive = false;
38107         }
38108         var r = this.grid.dataSource.getAt(index);
38109         this.selections.remove(r);
38110         if(!preventViewNotify){
38111             this.grid.getView().onRowDeselect(index);
38112         }
38113         this.fireEvent("rowdeselect", this, index);
38114         this.fireEvent("selectionchange", this);
38115     },
38116
38117     // private
38118     restoreLast : function(){
38119         if(this._last){
38120             this.last = this._last;
38121         }
38122     },
38123
38124     // private
38125     acceptsNav : function(row, col, cm){
38126         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38127     },
38128
38129     // private
38130     onEditorKey : function(field, e){
38131         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
38132         if(k == e.TAB){
38133             e.stopEvent();
38134             ed.completeEdit();
38135             if(e.shiftKey){
38136                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38137             }else{
38138                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38139             }
38140         }else if(k == e.ENTER && !e.ctrlKey){
38141             e.stopEvent();
38142             ed.completeEdit();
38143             if(e.shiftKey){
38144                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
38145             }else{
38146                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
38147             }
38148         }else if(k == e.ESC){
38149             ed.cancelEdit();
38150         }
38151         if(newCell){
38152             g.startEditing(newCell[0], newCell[1]);
38153         }
38154     }
38155 });/*
38156  * Based on:
38157  * Ext JS Library 1.1.1
38158  * Copyright(c) 2006-2007, Ext JS, LLC.
38159  *
38160  * Originally Released Under LGPL - original licence link has changed is not relivant.
38161  *
38162  * Fork - LGPL
38163  * <script type="text/javascript">
38164  */
38165 /**
38166  * @class Roo.grid.CellSelectionModel
38167  * @extends Roo.grid.AbstractSelectionModel
38168  * This class provides the basic implementation for cell selection in a grid.
38169  * @constructor
38170  * @param {Object} config The object containing the configuration of this model.
38171  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
38172  */
38173 Roo.grid.CellSelectionModel = function(config){
38174     Roo.apply(this, config);
38175
38176     this.selection = null;
38177
38178     this.addEvents({
38179         /**
38180              * @event beforerowselect
38181              * Fires before a cell is selected.
38182              * @param {SelectionModel} this
38183              * @param {Number} rowIndex The selected row index
38184              * @param {Number} colIndex The selected cell index
38185              */
38186             "beforecellselect" : true,
38187         /**
38188              * @event cellselect
38189              * Fires when a cell is selected.
38190              * @param {SelectionModel} this
38191              * @param {Number} rowIndex The selected row index
38192              * @param {Number} colIndex The selected cell index
38193              */
38194             "cellselect" : true,
38195         /**
38196              * @event selectionchange
38197              * Fires when the active selection changes.
38198              * @param {SelectionModel} this
38199              * @param {Object} selection null for no selection or an object (o) with two properties
38200                 <ul>
38201                 <li>o.record: the record object for the row the selection is in</li>
38202                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
38203                 </ul>
38204              */
38205             "selectionchange" : true,
38206         /**
38207              * @event tabend
38208              * Fires when the tab (or enter) was pressed on the last editable cell
38209              * You can use this to trigger add new row.
38210              * @param {SelectionModel} this
38211              */
38212             "tabend" : true,
38213          /**
38214              * @event beforeeditnext
38215              * Fires before the next editable sell is made active
38216              * You can use this to skip to another cell or fire the tabend
38217              *    if you set cell to false
38218              * @param {Object} eventdata object : { cell : [ row, col ] } 
38219              */
38220             "beforeeditnext" : true
38221     });
38222     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
38223 };
38224
38225 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
38226     
38227     enter_is_tab: false,
38228
38229     /** @ignore */
38230     initEvents : function(){
38231         this.grid.on("mousedown", this.handleMouseDown, this);
38232         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
38233         var view = this.grid.view;
38234         view.on("refresh", this.onViewChange, this);
38235         view.on("rowupdated", this.onRowUpdated, this);
38236         view.on("beforerowremoved", this.clearSelections, this);
38237         view.on("beforerowsinserted", this.clearSelections, this);
38238         if(this.grid.isEditor){
38239             this.grid.on("beforeedit", this.beforeEdit,  this);
38240         }
38241     },
38242
38243         //private
38244     beforeEdit : function(e){
38245         this.select(e.row, e.column, false, true, e.record);
38246     },
38247
38248         //private
38249     onRowUpdated : function(v, index, r){
38250         if(this.selection && this.selection.record == r){
38251             v.onCellSelect(index, this.selection.cell[1]);
38252         }
38253     },
38254
38255         //private
38256     onViewChange : function(){
38257         this.clearSelections(true);
38258     },
38259
38260         /**
38261          * Returns the currently selected cell,.
38262          * @return {Array} The selected cell (row, column) or null if none selected.
38263          */
38264     getSelectedCell : function(){
38265         return this.selection ? this.selection.cell : null;
38266     },
38267
38268     /**
38269      * Clears all selections.
38270      * @param {Boolean} true to prevent the gridview from being notified about the change.
38271      */
38272     clearSelections : function(preventNotify){
38273         var s = this.selection;
38274         if(s){
38275             if(preventNotify !== true){
38276                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
38277             }
38278             this.selection = null;
38279             this.fireEvent("selectionchange", this, null);
38280         }
38281     },
38282
38283     /**
38284      * Returns true if there is a selection.
38285      * @return {Boolean}
38286      */
38287     hasSelection : function(){
38288         return this.selection ? true : false;
38289     },
38290
38291     /** @ignore */
38292     handleMouseDown : function(e, t){
38293         var v = this.grid.getView();
38294         if(this.isLocked()){
38295             return;
38296         };
38297         var row = v.findRowIndex(t);
38298         var cell = v.findCellIndex(t);
38299         if(row !== false && cell !== false){
38300             this.select(row, cell);
38301         }
38302     },
38303
38304     /**
38305      * Selects a cell.
38306      * @param {Number} rowIndex
38307      * @param {Number} collIndex
38308      */
38309     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38310         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38311             this.clearSelections();
38312             r = r || this.grid.dataSource.getAt(rowIndex);
38313             this.selection = {
38314                 record : r,
38315                 cell : [rowIndex, colIndex]
38316             };
38317             if(!preventViewNotify){
38318                 var v = this.grid.getView();
38319                 v.onCellSelect(rowIndex, colIndex);
38320                 if(preventFocus !== true){
38321                     v.focusCell(rowIndex, colIndex);
38322                 }
38323             }
38324             this.fireEvent("cellselect", this, rowIndex, colIndex);
38325             this.fireEvent("selectionchange", this, this.selection);
38326         }
38327     },
38328
38329         //private
38330     isSelectable : function(rowIndex, colIndex, cm){
38331         return !cm.isHidden(colIndex);
38332     },
38333
38334     /** @ignore */
38335     handleKeyDown : function(e){
38336         //Roo.log('Cell Sel Model handleKeyDown');
38337         if(!e.isNavKeyPress()){
38338             return;
38339         }
38340         var g = this.grid, s = this.selection;
38341         if(!s){
38342             e.stopEvent();
38343             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38344             if(cell){
38345                 this.select(cell[0], cell[1]);
38346             }
38347             return;
38348         }
38349         var sm = this;
38350         var walk = function(row, col, step){
38351             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38352         };
38353         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38354         var newCell;
38355
38356       
38357
38358         switch(k){
38359             case e.TAB:
38360                 // handled by onEditorKey
38361                 if (g.isEditor && g.editing) {
38362                     return;
38363                 }
38364                 if(e.shiftKey) {
38365                     newCell = walk(r, c-1, -1);
38366                 } else {
38367                     newCell = walk(r, c+1, 1);
38368                 }
38369                 break;
38370             
38371             case e.DOWN:
38372                newCell = walk(r+1, c, 1);
38373                 break;
38374             
38375             case e.UP:
38376                 newCell = walk(r-1, c, -1);
38377                 break;
38378             
38379             case e.RIGHT:
38380                 newCell = walk(r, c+1, 1);
38381                 break;
38382             
38383             case e.LEFT:
38384                 newCell = walk(r, c-1, -1);
38385                 break;
38386             
38387             case e.ENTER:
38388                 
38389                 if(g.isEditor && !g.editing){
38390                    g.startEditing(r, c);
38391                    e.stopEvent();
38392                    return;
38393                 }
38394                 
38395                 
38396              break;
38397         };
38398         if(newCell){
38399             this.select(newCell[0], newCell[1]);
38400             e.stopEvent();
38401             
38402         }
38403     },
38404
38405     acceptsNav : function(row, col, cm){
38406         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38407     },
38408     /**
38409      * Selects a cell.
38410      * @param {Number} field (not used) - as it's normally used as a listener
38411      * @param {Number} e - event - fake it by using
38412      *
38413      * var e = Roo.EventObjectImpl.prototype;
38414      * e.keyCode = e.TAB
38415      *
38416      * 
38417      */
38418     onEditorKey : function(field, e){
38419         
38420         var k = e.getKey(),
38421             newCell,
38422             g = this.grid,
38423             ed = g.activeEditor,
38424             forward = false;
38425         ///Roo.log('onEditorKey' + k);
38426         
38427         
38428         if (this.enter_is_tab && k == e.ENTER) {
38429             k = e.TAB;
38430         }
38431         
38432         if(k == e.TAB){
38433             if(e.shiftKey){
38434                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38435             }else{
38436                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38437                 forward = true;
38438             }
38439             
38440             e.stopEvent();
38441             
38442         } else if(k == e.ENTER &&  !e.ctrlKey){
38443             ed.completeEdit();
38444             e.stopEvent();
38445             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38446         
38447                 } else if(k == e.ESC){
38448             ed.cancelEdit();
38449         }
38450                 
38451         if (newCell) {
38452             var ecall = { cell : newCell, forward : forward };
38453             this.fireEvent('beforeeditnext', ecall );
38454             newCell = ecall.cell;
38455                         forward = ecall.forward;
38456         }
38457                 
38458         if(newCell){
38459             //Roo.log('next cell after edit');
38460             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38461         } else if (forward) {
38462             // tabbed past last
38463             this.fireEvent.defer(100, this, ['tabend',this]);
38464         }
38465     }
38466 });/*
38467  * Based on:
38468  * Ext JS Library 1.1.1
38469  * Copyright(c) 2006-2007, Ext JS, LLC.
38470  *
38471  * Originally Released Under LGPL - original licence link has changed is not relivant.
38472  *
38473  * Fork - LGPL
38474  * <script type="text/javascript">
38475  */
38476  
38477 /**
38478  * @class Roo.grid.EditorGrid
38479  * @extends Roo.grid.Grid
38480  * Class for creating and editable grid.
38481  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38482  * The container MUST have some type of size defined for the grid to fill. The container will be 
38483  * automatically set to position relative if it isn't already.
38484  * @param {Object} dataSource The data model to bind to
38485  * @param {Object} colModel The column model with info about this grid's columns
38486  */
38487 Roo.grid.EditorGrid = function(container, config){
38488     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38489     this.getGridEl().addClass("xedit-grid");
38490
38491     if(!this.selModel){
38492         this.selModel = new Roo.grid.CellSelectionModel();
38493     }
38494
38495     this.activeEditor = null;
38496
38497         this.addEvents({
38498             /**
38499              * @event beforeedit
38500              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38501              * <ul style="padding:5px;padding-left:16px;">
38502              * <li>grid - This grid</li>
38503              * <li>record - The record being edited</li>
38504              * <li>field - The field name being edited</li>
38505              * <li>value - The value for the field being edited.</li>
38506              * <li>row - The grid row index</li>
38507              * <li>column - The grid column index</li>
38508              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38509              * </ul>
38510              * @param {Object} e An edit event (see above for description)
38511              */
38512             "beforeedit" : true,
38513             /**
38514              * @event afteredit
38515              * Fires after a cell is edited. <br />
38516              * <ul style="padding:5px;padding-left:16px;">
38517              * <li>grid - This grid</li>
38518              * <li>record - The record being edited</li>
38519              * <li>field - The field name being edited</li>
38520              * <li>value - The value being set</li>
38521              * <li>originalValue - The original value for the field, before the edit.</li>
38522              * <li>row - The grid row index</li>
38523              * <li>column - The grid column index</li>
38524              * </ul>
38525              * @param {Object} e An edit event (see above for description)
38526              */
38527             "afteredit" : true,
38528             /**
38529              * @event validateedit
38530              * Fires after a cell is edited, but before the value is set in the record. 
38531          * You can use this to modify the value being set in the field, Return false
38532              * to cancel the change. The edit event object has the following properties <br />
38533              * <ul style="padding:5px;padding-left:16px;">
38534          * <li>editor - This editor</li>
38535              * <li>grid - This grid</li>
38536              * <li>record - The record being edited</li>
38537              * <li>field - The field name being edited</li>
38538              * <li>value - The value being set</li>
38539              * <li>originalValue - The original value for the field, before the edit.</li>
38540              * <li>row - The grid row index</li>
38541              * <li>column - The grid column index</li>
38542              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38543              * </ul>
38544              * @param {Object} e An edit event (see above for description)
38545              */
38546             "validateedit" : true
38547         });
38548     this.on("bodyscroll", this.stopEditing,  this);
38549     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38550 };
38551
38552 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38553     /**
38554      * @cfg {Number} clicksToEdit
38555      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38556      */
38557     clicksToEdit: 2,
38558
38559     // private
38560     isEditor : true,
38561     // private
38562     trackMouseOver: false, // causes very odd FF errors
38563
38564     onCellDblClick : function(g, row, col){
38565         this.startEditing(row, col);
38566     },
38567
38568     onEditComplete : function(ed, value, startValue){
38569         this.editing = false;
38570         this.activeEditor = null;
38571         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
38572         var r = ed.record;
38573         var field = this.colModel.getDataIndex(ed.col);
38574         var e = {
38575             grid: this,
38576             record: r,
38577             field: field,
38578             originalValue: startValue,
38579             value: value,
38580             row: ed.row,
38581             column: ed.col,
38582             cancel:false,
38583             editor: ed
38584         };
38585         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
38586         cell.show();
38587           
38588         if(String(value) !== String(startValue)){
38589             
38590             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
38591                 r.set(field, e.value);
38592                 // if we are dealing with a combo box..
38593                 // then we also set the 'name' colum to be the displayField
38594                 if (ed.field.displayField && ed.field.name) {
38595                     r.set(ed.field.name, ed.field.el.dom.value);
38596                 }
38597                 
38598                 delete e.cancel; //?? why!!!
38599                 this.fireEvent("afteredit", e);
38600             }
38601         } else {
38602             this.fireEvent("afteredit", e); // always fire it!
38603         }
38604         this.view.focusCell(ed.row, ed.col);
38605     },
38606
38607     /**
38608      * Starts editing the specified for the specified row/column
38609      * @param {Number} rowIndex
38610      * @param {Number} colIndex
38611      */
38612     startEditing : function(row, col){
38613         this.stopEditing();
38614         if(this.colModel.isCellEditable(col, row)){
38615             this.view.ensureVisible(row, col, true);
38616           
38617             var r = this.dataSource.getAt(row);
38618             var field = this.colModel.getDataIndex(col);
38619             var cell = Roo.get(this.view.getCell(row,col));
38620             var e = {
38621                 grid: this,
38622                 record: r,
38623                 field: field,
38624                 value: r.data[field],
38625                 row: row,
38626                 column: col,
38627                 cancel:false 
38628             };
38629             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
38630                 this.editing = true;
38631                 var ed = this.colModel.getCellEditor(col, row);
38632                 
38633                 if (!ed) {
38634                     return;
38635                 }
38636                 if(!ed.rendered){
38637                     ed.render(ed.parentEl || document.body);
38638                 }
38639                 ed.field.reset();
38640                
38641                 cell.hide();
38642                 
38643                 (function(){ // complex but required for focus issues in safari, ie and opera
38644                     ed.row = row;
38645                     ed.col = col;
38646                     ed.record = r;
38647                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
38648                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
38649                     this.activeEditor = ed;
38650                     var v = r.data[field];
38651                     ed.startEdit(this.view.getCell(row, col), v);
38652                     // combo's with 'displayField and name set
38653                     if (ed.field.displayField && ed.field.name) {
38654                         ed.field.el.dom.value = r.data[ed.field.name];
38655                     }
38656                     
38657                     
38658                 }).defer(50, this);
38659             }
38660         }
38661     },
38662         
38663     /**
38664      * Stops any active editing
38665      */
38666     stopEditing : function(){
38667         if(this.activeEditor){
38668             this.activeEditor.completeEdit();
38669         }
38670         this.activeEditor = null;
38671     },
38672         
38673          /**
38674      * Called to get grid's drag proxy text, by default returns this.ddText.
38675      * @return {String}
38676      */
38677     getDragDropText : function(){
38678         var count = this.selModel.getSelectedCell() ? 1 : 0;
38679         return String.format(this.ddText, count, count == 1 ? '' : 's');
38680     }
38681         
38682 });/*
38683  * Based on:
38684  * Ext JS Library 1.1.1
38685  * Copyright(c) 2006-2007, Ext JS, LLC.
38686  *
38687  * Originally Released Under LGPL - original licence link has changed is not relivant.
38688  *
38689  * Fork - LGPL
38690  * <script type="text/javascript">
38691  */
38692
38693 // private - not really -- you end up using it !
38694 // This is a support class used internally by the Grid components
38695
38696 /**
38697  * @class Roo.grid.GridEditor
38698  * @extends Roo.Editor
38699  * Class for creating and editable grid elements.
38700  * @param {Object} config any settings (must include field)
38701  */
38702 Roo.grid.GridEditor = function(field, config){
38703     if (!config && field.field) {
38704         config = field;
38705         field = Roo.factory(config.field, Roo.form);
38706     }
38707     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
38708     field.monitorTab = false;
38709 };
38710
38711 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
38712     
38713     /**
38714      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
38715      */
38716     
38717     alignment: "tl-tl",
38718     autoSize: "width",
38719     hideEl : false,
38720     cls: "x-small-editor x-grid-editor",
38721     shim:false,
38722     shadow:"frame"
38723 });/*
38724  * Based on:
38725  * Ext JS Library 1.1.1
38726  * Copyright(c) 2006-2007, Ext JS, LLC.
38727  *
38728  * Originally Released Under LGPL - original licence link has changed is not relivant.
38729  *
38730  * Fork - LGPL
38731  * <script type="text/javascript">
38732  */
38733   
38734
38735   
38736 Roo.grid.PropertyRecord = Roo.data.Record.create([
38737     {name:'name',type:'string'},  'value'
38738 ]);
38739
38740
38741 Roo.grid.PropertyStore = function(grid, source){
38742     this.grid = grid;
38743     this.store = new Roo.data.Store({
38744         recordType : Roo.grid.PropertyRecord
38745     });
38746     this.store.on('update', this.onUpdate,  this);
38747     if(source){
38748         this.setSource(source);
38749     }
38750     Roo.grid.PropertyStore.superclass.constructor.call(this);
38751 };
38752
38753
38754
38755 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
38756     setSource : function(o){
38757         this.source = o;
38758         this.store.removeAll();
38759         var data = [];
38760         for(var k in o){
38761             if(this.isEditableValue(o[k])){
38762                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
38763             }
38764         }
38765         this.store.loadRecords({records: data}, {}, true);
38766     },
38767
38768     onUpdate : function(ds, record, type){
38769         if(type == Roo.data.Record.EDIT){
38770             var v = record.data['value'];
38771             var oldValue = record.modified['value'];
38772             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
38773                 this.source[record.id] = v;
38774                 record.commit();
38775                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
38776             }else{
38777                 record.reject();
38778             }
38779         }
38780     },
38781
38782     getProperty : function(row){
38783        return this.store.getAt(row);
38784     },
38785
38786     isEditableValue: function(val){
38787         if(val && val instanceof Date){
38788             return true;
38789         }else if(typeof val == 'object' || typeof val == 'function'){
38790             return false;
38791         }
38792         return true;
38793     },
38794
38795     setValue : function(prop, value){
38796         this.source[prop] = value;
38797         this.store.getById(prop).set('value', value);
38798     },
38799
38800     getSource : function(){
38801         return this.source;
38802     }
38803 });
38804
38805 Roo.grid.PropertyColumnModel = function(grid, store){
38806     this.grid = grid;
38807     var g = Roo.grid;
38808     g.PropertyColumnModel.superclass.constructor.call(this, [
38809         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
38810         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
38811     ]);
38812     this.store = store;
38813     this.bselect = Roo.DomHelper.append(document.body, {
38814         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
38815             {tag: 'option', value: 'true', html: 'true'},
38816             {tag: 'option', value: 'false', html: 'false'}
38817         ]
38818     });
38819     Roo.id(this.bselect);
38820     var f = Roo.form;
38821     this.editors = {
38822         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
38823         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
38824         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
38825         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
38826         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
38827     };
38828     this.renderCellDelegate = this.renderCell.createDelegate(this);
38829     this.renderPropDelegate = this.renderProp.createDelegate(this);
38830 };
38831
38832 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
38833     
38834     
38835     nameText : 'Name',
38836     valueText : 'Value',
38837     
38838     dateFormat : 'm/j/Y',
38839     
38840     
38841     renderDate : function(dateVal){
38842         return dateVal.dateFormat(this.dateFormat);
38843     },
38844
38845     renderBool : function(bVal){
38846         return bVal ? 'true' : 'false';
38847     },
38848
38849     isCellEditable : function(colIndex, rowIndex){
38850         return colIndex == 1;
38851     },
38852
38853     getRenderer : function(col){
38854         return col == 1 ?
38855             this.renderCellDelegate : this.renderPropDelegate;
38856     },
38857
38858     renderProp : function(v){
38859         return this.getPropertyName(v);
38860     },
38861
38862     renderCell : function(val){
38863         var rv = val;
38864         if(val instanceof Date){
38865             rv = this.renderDate(val);
38866         }else if(typeof val == 'boolean'){
38867             rv = this.renderBool(val);
38868         }
38869         return Roo.util.Format.htmlEncode(rv);
38870     },
38871
38872     getPropertyName : function(name){
38873         var pn = this.grid.propertyNames;
38874         return pn && pn[name] ? pn[name] : name;
38875     },
38876
38877     getCellEditor : function(colIndex, rowIndex){
38878         var p = this.store.getProperty(rowIndex);
38879         var n = p.data['name'], val = p.data['value'];
38880         
38881         if(typeof(this.grid.customEditors[n]) == 'string'){
38882             return this.editors[this.grid.customEditors[n]];
38883         }
38884         if(typeof(this.grid.customEditors[n]) != 'undefined'){
38885             return this.grid.customEditors[n];
38886         }
38887         if(val instanceof Date){
38888             return this.editors['date'];
38889         }else if(typeof val == 'number'){
38890             return this.editors['number'];
38891         }else if(typeof val == 'boolean'){
38892             return this.editors['boolean'];
38893         }else{
38894             return this.editors['string'];
38895         }
38896     }
38897 });
38898
38899 /**
38900  * @class Roo.grid.PropertyGrid
38901  * @extends Roo.grid.EditorGrid
38902  * This class represents the  interface of a component based property grid control.
38903  * <br><br>Usage:<pre><code>
38904  var grid = new Roo.grid.PropertyGrid("my-container-id", {
38905       
38906  });
38907  // set any options
38908  grid.render();
38909  * </code></pre>
38910   
38911  * @constructor
38912  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
38913  * The container MUST have some type of size defined for the grid to fill. The container will be
38914  * automatically set to position relative if it isn't already.
38915  * @param {Object} config A config object that sets properties on this grid.
38916  */
38917 Roo.grid.PropertyGrid = function(container, config){
38918     config = config || {};
38919     var store = new Roo.grid.PropertyStore(this);
38920     this.store = store;
38921     var cm = new Roo.grid.PropertyColumnModel(this, store);
38922     store.store.sort('name', 'ASC');
38923     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
38924         ds: store.store,
38925         cm: cm,
38926         enableColLock:false,
38927         enableColumnMove:false,
38928         stripeRows:false,
38929         trackMouseOver: false,
38930         clicksToEdit:1
38931     }, config));
38932     this.getGridEl().addClass('x-props-grid');
38933     this.lastEditRow = null;
38934     this.on('columnresize', this.onColumnResize, this);
38935     this.addEvents({
38936          /**
38937              * @event beforepropertychange
38938              * Fires before a property changes (return false to stop?)
38939              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38940              * @param {String} id Record Id
38941              * @param {String} newval New Value
38942          * @param {String} oldval Old Value
38943              */
38944         "beforepropertychange": true,
38945         /**
38946              * @event propertychange
38947              * Fires after a property changes
38948              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
38949              * @param {String} id Record Id
38950              * @param {String} newval New Value
38951          * @param {String} oldval Old Value
38952              */
38953         "propertychange": true
38954     });
38955     this.customEditors = this.customEditors || {};
38956 };
38957 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
38958     
38959      /**
38960      * @cfg {Object} customEditors map of colnames=> custom editors.
38961      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
38962      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
38963      * false disables editing of the field.
38964          */
38965     
38966       /**
38967      * @cfg {Object} propertyNames map of property Names to their displayed value
38968          */
38969     
38970     render : function(){
38971         Roo.grid.PropertyGrid.superclass.render.call(this);
38972         this.autoSize.defer(100, this);
38973     },
38974
38975     autoSize : function(){
38976         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
38977         if(this.view){
38978             this.view.fitColumns();
38979         }
38980     },
38981
38982     onColumnResize : function(){
38983         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
38984         this.autoSize();
38985     },
38986     /**
38987      * Sets the data for the Grid
38988      * accepts a Key => Value object of all the elements avaiable.
38989      * @param {Object} data  to appear in grid.
38990      */
38991     setSource : function(source){
38992         this.store.setSource(source);
38993         //this.autoSize();
38994     },
38995     /**
38996      * Gets all the data from the grid.
38997      * @return {Object} data  data stored in grid
38998      */
38999     getSource : function(){
39000         return this.store.getSource();
39001     }
39002 });/*
39003  * Based on:
39004  * Ext JS Library 1.1.1
39005  * Copyright(c) 2006-2007, Ext JS, LLC.
39006  *
39007  * Originally Released Under LGPL - original licence link has changed is not relivant.
39008  *
39009  * Fork - LGPL
39010  * <script type="text/javascript">
39011  */
39012  
39013 /**
39014  * @class Roo.LoadMask
39015  * A simple utility class for generically masking elements while loading data.  If the element being masked has
39016  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
39017  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
39018  * element's UpdateManager load indicator and will be destroyed after the initial load.
39019  * @constructor
39020  * Create a new LoadMask
39021  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
39022  * @param {Object} config The config object
39023  */
39024 Roo.LoadMask = function(el, config){
39025     this.el = Roo.get(el);
39026     Roo.apply(this, config);
39027     if(this.store){
39028         this.store.on('beforeload', this.onBeforeLoad, this);
39029         this.store.on('load', this.onLoad, this);
39030         this.store.on('loadexception', this.onLoadException, this);
39031         this.removeMask = false;
39032     }else{
39033         var um = this.el.getUpdateManager();
39034         um.showLoadIndicator = false; // disable the default indicator
39035         um.on('beforeupdate', this.onBeforeLoad, this);
39036         um.on('update', this.onLoad, this);
39037         um.on('failure', this.onLoad, this);
39038         this.removeMask = true;
39039     }
39040 };
39041
39042 Roo.LoadMask.prototype = {
39043     /**
39044      * @cfg {Boolean} removeMask
39045      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
39046      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
39047      */
39048     /**
39049      * @cfg {String} msg
39050      * The text to display in a centered loading message box (defaults to 'Loading...')
39051      */
39052     msg : 'Loading...',
39053     /**
39054      * @cfg {String} msgCls
39055      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
39056      */
39057     msgCls : 'x-mask-loading',
39058
39059     /**
39060      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
39061      * @type Boolean
39062      */
39063     disabled: false,
39064
39065     /**
39066      * Disables the mask to prevent it from being displayed
39067      */
39068     disable : function(){
39069        this.disabled = true;
39070     },
39071
39072     /**
39073      * Enables the mask so that it can be displayed
39074      */
39075     enable : function(){
39076         this.disabled = false;
39077     },
39078     
39079     onLoadException : function()
39080     {
39081         Roo.log(arguments);
39082         
39083         if (typeof(arguments[3]) != 'undefined') {
39084             Roo.MessageBox.alert("Error loading",arguments[3]);
39085         } 
39086         /*
39087         try {
39088             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39089                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39090             }   
39091         } catch(e) {
39092             
39093         }
39094         */
39095     
39096         
39097         
39098         this.el.unmask(this.removeMask);
39099     },
39100     // private
39101     onLoad : function()
39102     {
39103         this.el.unmask(this.removeMask);
39104     },
39105
39106     // private
39107     onBeforeLoad : function(){
39108         if(!this.disabled){
39109             this.el.mask(this.msg, this.msgCls);
39110         }
39111     },
39112
39113     // private
39114     destroy : function(){
39115         if(this.store){
39116             this.store.un('beforeload', this.onBeforeLoad, this);
39117             this.store.un('load', this.onLoad, this);
39118             this.store.un('loadexception', this.onLoadException, this);
39119         }else{
39120             var um = this.el.getUpdateManager();
39121             um.un('beforeupdate', this.onBeforeLoad, this);
39122             um.un('update', this.onLoad, this);
39123             um.un('failure', this.onLoad, this);
39124         }
39125     }
39126 };/*
39127  * Based on:
39128  * Ext JS Library 1.1.1
39129  * Copyright(c) 2006-2007, Ext JS, LLC.
39130  *
39131  * Originally Released Under LGPL - original licence link has changed is not relivant.
39132  *
39133  * Fork - LGPL
39134  * <script type="text/javascript">
39135  */
39136
39137
39138 /**
39139  * @class Roo.XTemplate
39140  * @extends Roo.Template
39141  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
39142 <pre><code>
39143 var t = new Roo.XTemplate(
39144         '&lt;select name="{name}"&gt;',
39145                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
39146         '&lt;/select&gt;'
39147 );
39148  
39149 // then append, applying the master template values
39150  </code></pre>
39151  *
39152  * Supported features:
39153  *
39154  *  Tags:
39155
39156 <pre><code>
39157       {a_variable} - output encoded.
39158       {a_variable.format:("Y-m-d")} - call a method on the variable
39159       {a_variable:raw} - unencoded output
39160       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
39161       {a_variable:this.method_on_template(...)} - call a method on the template object.
39162  
39163 </code></pre>
39164  *  The tpl tag:
39165 <pre><code>
39166         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
39167         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
39168         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
39169         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
39170   
39171         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
39172         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
39173 </code></pre>
39174  *      
39175  */
39176 Roo.XTemplate = function()
39177 {
39178     Roo.XTemplate.superclass.constructor.apply(this, arguments);
39179     if (this.html) {
39180         this.compile();
39181     }
39182 };
39183
39184
39185 Roo.extend(Roo.XTemplate, Roo.Template, {
39186
39187     /**
39188      * The various sub templates
39189      */
39190     tpls : false,
39191     /**
39192      *
39193      * basic tag replacing syntax
39194      * WORD:WORD()
39195      *
39196      * // you can fake an object call by doing this
39197      *  x.t:(test,tesT) 
39198      * 
39199      */
39200     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
39201
39202     /**
39203      * compile the template
39204      *
39205      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
39206      *
39207      */
39208     compile: function()
39209     {
39210         var s = this.html;
39211      
39212         s = ['<tpl>', s, '</tpl>'].join('');
39213     
39214         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
39215             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
39216             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
39217             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
39218             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
39219             m,
39220             id     = 0,
39221             tpls   = [];
39222     
39223         while(true == !!(m = s.match(re))){
39224             var forMatch   = m[0].match(nameRe),
39225                 ifMatch   = m[0].match(ifRe),
39226                 execMatch   = m[0].match(execRe),
39227                 namedMatch   = m[0].match(namedRe),
39228                 
39229                 exp  = null, 
39230                 fn   = null,
39231                 exec = null,
39232                 name = forMatch && forMatch[1] ? forMatch[1] : '';
39233                 
39234             if (ifMatch) {
39235                 // if - puts fn into test..
39236                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
39237                 if(exp){
39238                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
39239                 }
39240             }
39241             
39242             if (execMatch) {
39243                 // exec - calls a function... returns empty if true is  returned.
39244                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
39245                 if(exp){
39246                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
39247                 }
39248             }
39249             
39250             
39251             if (name) {
39252                 // for = 
39253                 switch(name){
39254                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
39255                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
39256                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
39257                 }
39258             }
39259             var uid = namedMatch ? namedMatch[1] : id;
39260             
39261             
39262             tpls.push({
39263                 id:     namedMatch ? namedMatch[1] : id,
39264                 target: name,
39265                 exec:   exec,
39266                 test:   fn,
39267                 body:   m[1] || ''
39268             });
39269             if (namedMatch) {
39270                 s = s.replace(m[0], '');
39271             } else { 
39272                 s = s.replace(m[0], '{xtpl'+ id + '}');
39273             }
39274             ++id;
39275         }
39276         this.tpls = [];
39277         for(var i = tpls.length-1; i >= 0; --i){
39278             this.compileTpl(tpls[i]);
39279             this.tpls[tpls[i].id] = tpls[i];
39280         }
39281         this.master = tpls[tpls.length-1];
39282         return this;
39283     },
39284     /**
39285      * same as applyTemplate, except it's done to one of the subTemplates
39286      * when using named templates, you can do:
39287      *
39288      * var str = pl.applySubTemplate('your-name', values);
39289      *
39290      * 
39291      * @param {Number} id of the template
39292      * @param {Object} values to apply to template
39293      * @param {Object} parent (normaly the instance of this object)
39294      */
39295     applySubTemplate : function(id, values, parent)
39296     {
39297         
39298         
39299         var t = this.tpls[id];
39300         
39301         
39302         try { 
39303             if(t.test && !t.test.call(this, values, parent)){
39304                 return '';
39305             }
39306         } catch(e) {
39307             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
39308             Roo.log(e.toString());
39309             Roo.log(t.test);
39310             return ''
39311         }
39312         try { 
39313             
39314             if(t.exec && t.exec.call(this, values, parent)){
39315                 return '';
39316             }
39317         } catch(e) {
39318             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
39319             Roo.log(e.toString());
39320             Roo.log(t.exec);
39321             return ''
39322         }
39323         try {
39324             var vs = t.target ? t.target.call(this, values, parent) : values;
39325             parent = t.target ? values : parent;
39326             if(t.target && vs instanceof Array){
39327                 var buf = [];
39328                 for(var i = 0, len = vs.length; i < len; i++){
39329                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
39330                 }
39331                 return buf.join('');
39332             }
39333             return t.compiled.call(this, vs, parent);
39334         } catch (e) {
39335             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
39336             Roo.log(e.toString());
39337             Roo.log(t.compiled);
39338             return '';
39339         }
39340     },
39341
39342     compileTpl : function(tpl)
39343     {
39344         var fm = Roo.util.Format;
39345         var useF = this.disableFormats !== true;
39346         var sep = Roo.isGecko ? "+" : ",";
39347         var undef = function(str) {
39348             Roo.log("Property not found :"  + str);
39349             return '';
39350         };
39351         
39352         var fn = function(m, name, format, args)
39353         {
39354             //Roo.log(arguments);
39355             args = args ? args.replace(/\\'/g,"'") : args;
39356             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
39357             if (typeof(format) == 'undefined') {
39358                 format= 'htmlEncode';
39359             }
39360             if (format == 'raw' ) {
39361                 format = false;
39362             }
39363             
39364             if(name.substr(0, 4) == 'xtpl'){
39365                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
39366             }
39367             
39368             // build an array of options to determine if value is undefined..
39369             
39370             // basically get 'xxxx.yyyy' then do
39371             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
39372             //    (function () { Roo.log("Property not found"); return ''; })() :
39373             //    ......
39374             
39375             var udef_ar = [];
39376             var lookfor = '';
39377             Roo.each(name.split('.'), function(st) {
39378                 lookfor += (lookfor.length ? '.': '') + st;
39379                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
39380             });
39381             
39382             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
39383             
39384             
39385             if(format && useF){
39386                 
39387                 args = args ? ',' + args : "";
39388                  
39389                 if(format.substr(0, 5) != "this."){
39390                     format = "fm." + format + '(';
39391                 }else{
39392                     format = 'this.call("'+ format.substr(5) + '", ';
39393                     args = ", values";
39394                 }
39395                 
39396                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
39397             }
39398              
39399             if (args.length) {
39400                 // called with xxyx.yuu:(test,test)
39401                 // change to ()
39402                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
39403             }
39404             // raw.. - :raw modifier..
39405             return "'"+ sep + udef_st  + name + ")"+sep+"'";
39406             
39407         };
39408         var body;
39409         // branched to use + in gecko and [].join() in others
39410         if(Roo.isGecko){
39411             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
39412                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
39413                     "';};};";
39414         }else{
39415             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
39416             body.push(tpl.body.replace(/(\r\n|\n)/g,
39417                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
39418             body.push("'].join('');};};");
39419             body = body.join('');
39420         }
39421         
39422         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
39423        
39424         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
39425         eval(body);
39426         
39427         return this;
39428     },
39429
39430     applyTemplate : function(values){
39431         return this.master.compiled.call(this, values, {});
39432         //var s = this.subs;
39433     },
39434
39435     apply : function(){
39436         return this.applyTemplate.apply(this, arguments);
39437     }
39438
39439  });
39440
39441 Roo.XTemplate.from = function(el){
39442     el = Roo.getDom(el);
39443     return new Roo.XTemplate(el.value || el.innerHTML);
39444 };/*
39445  * Original code for Roojs - LGPL
39446  * <script type="text/javascript">
39447  */
39448  
39449 /**
39450  * @class Roo.XComponent
39451  * A delayed Element creator...
39452  * Or a way to group chunks of interface together.
39453  * 
39454  * Mypart.xyx = new Roo.XComponent({
39455
39456     parent : 'Mypart.xyz', // empty == document.element.!!
39457     order : '001',
39458     name : 'xxxx'
39459     region : 'xxxx'
39460     disabled : function() {} 
39461      
39462     tree : function() { // return an tree of xtype declared components
39463         var MODULE = this;
39464         return 
39465         {
39466             xtype : 'NestedLayoutPanel',
39467             // technicall
39468         }
39469      ]
39470  *})
39471  *
39472  *
39473  * It can be used to build a big heiracy, with parent etc.
39474  * or you can just use this to render a single compoent to a dom element
39475  * MYPART.render(Roo.Element | String(id) | dom_element )
39476  * 
39477  * @extends Roo.util.Observable
39478  * @constructor
39479  * @param cfg {Object} configuration of component
39480  * 
39481  */
39482 Roo.XComponent = function(cfg) {
39483     Roo.apply(this, cfg);
39484     this.addEvents({ 
39485         /**
39486              * @event built
39487              * Fires when this the componnt is built
39488              * @param {Roo.XComponent} c the component
39489              */
39490         'built' : true
39491         
39492     });
39493     this.region = this.region || 'center'; // default..
39494     Roo.XComponent.register(this);
39495     this.modules = false;
39496     this.el = false; // where the layout goes..
39497     
39498     
39499 }
39500 Roo.extend(Roo.XComponent, Roo.util.Observable, {
39501     /**
39502      * @property el
39503      * The created element (with Roo.factory())
39504      * @type {Roo.Layout}
39505      */
39506     el  : false,
39507     
39508     /**
39509      * @property el
39510      * for BC  - use el in new code
39511      * @type {Roo.Layout}
39512      */
39513     panel : false,
39514     
39515     /**
39516      * @property layout
39517      * for BC  - use el in new code
39518      * @type {Roo.Layout}
39519      */
39520     layout : false,
39521     
39522      /**
39523      * @cfg {Function|boolean} disabled
39524      * If this module is disabled by some rule, return true from the funtion
39525      */
39526     disabled : false,
39527     
39528     /**
39529      * @cfg {String} parent 
39530      * Name of parent element which it get xtype added to..
39531      */
39532     parent: false,
39533     
39534     /**
39535      * @cfg {String} order
39536      * Used to set the order in which elements are created (usefull for multiple tabs)
39537      */
39538     
39539     order : false,
39540     /**
39541      * @cfg {String} name
39542      * String to display while loading.
39543      */
39544     name : false,
39545     /**
39546      * @cfg {String} region
39547      * Region to render component to (defaults to center)
39548      */
39549     region : 'center',
39550     
39551     /**
39552      * @cfg {Array} items
39553      * A single item array - the first element is the root of the tree..
39554      * It's done this way to stay compatible with the Xtype system...
39555      */
39556     items : false,
39557     
39558     /**
39559      * @property _tree
39560      * The method that retuns the tree of parts that make up this compoennt 
39561      * @type {function}
39562      */
39563     _tree  : false,
39564     
39565      /**
39566      * render
39567      * render element to dom or tree
39568      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
39569      */
39570     
39571     render : function(el)
39572     {
39573         
39574         el = el || false;
39575         var hp = this.parent ? 1 : 0;
39576         
39577         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
39578             // if parent is a '#.....' string, then let's use that..
39579             var ename = this.parent.substr(1)
39580             this.parent = false;
39581             el = Roo.get(ename);
39582             if (!el) {
39583                 Roo.log("Warning - element can not be found :#" + ename );
39584                 return;
39585             }
39586         }
39587         
39588         
39589         if (!this.parent) {
39590             
39591             el = el ? Roo.get(el) : false;      
39592             
39593             // it's a top level one..
39594             this.parent =  {
39595                 el : new Roo.BorderLayout(el || document.body, {
39596                 
39597                      center: {
39598                          titlebar: false,
39599                          autoScroll:false,
39600                          closeOnTab: true,
39601                          tabPosition: 'top',
39602                           //resizeTabs: true,
39603                          alwaysShowTabs: el && hp? false :  true,
39604                          hideTabs: el || !hp ? true :  false,
39605                          minTabWidth: 140
39606                      }
39607                  })
39608             }
39609         }
39610         
39611                 if (!this.parent.el) {
39612                         // probably an old style ctor, which has been disabled.
39613                         return;
39614                         
39615                 }
39616                 // The 'tree' method is  '_tree now' 
39617             
39618         var tree = this._tree ? this._tree() : this.tree();
39619         tree.region = tree.region || this.region;
39620         this.el = this.parent.el.addxtype(tree);
39621         this.fireEvent('built', this);
39622         
39623         this.panel = this.el;
39624         this.layout = this.panel.layout;
39625                 this.parentLayout = this.parent.layout  || false;  
39626          
39627     }
39628     
39629 });
39630
39631 Roo.apply(Roo.XComponent, {
39632     /**
39633      * @property  hideProgress
39634      * true to disable the building progress bar.. usefull on single page renders.
39635      * @type Boolean
39636      */
39637     hideProgress : false,
39638     /**
39639      * @property  buildCompleted
39640      * True when the builder has completed building the interface.
39641      * @type Boolean
39642      */
39643     buildCompleted : false,
39644      
39645     /**
39646      * @property  topModule
39647      * the upper most module - uses document.element as it's constructor.
39648      * @type Object
39649      */
39650      
39651     topModule  : false,
39652       
39653     /**
39654      * @property  modules
39655      * array of modules to be created by registration system.
39656      * @type {Array} of Roo.XComponent
39657      */
39658     
39659     modules : [],
39660     /**
39661      * @property  elmodules
39662      * array of modules to be created by which use #ID 
39663      * @type {Array} of Roo.XComponent
39664      */
39665      
39666     elmodules : [],
39667
39668     
39669     /**
39670      * Register components to be built later.
39671      *
39672      * This solves the following issues
39673      * - Building is not done on page load, but after an authentication process has occured.
39674      * - Interface elements are registered on page load
39675      * - Parent Interface elements may not be loaded before child, so this handles that..
39676      * 
39677      *
39678      * example:
39679      * 
39680      * MyApp.register({
39681           order : '000001',
39682           module : 'Pman.Tab.projectMgr',
39683           region : 'center',
39684           parent : 'Pman.layout',
39685           disabled : false,  // or use a function..
39686         })
39687      
39688      * * @param {Object} details about module
39689      */
39690     register : function(obj) {
39691                 
39692         Roo.XComponent.event.fireEvent('register', obj);
39693         switch(typeof(obj.disabled) ) {
39694                 
39695             case 'undefined':
39696                 break;
39697             
39698             case 'function':
39699                 if ( obj.disabled() ) {
39700                         return;
39701                 }
39702                 break;
39703             
39704             default:
39705                 if (obj.disabled) {
39706                         return;
39707                 }
39708                 break;
39709         }
39710                 
39711         this.modules.push(obj);
39712          
39713     },
39714     /**
39715      * convert a string to an object..
39716      * eg. 'AAA.BBB' -> finds AAA.BBB
39717
39718      */
39719     
39720     toObject : function(str)
39721     {
39722         if (!str || typeof(str) == 'object') {
39723             return str;
39724         }
39725         if (str.substring(0,1) == '#') {
39726             return str;
39727         }
39728
39729         var ar = str.split('.');
39730         var rt, o;
39731         rt = ar.shift();
39732             /** eval:var:o */
39733         try {
39734             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
39735         } catch (e) {
39736             throw "Module not found : " + str;
39737         }
39738         
39739         if (o === false) {
39740             throw "Module not found : " + str;
39741         }
39742         Roo.each(ar, function(e) {
39743             if (typeof(o[e]) == 'undefined') {
39744                 throw "Module not found : " + str;
39745             }
39746             o = o[e];
39747         });
39748         
39749         return o;
39750         
39751     },
39752     
39753     
39754     /**
39755      * move modules into their correct place in the tree..
39756      * 
39757      */
39758     preBuild : function ()
39759     {
39760         var _t = this;
39761         Roo.each(this.modules , function (obj)
39762         {
39763             Roo.XComponent.event.fireEvent('beforebuild', obj);
39764             
39765             var opar = obj.parent;
39766             try { 
39767                 obj.parent = this.toObject(opar);
39768             } catch(e) {
39769                 Roo.log("parent:toObject failed: " + e.toString());
39770                 return;
39771             }
39772             
39773             if (!obj.parent) {
39774                 Roo.debug && Roo.log("GOT top level module");
39775                 Roo.debug && Roo.log(obj);
39776                 obj.modules = new Roo.util.MixedCollection(false, 
39777                     function(o) { return o.order + '' }
39778                 );
39779                 this.topModule = obj;
39780                 return;
39781             }
39782                         // parent is a string (usually a dom element name..)
39783             if (typeof(obj.parent) == 'string') {
39784                 this.elmodules.push(obj);
39785                 return;
39786             }
39787             if (obj.parent.constructor != Roo.XComponent) {
39788                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
39789             }
39790             if (!obj.parent.modules) {
39791                 obj.parent.modules = new Roo.util.MixedCollection(false, 
39792                     function(o) { return o.order + '' }
39793                 );
39794             }
39795             if (obj.parent.disabled) {
39796                 obj.disabled = true;
39797             }
39798             obj.parent.modules.add(obj);
39799         }, this);
39800     },
39801     
39802      /**
39803      * make a list of modules to build.
39804      * @return {Array} list of modules. 
39805      */ 
39806     
39807     buildOrder : function()
39808     {
39809         var _this = this;
39810         var cmp = function(a,b) {   
39811             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
39812         };
39813         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
39814             throw "No top level modules to build";
39815         }
39816         
39817         // make a flat list in order of modules to build.
39818         var mods = this.topModule ? [ this.topModule ] : [];
39819                 
39820         // elmodules (is a list of DOM based modules )
39821         Roo.each(this.elmodules, function(e) {
39822             mods.push(e)
39823         });
39824
39825         
39826         // add modules to their parents..
39827         var addMod = function(m) {
39828             Roo.debug && Roo.log("build Order: add: " + m.name);
39829             
39830         mods.push(m);
39831         if (m.modules && !m.disabled) {
39832             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
39833             m.modules.keySort('ASC',  cmp );
39834             Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
39835
39836             m.modules.each(addMod);
39837         } else {
39838             Roo.debug && Roo.log("build Order: no child modules");
39839             }
39840             // not sure if this is used any more..
39841             if (m.finalize) {
39842                 m.finalize.name = m.name + " (clean up) ";
39843                 mods.push(m.finalize);
39844             }
39845             
39846         }
39847         if (this.topModule) { 
39848             this.topModule.modules.keySort('ASC',  cmp );
39849             this.topModule.modules.each(addMod);
39850         }
39851         return mods;
39852     },
39853     
39854      /**
39855      * Build the registered modules.
39856      * @param {Object} parent element.
39857      * @param {Function} optional method to call after module has been added.
39858      * 
39859      */ 
39860    
39861     build : function() 
39862     {
39863         
39864         this.preBuild();
39865         var mods = this.buildOrder();
39866       
39867         //this.allmods = mods;
39868         //Roo.debug && Roo.log(mods);
39869         //return;
39870         if (!mods.length) { // should not happen
39871             throw "NO modules!!!";
39872         }
39873         
39874         
39875         var msg = "Building Interface...";
39876         // flash it up as modal - so we store the mask!?
39877         if (!this.hideProgress) {
39878             Roo.MessageBox.show({ title: 'loading' });
39879             Roo.MessageBox.show({
39880                title: "Please wait...",
39881                msg: msg,
39882                width:450,
39883                progress:true,
39884                closable:false,
39885                modal: false
39886               
39887             });
39888         }
39889         var total = mods.length;
39890         
39891         var _this = this;
39892         var progressRun = function() {
39893             if (!mods.length) {
39894                 Roo.debug && Roo.log('hide?');
39895                 if (!this.hideProgress) {
39896                     Roo.MessageBox.hide();
39897                 }
39898                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
39899                 
39900                 // THE END...
39901                 return false;   
39902             }
39903             
39904             var m = mods.shift();
39905             
39906             
39907             Roo.debug && Roo.log(m);
39908             // not sure if this is supported any more.. - modules that are are just function
39909             if (typeof(m) == 'function') { 
39910                 m.call(this);
39911                 return progressRun.defer(10, _this);
39912             } 
39913             
39914             
39915             msg = "Building Interface " + (total  - mods.length) + 
39916                     " of " + total + 
39917                     (m.name ? (' - ' + m.name) : '');
39918                         Roo.debug && Roo.log(msg);
39919             if (!this.hideProgress) { 
39920                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
39921             }
39922             
39923          
39924             // is the module disabled?
39925             var disabled = (typeof(m.disabled) == 'function') ?
39926                 m.disabled.call(m.module.disabled) : m.disabled;    
39927             
39928             
39929             if (disabled) {
39930                 return progressRun(); // we do not update the display!
39931             }
39932             
39933             // now build 
39934             
39935                         
39936                         
39937             m.render();
39938             // it's 10 on top level, and 1 on others??? why...
39939             return progressRun.defer(10, _this);
39940              
39941         }
39942         progressRun.defer(1, _this);
39943      
39944         
39945         
39946     },
39947         
39948         
39949         /**
39950          * Event Object.
39951          *
39952          *
39953          */
39954         event: false, 
39955     /**
39956          * wrapper for event.on - aliased later..  
39957          * Typically use to register a event handler for register:
39958          *
39959          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
39960          *
39961          */
39962     on : false
39963    
39964     
39965     
39966 });
39967
39968 Roo.XComponent.event = new Roo.util.Observable({
39969                 events : { 
39970                         /**
39971                          * @event register
39972                          * Fires when an Component is registered,
39973                          * set the disable property on the Component to stop registration.
39974                          * @param {Roo.XComponent} c the component being registerd.
39975                          * 
39976                          */
39977                         'register' : true,
39978             /**
39979                          * @event beforebuild
39980                          * Fires before each Component is built
39981                          * can be used to apply permissions.
39982                          * @param {Roo.XComponent} c the component being registerd.
39983                          * 
39984                          */
39985                         'beforebuild' : true,
39986                         /**
39987                          * @event buildcomplete
39988                          * Fires on the top level element when all elements have been built
39989                          * @param {Roo.XComponent} the top level component.
39990                          */
39991                         'buildcomplete' : true
39992                         
39993                 }
39994 });
39995
39996 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
39997  //<script type="text/javascript">
39998
39999
40000 /**
40001  * @class Roo.Login
40002  * @extends Roo.LayoutDialog
40003  * A generic Login Dialog..... - only one needed in theory!?!?
40004  *
40005  * Fires XComponent builder on success...
40006  * 
40007  * Sends 
40008  *    username,password, lang = for login actions.
40009  *    check = 1 for periodic checking that sesion is valid.
40010  *    passwordRequest = email request password
40011  *    logout = 1 = to logout
40012  * 
40013  * Affects: (this id="????" elements)
40014  *   loading  (removed) (used to indicate application is loading)
40015  *   loading-mask (hides) (used to hide application when it's building loading)
40016  *   
40017  * 
40018  * Usage: 
40019  *    
40020  * 
40021  * Myapp.login = Roo.Login({
40022      url: xxxx,
40023    
40024      realm : 'Myapp', 
40025      
40026      
40027      method : 'POST',
40028      
40029      
40030      * 
40031  })
40032  * 
40033  * 
40034  * 
40035  **/
40036  
40037 Roo.Login = function(cfg)
40038 {
40039     this.addEvents({
40040         'refreshed' : true
40041     });
40042     
40043     Roo.apply(this,cfg);
40044     
40045     Roo.onReady(function() {
40046         this.onLoad();
40047     }, this);
40048     // call parent..
40049     
40050    
40051     Roo.Login.superclass.constructor.call(this, this);
40052     //this.addxtype(this.items[0]);
40053     
40054     
40055 }
40056
40057
40058 Roo.extend(Roo.Login, Roo.LayoutDialog, {
40059     
40060     /**
40061      * @cfg {String} method
40062      * Method used to query for login details.
40063      */
40064     
40065     method : 'POST',
40066     /**
40067      * @cfg {String} url
40068      * URL to query login data. - eg. baseURL + '/Login.php'
40069      */
40070     url : '',
40071     
40072     /**
40073      * @property user
40074      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
40075      * @type {Object} 
40076      */
40077     user : false,
40078     /**
40079      * @property checkFails
40080      * Number of times we have attempted to get authentication check, and failed.
40081      * @type {Number} 
40082      */
40083     checkFails : 0,
40084       /**
40085      * @property intervalID
40086      * The window interval that does the constant login checking.
40087      * @type {Number} 
40088      */
40089     intervalID : 0,
40090     
40091     
40092     onLoad : function() // called on page load...
40093     {
40094         // load 
40095          
40096         if (Roo.get('loading')) { // clear any loading indicator..
40097             Roo.get('loading').remove();
40098         }
40099         
40100         //this.switchLang('en'); // set the language to english..
40101        
40102         this.check({
40103             success:  function(response, opts)  {  // check successfull...
40104             
40105                 var res = this.processResponse(response);
40106                 this.checkFails =0;
40107                 if (!res.success) { // error!
40108                     this.checkFails = 5;
40109                     //console.log('call failure');
40110                     return this.failure(response,opts);
40111                 }
40112                 
40113                 if (!res.data.id) { // id=0 == login failure.
40114                     return this.show();
40115                 }
40116                 
40117                               
40118                         //console.log(success);
40119                 this.fillAuth(res.data);   
40120                 this.checkFails =0;
40121                 Roo.XComponent.build();
40122             },
40123             failure : this.show
40124         });
40125         
40126     }, 
40127     
40128     
40129     check: function(cfg) // called every so often to refresh cookie etc..
40130     {
40131         if (cfg.again) { // could be undefined..
40132             this.checkFails++;
40133         } else {
40134             this.checkFails = 0;
40135         }
40136         var _this = this;
40137         if (this.sending) {
40138             if ( this.checkFails > 4) {
40139                 Roo.MessageBox.alert("Error",  
40140                     "Error getting authentication status. - try reloading, or wait a while", function() {
40141                         _this.sending = false;
40142                     }); 
40143                 return;
40144             }
40145             cfg.again = true;
40146             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
40147             return;
40148         }
40149         this.sending = true;
40150         
40151         Roo.Ajax.request({  
40152             url: this.url,
40153             params: {
40154                 getAuthUser: true
40155             },  
40156             method: this.method,
40157             success:  cfg.success || this.success,
40158             failure : cfg.failure || this.failure,
40159             scope : this,
40160             callCfg : cfg
40161               
40162         });  
40163     }, 
40164     
40165     
40166     logout: function()
40167     {
40168         window.onbeforeunload = function() { }; // false does not work for IE..
40169         this.user = false;
40170         var _this = this;
40171         
40172         Roo.Ajax.request({  
40173             url: this.url,
40174             params: {
40175                 logout: 1
40176             },  
40177             method: 'GET',
40178             failure : function() {
40179                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
40180                     document.location = document.location.toString() + '?ts=' + Math.random();
40181                 });
40182                 
40183             },
40184             success : function() {
40185                 _this.user = false;
40186                 this.checkFails =0;
40187                 // fixme..
40188                 document.location = document.location.toString() + '?ts=' + Math.random();
40189             }
40190               
40191               
40192         }); 
40193     },
40194     
40195     processResponse : function (response)
40196     {
40197         var res = '';
40198         try {
40199             res = Roo.decode(response.responseText);
40200             // oops...
40201             if (typeof(res) != 'object') {
40202                 res = { success : false, errorMsg : res, errors : true };
40203             }
40204             if (typeof(res.success) == 'undefined') {
40205                 res.success = false;
40206             }
40207             
40208         } catch(e) {
40209             res = { success : false,  errorMsg : response.responseText, errors : true };
40210         }
40211         return res;
40212     },
40213     
40214     success : function(response, opts)  // check successfull...
40215     {  
40216         this.sending = false;
40217         var res = this.processResponse(response);
40218         if (!res.success) {
40219             return this.failure(response, opts);
40220         }
40221         if (!res.data || !res.data.id) {
40222             return this.failure(response,opts);
40223         }
40224         //console.log(res);
40225         this.fillAuth(res.data);
40226         
40227         this.checkFails =0;
40228         
40229     },
40230     
40231     
40232     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
40233     {
40234         this.authUser = -1;
40235         this.sending = false;
40236         var res = this.processResponse(response);
40237         //console.log(res);
40238         if ( this.checkFails > 2) {
40239         
40240             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
40241                 "Error getting authentication status. - try reloading"); 
40242             return;
40243         }
40244         opts.callCfg.again = true;
40245         this.check.defer(1000, this, [ opts.callCfg ]);
40246         return;  
40247     },
40248     
40249     
40250     
40251     fillAuth: function(au) {
40252         this.startAuthCheck();
40253         this.authUserId = au.id;
40254         this.authUser = au;
40255         this.lastChecked = new Date();
40256         this.fireEvent('refreshed', au);
40257         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
40258         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
40259         au.lang = au.lang || 'en';
40260         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
40261         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
40262         this.switchLang(au.lang );
40263         
40264      
40265         // open system... - -on setyp..
40266         if (this.authUserId  < 0) {
40267             Roo.MessageBox.alert("Warning", 
40268                 "This is an open system - please set up a admin user with a password.");  
40269         }
40270          
40271         //Pman.onload(); // which should do nothing if it's a re-auth result...
40272         
40273              
40274     },
40275     
40276     startAuthCheck : function() // starter for timeout checking..
40277     {
40278         if (this.intervalID) { // timer already in place...
40279             return false;
40280         }
40281         var _this = this;
40282         this.intervalID =  window.setInterval(function() {
40283               _this.check(false);
40284             }, 120000); // every 120 secs = 2mins..
40285         
40286         
40287     },
40288          
40289     
40290     switchLang : function (lang) 
40291     {
40292         _T = typeof(_T) == 'undefined' ? false : _T;
40293           if (!_T || !lang.length) {
40294             return;
40295         }
40296         
40297         if (!_T && lang != 'en') {
40298             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
40299             return;
40300         }
40301         
40302         if (typeof(_T.en) == 'undefined') {
40303             _T.en = {};
40304             Roo.apply(_T.en, _T);
40305         }
40306         
40307         if (typeof(_T[lang]) == 'undefined') {
40308             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
40309             return;
40310         }
40311         
40312         
40313         Roo.apply(_T, _T[lang]);
40314         // just need to set the text values for everything...
40315         var _this = this;
40316         /* this will not work ...
40317         if (this.form) { 
40318             
40319                
40320             function formLabel(name, val) {
40321                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
40322             }
40323             
40324             formLabel('password', "Password"+':');
40325             formLabel('username', "Email Address"+':');
40326             formLabel('lang', "Language"+':');
40327             this.dialog.setTitle("Login");
40328             this.dialog.buttons[0].setText("Forgot Password");
40329             this.dialog.buttons[1].setText("Login");
40330         }
40331         */
40332         
40333         
40334     },
40335     
40336     
40337     title: "Login",
40338     modal: true,
40339     width:  350,
40340     //height: 230,
40341     height: 180,
40342     shadow: true,
40343     minWidth:200,
40344     minHeight:180,
40345     //proxyDrag: true,
40346     closable: false,
40347     draggable: false,
40348     collapsible: false,
40349     resizable: false,
40350     center: {  // needed??
40351         autoScroll:false,
40352         titlebar: false,
40353        // tabPosition: 'top',
40354         hideTabs: true,
40355         closeOnTab: true,
40356         alwaysShowTabs: false
40357     } ,
40358     listeners : {
40359         
40360         show  : function(dlg)
40361         {
40362             //console.log(this);
40363             this.form = this.layout.getRegion('center').activePanel.form;
40364             this.form.dialog = dlg;
40365             this.buttons[0].form = this.form;
40366             this.buttons[0].dialog = dlg;
40367             this.buttons[1].form = this.form;
40368             this.buttons[1].dialog = dlg;
40369            
40370            //this.resizeToLogo.defer(1000,this);
40371             // this is all related to resizing for logos..
40372             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
40373            //// if (!sz) {
40374              //   this.resizeToLogo.defer(1000,this);
40375              //   return;
40376            // }
40377             //var w = Ext.lib.Dom.getViewWidth() - 100;
40378             //var h = Ext.lib.Dom.getViewHeight() - 100;
40379             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
40380             //this.center();
40381             if (this.disabled) {
40382                 this.hide();
40383                 return;
40384             }
40385             
40386             if (this.user.id < 0) { // used for inital setup situations.
40387                 return;
40388             }
40389             
40390             if (this.intervalID) {
40391                 // remove the timer
40392                 window.clearInterval(this.intervalID);
40393                 this.intervalID = false;
40394             }
40395             
40396             
40397             if (Roo.get('loading')) {
40398                 Roo.get('loading').remove();
40399             }
40400             if (Roo.get('loading-mask')) {
40401                 Roo.get('loading-mask').hide();
40402             }
40403             
40404             //incomming._node = tnode;
40405             this.form.reset();
40406             //this.dialog.modal = !modal;
40407             //this.dialog.show();
40408             this.el.unmask(); 
40409             
40410             
40411             this.form.setValues({
40412                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
40413                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
40414             });
40415             
40416             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
40417             if (this.form.findField('username').getValue().length > 0 ){
40418                 this.form.findField('password').focus();
40419             } else {
40420                this.form.findField('username').focus();
40421             }
40422     
40423         }
40424     },
40425     items : [
40426          {
40427        
40428             xtype : 'ContentPanel',
40429             xns : Roo,
40430             region: 'center',
40431             fitToFrame : true,
40432             
40433             items : [
40434     
40435                 {
40436                
40437                     xtype : 'Form',
40438                     xns : Roo.form,
40439                     labelWidth: 100,
40440                     style : 'margin: 10px;',
40441                     
40442                     listeners : {
40443                         actionfailed : function(f, act) {
40444                             // form can return { errors: .... }
40445                                 
40446                             //act.result.errors // invalid form element list...
40447                             //act.result.errorMsg// invalid form element list...
40448                             
40449                             this.dialog.el.unmask();
40450                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
40451                                         "Login failed - communication error - try again.");
40452                                       
40453                         },
40454                         actioncomplete: function(re, act) {
40455                              
40456                             Roo.state.Manager.set(
40457                                 this.dialog.realm + '.username',  
40458                                     this.findField('username').getValue()
40459                             );
40460                             Roo.state.Manager.set(
40461                                 this.dialog.realm + '.lang',  
40462                                 this.findField('lang').getValue() 
40463                             );
40464                             
40465                             this.dialog.fillAuth(act.result.data);
40466                               
40467                             this.dialog.hide();
40468                             
40469                             if (Roo.get('loading-mask')) {
40470                                 Roo.get('loading-mask').show();
40471                             }
40472                             Roo.XComponent.build();
40473                             
40474                              
40475                             
40476                         }
40477                     },
40478                     items : [
40479                         {
40480                             xtype : 'TextField',
40481                             xns : Roo.form,
40482                             fieldLabel: "Email Address",
40483                             name: 'username',
40484                             width:200,
40485                             autoCreate : {tag: "input", type: "text", size: "20"}
40486                         },
40487                         {
40488                             xtype : 'TextField',
40489                             xns : Roo.form,
40490                             fieldLabel: "Password",
40491                             inputType: 'password',
40492                             name: 'password',
40493                             width:200,
40494                             autoCreate : {tag: "input", type: "text", size: "20"},
40495                             listeners : {
40496                                 specialkey : function(e,ev) {
40497                                     if (ev.keyCode == 13) {
40498                                         this.form.dialog.el.mask("Logging in");
40499                                         this.form.doAction('submit', {
40500                                             url: this.form.dialog.url,
40501                                             method: this.form.dialog.method
40502                                         });
40503                                     }
40504                                 }
40505                             }  
40506                         },
40507                         {
40508                             xtype : 'ComboBox',
40509                             xns : Roo.form,
40510                             fieldLabel: "Language",
40511                             name : 'langdisp',
40512                             store: {
40513                                 xtype : 'SimpleStore',
40514                                 fields: ['lang', 'ldisp'],
40515                                 data : [
40516                                     [ 'en', 'English' ],
40517                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
40518                                     [ 'zh_CN', '\u7C21\u4E2D' ]
40519                                 ]
40520                             },
40521                             
40522                             valueField : 'lang',
40523                             hiddenName:  'lang',
40524                             width: 200,
40525                             displayField:'ldisp',
40526                             typeAhead: false,
40527                             editable: false,
40528                             mode: 'local',
40529                             triggerAction: 'all',
40530                             emptyText:'Select a Language...',
40531                             selectOnFocus:true,
40532                             listeners : {
40533                                 select :  function(cb, rec, ix) {
40534                                     this.form.switchLang(rec.data.lang);
40535                                 }
40536                             }
40537                         
40538                         }
40539                     ]
40540                 }
40541                   
40542                 
40543             ]
40544         }
40545     ],
40546     buttons : [
40547         {
40548             xtype : 'Button',
40549             xns : 'Roo',
40550             text : "Forgot Password",
40551             listeners : {
40552                 click : function() {
40553                     //console.log(this);
40554                     var n = this.form.findField('username').getValue();
40555                     if (!n.length) {
40556                         Roo.MessageBox.alert("Error", "Fill in your email address");
40557                         return;
40558                     }
40559                     Roo.Ajax.request({
40560                         url: this.dialog.url,
40561                         params: {
40562                             passwordRequest: n
40563                         },
40564                         method: this.dialog.method,
40565                         success:  function(response, opts)  {  // check successfull...
40566                         
40567                             var res = this.dialog.processResponse(response);
40568                             if (!res.success) { // error!
40569                                Roo.MessageBox.alert("Error" ,
40570                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
40571                                return;
40572                             }
40573                             Roo.MessageBox.alert("Notice" ,
40574                                 "Please check you email for the Password Reset message");
40575                         },
40576                         failure : function() {
40577                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
40578                         }
40579                         
40580                     });
40581                 }
40582             }
40583         },
40584         {
40585             xtype : 'Button',
40586             xns : 'Roo',
40587             text : "Login",
40588             listeners : {
40589                 
40590                 click : function () {
40591                         
40592                     this.dialog.el.mask("Logging in");
40593                     this.form.doAction('submit', {
40594                             url: this.dialog.url,
40595                             method: this.dialog.method
40596                     });
40597                 }
40598             }
40599         }
40600     ]
40601   
40602   
40603 })
40604  
40605
40606
40607