Roo/Resizable.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     this.wrapEl  = this.el.wrap().wrap();
9174     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9175     
9176     
9177     if(typeof(this.tpl) == "string"){
9178         this.tpl = new Roo.Template(this.tpl);
9179     } else {
9180         // support xtype ctors..
9181         this.tpl = new Roo.factory(this.tpl, Roo);
9182     }
9183     
9184     
9185     this.tpl.compile();
9186    
9187   
9188     
9189      
9190     /** @private */
9191     this.addEvents({
9192         /**
9193          * @event beforeclick
9194          * Fires before a click is processed. Returns false to cancel the default action.
9195          * @param {Roo.View} this
9196          * @param {Number} index The index of the target node
9197          * @param {HTMLElement} node The target node
9198          * @param {Roo.EventObject} e The raw event object
9199          */
9200             "beforeclick" : true,
9201         /**
9202          * @event click
9203          * Fires when a template node is clicked.
9204          * @param {Roo.View} this
9205          * @param {Number} index The index of the target node
9206          * @param {HTMLElement} node The target node
9207          * @param {Roo.EventObject} e The raw event object
9208          */
9209             "click" : true,
9210         /**
9211          * @event dblclick
9212          * Fires when a template node is double clicked.
9213          * @param {Roo.View} this
9214          * @param {Number} index The index of the target node
9215          * @param {HTMLElement} node The target node
9216          * @param {Roo.EventObject} e The raw event object
9217          */
9218             "dblclick" : true,
9219         /**
9220          * @event contextmenu
9221          * Fires when a template node is right clicked.
9222          * @param {Roo.View} this
9223          * @param {Number} index The index of the target node
9224          * @param {HTMLElement} node The target node
9225          * @param {Roo.EventObject} e The raw event object
9226          */
9227             "contextmenu" : true,
9228         /**
9229          * @event selectionchange
9230          * Fires when the selected nodes change.
9231          * @param {Roo.View} this
9232          * @param {Array} selections Array of the selected nodes
9233          */
9234             "selectionchange" : true,
9235     
9236         /**
9237          * @event beforeselect
9238          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9239          * @param {Roo.View} this
9240          * @param {HTMLElement} node The node to be selected
9241          * @param {Array} selections Array of currently selected nodes
9242          */
9243             "beforeselect" : true,
9244         /**
9245          * @event preparedata
9246          * Fires on every row to render, to allow you to change the data.
9247          * @param {Roo.View} this
9248          * @param {Object} data to be rendered (change this)
9249          */
9250           "preparedata" : true
9251           
9252           
9253         });
9254
9255
9256
9257     this.el.on({
9258         "click": this.onClick,
9259         "dblclick": this.onDblClick,
9260         "contextmenu": this.onContextMenu,
9261         scope:this
9262     });
9263
9264     this.selections = [];
9265     this.nodes = [];
9266     this.cmp = new Roo.CompositeElementLite([]);
9267     if(this.store){
9268         this.store = Roo.factory(this.store, Roo.data);
9269         this.setStore(this.store, true);
9270     }
9271     
9272     if ( this.footer && this.footer.xtype) {
9273            
9274          var fctr = this.wrapEl.appendChild(document.createElement("div"));
9275         
9276         this.footer.dataSource = this.store
9277         this.footer.container = fctr;
9278         this.footer = Roo.factory(this.footer, Roo);
9279         fctr.insertFirst(this.el);
9280         
9281         // this is a bit insane - as the paging toolbar seems to detach the el..
9282 //        dom.parentNode.parentNode.parentNode
9283          // they get detached?
9284     }
9285     
9286     
9287     Roo.View.superclass.constructor.call(this);
9288     
9289     
9290 };
9291
9292 Roo.extend(Roo.View, Roo.util.Observable, {
9293     
9294      /**
9295      * @cfg {Roo.data.Store} store Data store to load data from.
9296      */
9297     store : false,
9298     
9299     /**
9300      * @cfg {String|Roo.Element} el The container element.
9301      */
9302     el : '',
9303     
9304     /**
9305      * @cfg {String|Roo.Template} tpl The template used by this View 
9306      */
9307     tpl : false,
9308     /**
9309      * @cfg {String} dataName the named area of the template to use as the data area
9310      *                          Works with domtemplates roo-name="name"
9311      */
9312     dataName: false,
9313     /**
9314      * @cfg {String} selectedClass The css class to add to selected nodes
9315      */
9316     selectedClass : "x-view-selected",
9317      /**
9318      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9319      */
9320     emptyText : "",
9321     
9322     /**
9323      * @cfg {String} text to display on mask (default Loading)
9324      */
9325     mask : false,
9326     /**
9327      * @cfg {Boolean} multiSelect Allow multiple selection
9328      */
9329     multiSelect : false,
9330     /**
9331      * @cfg {Boolean} singleSelect Allow single selection
9332      */
9333     singleSelect:  false,
9334     
9335     /**
9336      * @cfg {Boolean} toggleSelect - selecting 
9337      */
9338     toggleSelect : false,
9339     
9340     /**
9341      * Returns the element this view is bound to.
9342      * @return {Roo.Element}
9343      */
9344     getEl : function(){
9345         return this.wrapEl;
9346     },
9347     
9348     
9349
9350     /**
9351      * Refreshes the view. - called by datachanged on the store. - do not call directly.
9352      */
9353     refresh : function(){
9354         var t = this.tpl;
9355         
9356         // if we are using something like 'domtemplate', then
9357         // the what gets used is:
9358         // t.applySubtemplate(NAME, data, wrapping data..)
9359         // the outer template then get' applied with
9360         //     the store 'extra data'
9361         // and the body get's added to the
9362         //      roo-name="data" node?
9363         //      <span class='roo-tpl-{name}'></span> ?????
9364         
9365         
9366         
9367         this.clearSelections();
9368         this.el.update("");
9369         var html = [];
9370         var records = this.store.getRange();
9371         if(records.length < 1) {
9372             
9373             // is this valid??  = should it render a template??
9374             
9375             this.el.update(this.emptyText);
9376             return;
9377         }
9378         var el = this.el;
9379         if (this.dataName) {
9380             this.el.update(t.apply(this.store.meta)); //????
9381             el = this.el.child('.roo-tpl-' + this.dataName);
9382         }
9383         
9384         for(var i = 0, len = records.length; i < len; i++){
9385             var data = this.prepareData(records[i].data, i, records[i]);
9386             this.fireEvent("preparedata", this, data, i, records[i]);
9387             html[html.length] = Roo.util.Format.trim(
9388                 this.dataName ?
9389                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9390                     t.apply(data)
9391             );
9392         }
9393         
9394         
9395         
9396         el.update(html.join(""));
9397         this.nodes = el.dom.childNodes;
9398         this.updateIndexes(0);
9399     },
9400
9401     /**
9402      * Function to override to reformat the data that is sent to
9403      * the template for each node.
9404      * DEPRICATED - use the preparedata event handler.
9405      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9406      * a JSON object for an UpdateManager bound view).
9407      */
9408     prepareData : function(data, index, record)
9409     {
9410         this.fireEvent("preparedata", this, data, index, record);
9411         return data;
9412     },
9413
9414     onUpdate : function(ds, record){
9415         this.clearSelections();
9416         var index = this.store.indexOf(record);
9417         var n = this.nodes[index];
9418         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9419         n.parentNode.removeChild(n);
9420         this.updateIndexes(index, index);
9421     },
9422
9423     
9424     
9425 // --------- FIXME     
9426     onAdd : function(ds, records, index)
9427     {
9428         this.clearSelections();
9429         if(this.nodes.length == 0){
9430             this.refresh();
9431             return;
9432         }
9433         var n = this.nodes[index];
9434         for(var i = 0, len = records.length; i < len; i++){
9435             var d = this.prepareData(records[i].data, i, records[i]);
9436             if(n){
9437                 this.tpl.insertBefore(n, d);
9438             }else{
9439                 
9440                 this.tpl.append(this.el, d);
9441             }
9442         }
9443         this.updateIndexes(index);
9444     },
9445
9446     onRemove : function(ds, record, index){
9447         this.clearSelections();
9448         var el = this.dataName  ?
9449             this.el.child('.roo-tpl-' + this.dataName) :
9450             this.el; 
9451         el.dom.removeChild(this.nodes[index]);
9452         this.updateIndexes(index);
9453     },
9454
9455     /**
9456      * Refresh an individual node.
9457      * @param {Number} index
9458      */
9459     refreshNode : function(index){
9460         this.onUpdate(this.store, this.store.getAt(index));
9461     },
9462
9463     updateIndexes : function(startIndex, endIndex){
9464         var ns = this.nodes;
9465         startIndex = startIndex || 0;
9466         endIndex = endIndex || ns.length - 1;
9467         for(var i = startIndex; i <= endIndex; i++){
9468             ns[i].nodeIndex = i;
9469         }
9470     },
9471
9472     /**
9473      * Changes the data store this view uses and refresh the view.
9474      * @param {Store} store
9475      */
9476     setStore : function(store, initial){
9477         if(!initial && this.store){
9478             this.store.un("datachanged", this.refresh);
9479             this.store.un("add", this.onAdd);
9480             this.store.un("remove", this.onRemove);
9481             this.store.un("update", this.onUpdate);
9482             this.store.un("clear", this.refresh);
9483             this.store.un("beforeload", this.onBeforeLoad);
9484             this.store.un("load", this.onLoad);
9485             this.store.un("loadexception", this.onLoad);
9486         }
9487         if(store){
9488           
9489             store.on("datachanged", this.refresh, this);
9490             store.on("add", this.onAdd, this);
9491             store.on("remove", this.onRemove, this);
9492             store.on("update", this.onUpdate, this);
9493             store.on("clear", this.refresh, this);
9494             store.on("beforeload", this.onBeforeLoad, this);
9495             store.on("load", this.onLoad, this);
9496             store.on("loadexception", this.onLoad, this);
9497         }
9498         
9499         if(store){
9500             this.refresh();
9501         }
9502     },
9503     /**
9504      * onbeforeLoad - masks the loading area.
9505      *
9506      */
9507     onBeforeLoad : function()
9508     {
9509         this.el.update("");
9510         this.el.mask(this.mask ? this.mask : "Loading" ); 
9511     },
9512     onLoad : function ()
9513     {
9514         this.el.unmask();
9515     },
9516     
9517
9518     /**
9519      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9520      * @param {HTMLElement} node
9521      * @return {HTMLElement} The template node
9522      */
9523     findItemFromChild : function(node){
9524         var el = this.dataName  ?
9525             this.el.child('.roo-tpl-' + this.dataName,true) :
9526             this.el.dom; 
9527         
9528         if(!node || node.parentNode == el){
9529                     return node;
9530             }
9531             var p = node.parentNode;
9532             while(p && p != el){
9533             if(p.parentNode == el){
9534                 return p;
9535             }
9536             p = p.parentNode;
9537         }
9538             return null;
9539     },
9540
9541     /** @ignore */
9542     onClick : function(e){
9543         var item = this.findItemFromChild(e.getTarget());
9544         if(item){
9545             var index = this.indexOf(item);
9546             if(this.onItemClick(item, index, e) !== false){
9547                 this.fireEvent("click", this, index, item, e);
9548             }
9549         }else{
9550             this.clearSelections();
9551         }
9552     },
9553
9554     /** @ignore */
9555     onContextMenu : function(e){
9556         var item = this.findItemFromChild(e.getTarget());
9557         if(item){
9558             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9559         }
9560     },
9561
9562     /** @ignore */
9563     onDblClick : function(e){
9564         var item = this.findItemFromChild(e.getTarget());
9565         if(item){
9566             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9567         }
9568     },
9569
9570     onItemClick : function(item, index, e)
9571     {
9572         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9573             return false;
9574         }
9575         if (this.toggleSelect) {
9576             var m = this.isSelected(item) ? 'unselect' : 'select';
9577             Roo.log(m);
9578             var _t = this;
9579             _t[m](item, true, false);
9580             return true;
9581         }
9582         if(this.multiSelect || this.singleSelect){
9583             if(this.multiSelect && e.shiftKey && this.lastSelection){
9584                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9585             }else{
9586                 this.select(item, this.multiSelect && e.ctrlKey);
9587                 this.lastSelection = item;
9588             }
9589             e.preventDefault();
9590         }
9591         return true;
9592     },
9593
9594     /**
9595      * Get the number of selected nodes.
9596      * @return {Number}
9597      */
9598     getSelectionCount : function(){
9599         return this.selections.length;
9600     },
9601
9602     /**
9603      * Get the currently selected nodes.
9604      * @return {Array} An array of HTMLElements
9605      */
9606     getSelectedNodes : function(){
9607         return this.selections;
9608     },
9609
9610     /**
9611      * Get the indexes of the selected nodes.
9612      * @return {Array}
9613      */
9614     getSelectedIndexes : function(){
9615         var indexes = [], s = this.selections;
9616         for(var i = 0, len = s.length; i < len; i++){
9617             indexes.push(s[i].nodeIndex);
9618         }
9619         return indexes;
9620     },
9621
9622     /**
9623      * Clear all selections
9624      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9625      */
9626     clearSelections : function(suppressEvent){
9627         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9628             this.cmp.elements = this.selections;
9629             this.cmp.removeClass(this.selectedClass);
9630             this.selections = [];
9631             if(!suppressEvent){
9632                 this.fireEvent("selectionchange", this, this.selections);
9633             }
9634         }
9635     },
9636
9637     /**
9638      * Returns true if the passed node is selected
9639      * @param {HTMLElement/Number} node The node or node index
9640      * @return {Boolean}
9641      */
9642     isSelected : function(node){
9643         var s = this.selections;
9644         if(s.length < 1){
9645             return false;
9646         }
9647         node = this.getNode(node);
9648         return s.indexOf(node) !== -1;
9649     },
9650
9651     /**
9652      * Selects nodes.
9653      * @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
9654      * @param {Boolean} keepExisting (optional) true to keep existing selections
9655      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9656      */
9657     select : function(nodeInfo, keepExisting, suppressEvent){
9658         if(nodeInfo instanceof Array){
9659             if(!keepExisting){
9660                 this.clearSelections(true);
9661             }
9662             for(var i = 0, len = nodeInfo.length; i < len; i++){
9663                 this.select(nodeInfo[i], true, true);
9664             }
9665             return;
9666         } 
9667         var node = this.getNode(nodeInfo);
9668         if(!node || this.isSelected(node)){
9669             return; // already selected.
9670         }
9671         if(!keepExisting){
9672             this.clearSelections(true);
9673         }
9674         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9675             Roo.fly(node).addClass(this.selectedClass);
9676             this.selections.push(node);
9677             if(!suppressEvent){
9678                 this.fireEvent("selectionchange", this, this.selections);
9679             }
9680         }
9681         
9682         
9683     },
9684       /**
9685      * Unselects nodes.
9686      * @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
9687      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9688      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9689      */
9690     unselect : function(nodeInfo, keepExisting, suppressEvent)
9691     {
9692         if(nodeInfo instanceof Array){
9693             Roo.each(this.selections, function(s) {
9694                 this.unselect(s, nodeInfo);
9695             }, this);
9696             return;
9697         }
9698         var node = this.getNode(nodeInfo);
9699         if(!node || !this.isSelected(node)){
9700             Roo.log("not selected");
9701             return; // not selected.
9702         }
9703         // fireevent???
9704         var ns = [];
9705         Roo.each(this.selections, function(s) {
9706             if (s == node ) {
9707                 Roo.fly(node).removeClass(this.selectedClass);
9708
9709                 return;
9710             }
9711             ns.push(s);
9712         },this);
9713         
9714         this.selections= ns;
9715         this.fireEvent("selectionchange", this, this.selections);
9716     },
9717
9718     /**
9719      * Gets a template node.
9720      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9721      * @return {HTMLElement} The node or null if it wasn't found
9722      */
9723     getNode : function(nodeInfo){
9724         if(typeof nodeInfo == "string"){
9725             return document.getElementById(nodeInfo);
9726         }else if(typeof nodeInfo == "number"){
9727             return this.nodes[nodeInfo];
9728         }
9729         return nodeInfo;
9730     },
9731
9732     /**
9733      * Gets a range template nodes.
9734      * @param {Number} startIndex
9735      * @param {Number} endIndex
9736      * @return {Array} An array of nodes
9737      */
9738     getNodes : function(start, end){
9739         var ns = this.nodes;
9740         start = start || 0;
9741         end = typeof end == "undefined" ? ns.length - 1 : end;
9742         var nodes = [];
9743         if(start <= end){
9744             for(var i = start; i <= end; i++){
9745                 nodes.push(ns[i]);
9746             }
9747         } else{
9748             for(var i = start; i >= end; i--){
9749                 nodes.push(ns[i]);
9750             }
9751         }
9752         return nodes;
9753     },
9754
9755     /**
9756      * Finds the index of the passed node
9757      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9758      * @return {Number} The index of the node or -1
9759      */
9760     indexOf : function(node){
9761         node = this.getNode(node);
9762         if(typeof node.nodeIndex == "number"){
9763             return node.nodeIndex;
9764         }
9765         var ns = this.nodes;
9766         for(var i = 0, len = ns.length; i < len; i++){
9767             if(ns[i] == node){
9768                 return i;
9769             }
9770         }
9771         return -1;
9772     }
9773 });
9774 /*
9775  * Based on:
9776  * Ext JS Library 1.1.1
9777  * Copyright(c) 2006-2007, Ext JS, LLC.
9778  *
9779  * Originally Released Under LGPL - original licence link has changed is not relivant.
9780  *
9781  * Fork - LGPL
9782  * <script type="text/javascript">
9783  */
9784
9785 /**
9786  * @class Roo.JsonView
9787  * @extends Roo.View
9788  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9789 <pre><code>
9790 var view = new Roo.JsonView({
9791     container: "my-element",
9792     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9793     multiSelect: true, 
9794     jsonRoot: "data" 
9795 });
9796
9797 // listen for node click?
9798 view.on("click", function(vw, index, node, e){
9799     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9800 });
9801
9802 // direct load of JSON data
9803 view.load("foobar.php");
9804
9805 // Example from my blog list
9806 var tpl = new Roo.Template(
9807     '&lt;div class="entry"&gt;' +
9808     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9809     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9810     "&lt;/div&gt;&lt;hr /&gt;"
9811 );
9812
9813 var moreView = new Roo.JsonView({
9814     container :  "entry-list", 
9815     template : tpl,
9816     jsonRoot: "posts"
9817 });
9818 moreView.on("beforerender", this.sortEntries, this);
9819 moreView.load({
9820     url: "/blog/get-posts.php",
9821     params: "allposts=true",
9822     text: "Loading Blog Entries..."
9823 });
9824 </code></pre>
9825
9826 * Note: old code is supported with arguments : (container, template, config)
9827
9828
9829  * @constructor
9830  * Create a new JsonView
9831  * 
9832  * @param {Object} config The config object
9833  * 
9834  */
9835 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9836     
9837     
9838     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9839
9840     var um = this.el.getUpdateManager();
9841     um.setRenderer(this);
9842     um.on("update", this.onLoad, this);
9843     um.on("failure", this.onLoadException, this);
9844
9845     /**
9846      * @event beforerender
9847      * Fires before rendering of the downloaded JSON data.
9848      * @param {Roo.JsonView} this
9849      * @param {Object} data The JSON data loaded
9850      */
9851     /**
9852      * @event load
9853      * Fires when data is loaded.
9854      * @param {Roo.JsonView} this
9855      * @param {Object} data The JSON data loaded
9856      * @param {Object} response The raw Connect response object
9857      */
9858     /**
9859      * @event loadexception
9860      * Fires when loading fails.
9861      * @param {Roo.JsonView} this
9862      * @param {Object} response The raw Connect response object
9863      */
9864     this.addEvents({
9865         'beforerender' : true,
9866         'load' : true,
9867         'loadexception' : true
9868     });
9869 };
9870 Roo.extend(Roo.JsonView, Roo.View, {
9871     /**
9872      * @type {String} The root property in the loaded JSON object that contains the data
9873      */
9874     jsonRoot : "",
9875
9876     /**
9877      * Refreshes the view.
9878      */
9879     refresh : function(){
9880         this.clearSelections();
9881         this.el.update("");
9882         var html = [];
9883         var o = this.jsonData;
9884         if(o && o.length > 0){
9885             for(var i = 0, len = o.length; i < len; i++){
9886                 var data = this.prepareData(o[i], i, o);
9887                 html[html.length] = this.tpl.apply(data);
9888             }
9889         }else{
9890             html.push(this.emptyText);
9891         }
9892         this.el.update(html.join(""));
9893         this.nodes = this.el.dom.childNodes;
9894         this.updateIndexes(0);
9895     },
9896
9897     /**
9898      * 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.
9899      * @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:
9900      <pre><code>
9901      view.load({
9902          url: "your-url.php",
9903          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9904          callback: yourFunction,
9905          scope: yourObject, //(optional scope)
9906          discardUrl: false,
9907          nocache: false,
9908          text: "Loading...",
9909          timeout: 30,
9910          scripts: false
9911      });
9912      </code></pre>
9913      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9914      * 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.
9915      * @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}
9916      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9917      * @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.
9918      */
9919     load : function(){
9920         var um = this.el.getUpdateManager();
9921         um.update.apply(um, arguments);
9922     },
9923
9924     render : function(el, response){
9925         this.clearSelections();
9926         this.el.update("");
9927         var o;
9928         try{
9929             o = Roo.util.JSON.decode(response.responseText);
9930             if(this.jsonRoot){
9931                 
9932                 o = o[this.jsonRoot];
9933             }
9934         } catch(e){
9935         }
9936         /**
9937          * The current JSON data or null
9938          */
9939         this.jsonData = o;
9940         this.beforeRender();
9941         this.refresh();
9942     },
9943
9944 /**
9945  * Get the number of records in the current JSON dataset
9946  * @return {Number}
9947  */
9948     getCount : function(){
9949         return this.jsonData ? this.jsonData.length : 0;
9950     },
9951
9952 /**
9953  * Returns the JSON object for the specified node(s)
9954  * @param {HTMLElement/Array} node The node or an array of nodes
9955  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9956  * you get the JSON object for the node
9957  */
9958     getNodeData : function(node){
9959         if(node instanceof Array){
9960             var data = [];
9961             for(var i = 0, len = node.length; i < len; i++){
9962                 data.push(this.getNodeData(node[i]));
9963             }
9964             return data;
9965         }
9966         return this.jsonData[this.indexOf(node)] || null;
9967     },
9968
9969     beforeRender : function(){
9970         this.snapshot = this.jsonData;
9971         if(this.sortInfo){
9972             this.sort.apply(this, this.sortInfo);
9973         }
9974         this.fireEvent("beforerender", this, this.jsonData);
9975     },
9976
9977     onLoad : function(el, o){
9978         this.fireEvent("load", this, this.jsonData, o);
9979     },
9980
9981     onLoadException : function(el, o){
9982         this.fireEvent("loadexception", this, o);
9983     },
9984
9985 /**
9986  * Filter the data by a specific property.
9987  * @param {String} property A property on your JSON objects
9988  * @param {String/RegExp} value Either string that the property values
9989  * should start with, or a RegExp to test against the property
9990  */
9991     filter : function(property, value){
9992         if(this.jsonData){
9993             var data = [];
9994             var ss = this.snapshot;
9995             if(typeof value == "string"){
9996                 var vlen = value.length;
9997                 if(vlen == 0){
9998                     this.clearFilter();
9999                     return;
10000                 }
10001                 value = value.toLowerCase();
10002                 for(var i = 0, len = ss.length; i < len; i++){
10003                     var o = ss[i];
10004                     if(o[property].substr(0, vlen).toLowerCase() == value){
10005                         data.push(o);
10006                     }
10007                 }
10008             } else if(value.exec){ // regex?
10009                 for(var i = 0, len = ss.length; i < len; i++){
10010                     var o = ss[i];
10011                     if(value.test(o[property])){
10012                         data.push(o);
10013                     }
10014                 }
10015             } else{
10016                 return;
10017             }
10018             this.jsonData = data;
10019             this.refresh();
10020         }
10021     },
10022
10023 /**
10024  * Filter by a function. The passed function will be called with each
10025  * object in the current dataset. If the function returns true the value is kept,
10026  * otherwise it is filtered.
10027  * @param {Function} fn
10028  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
10029  */
10030     filterBy : function(fn, scope){
10031         if(this.jsonData){
10032             var data = [];
10033             var ss = this.snapshot;
10034             for(var i = 0, len = ss.length; i < len; i++){
10035                 var o = ss[i];
10036                 if(fn.call(scope || this, o)){
10037                     data.push(o);
10038                 }
10039             }
10040             this.jsonData = data;
10041             this.refresh();
10042         }
10043     },
10044
10045 /**
10046  * Clears the current filter.
10047  */
10048     clearFilter : function(){
10049         if(this.snapshot && this.jsonData != this.snapshot){
10050             this.jsonData = this.snapshot;
10051             this.refresh();
10052         }
10053     },
10054
10055
10056 /**
10057  * Sorts the data for this view and refreshes it.
10058  * @param {String} property A property on your JSON objects to sort on
10059  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
10060  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
10061  */
10062     sort : function(property, dir, sortType){
10063         this.sortInfo = Array.prototype.slice.call(arguments, 0);
10064         if(this.jsonData){
10065             var p = property;
10066             var dsc = dir && dir.toLowerCase() == "desc";
10067             var f = function(o1, o2){
10068                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10069                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10070                 ;
10071                 if(v1 < v2){
10072                     return dsc ? +1 : -1;
10073                 } else if(v1 > v2){
10074                     return dsc ? -1 : +1;
10075                 } else{
10076                     return 0;
10077                 }
10078             };
10079             this.jsonData.sort(f);
10080             this.refresh();
10081             if(this.jsonData != this.snapshot){
10082                 this.snapshot.sort(f);
10083             }
10084         }
10085     }
10086 });/*
10087  * Based on:
10088  * Ext JS Library 1.1.1
10089  * Copyright(c) 2006-2007, Ext JS, LLC.
10090  *
10091  * Originally Released Under LGPL - original licence link has changed is not relivant.
10092  *
10093  * Fork - LGPL
10094  * <script type="text/javascript">
10095  */
10096  
10097
10098 /**
10099  * @class Roo.ColorPalette
10100  * @extends Roo.Component
10101  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10102  * Here's an example of typical usage:
10103  * <pre><code>
10104 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10105 cp.render('my-div');
10106
10107 cp.on('select', function(palette, selColor){
10108     // do something with selColor
10109 });
10110 </code></pre>
10111  * @constructor
10112  * Create a new ColorPalette
10113  * @param {Object} config The config object
10114  */
10115 Roo.ColorPalette = function(config){
10116     Roo.ColorPalette.superclass.constructor.call(this, config);
10117     this.addEvents({
10118         /**
10119              * @event select
10120              * Fires when a color is selected
10121              * @param {ColorPalette} this
10122              * @param {String} color The 6-digit color hex code (without the # symbol)
10123              */
10124         select: true
10125     });
10126
10127     if(this.handler){
10128         this.on("select", this.handler, this.scope, true);
10129     }
10130 };
10131 Roo.extend(Roo.ColorPalette, Roo.Component, {
10132     /**
10133      * @cfg {String} itemCls
10134      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10135      */
10136     itemCls : "x-color-palette",
10137     /**
10138      * @cfg {String} value
10139      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10140      * the hex codes are case-sensitive.
10141      */
10142     value : null,
10143     clickEvent:'click',
10144     // private
10145     ctype: "Roo.ColorPalette",
10146
10147     /**
10148      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10149      */
10150     allowReselect : false,
10151
10152     /**
10153      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10154      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10155      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10156      * of colors with the width setting until the box is symmetrical.</p>
10157      * <p>You can override individual colors if needed:</p>
10158      * <pre><code>
10159 var cp = new Roo.ColorPalette();
10160 cp.colors[0] = "FF0000";  // change the first box to red
10161 </code></pre>
10162
10163 Or you can provide a custom array of your own for complete control:
10164 <pre><code>
10165 var cp = new Roo.ColorPalette();
10166 cp.colors = ["000000", "993300", "333300"];
10167 </code></pre>
10168      * @type Array
10169      */
10170     colors : [
10171         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10172         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10173         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10174         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10175         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10176     ],
10177
10178     // private
10179     onRender : function(container, position){
10180         var t = new Roo.MasterTemplate(
10181             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10182         );
10183         var c = this.colors;
10184         for(var i = 0, len = c.length; i < len; i++){
10185             t.add([c[i]]);
10186         }
10187         var el = document.createElement("div");
10188         el.className = this.itemCls;
10189         t.overwrite(el);
10190         container.dom.insertBefore(el, position);
10191         this.el = Roo.get(el);
10192         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10193         if(this.clickEvent != 'click'){
10194             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10195         }
10196     },
10197
10198     // private
10199     afterRender : function(){
10200         Roo.ColorPalette.superclass.afterRender.call(this);
10201         if(this.value){
10202             var s = this.value;
10203             this.value = null;
10204             this.select(s);
10205         }
10206     },
10207
10208     // private
10209     handleClick : function(e, t){
10210         e.preventDefault();
10211         if(!this.disabled){
10212             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10213             this.select(c.toUpperCase());
10214         }
10215     },
10216
10217     /**
10218      * Selects the specified color in the palette (fires the select event)
10219      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10220      */
10221     select : function(color){
10222         color = color.replace("#", "");
10223         if(color != this.value || this.allowReselect){
10224             var el = this.el;
10225             if(this.value){
10226                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10227             }
10228             el.child("a.color-"+color).addClass("x-color-palette-sel");
10229             this.value = color;
10230             this.fireEvent("select", this, color);
10231         }
10232     }
10233 });/*
10234  * Based on:
10235  * Ext JS Library 1.1.1
10236  * Copyright(c) 2006-2007, Ext JS, LLC.
10237  *
10238  * Originally Released Under LGPL - original licence link has changed is not relivant.
10239  *
10240  * Fork - LGPL
10241  * <script type="text/javascript">
10242  */
10243  
10244 /**
10245  * @class Roo.DatePicker
10246  * @extends Roo.Component
10247  * Simple date picker class.
10248  * @constructor
10249  * Create a new DatePicker
10250  * @param {Object} config The config object
10251  */
10252 Roo.DatePicker = function(config){
10253     Roo.DatePicker.superclass.constructor.call(this, config);
10254
10255     this.value = config && config.value ?
10256                  config.value.clearTime() : new Date().clearTime();
10257
10258     this.addEvents({
10259         /**
10260              * @event select
10261              * Fires when a date is selected
10262              * @param {DatePicker} this
10263              * @param {Date} date The selected date
10264              */
10265         'select': true,
10266         /**
10267              * @event monthchange
10268              * Fires when the displayed month changes 
10269              * @param {DatePicker} this
10270              * @param {Date} date The selected month
10271              */
10272         'monthchange': true
10273     });
10274
10275     if(this.handler){
10276         this.on("select", this.handler,  this.scope || this);
10277     }
10278     // build the disabledDatesRE
10279     if(!this.disabledDatesRE && this.disabledDates){
10280         var dd = this.disabledDates;
10281         var re = "(?:";
10282         for(var i = 0; i < dd.length; i++){
10283             re += dd[i];
10284             if(i != dd.length-1) re += "|";
10285         }
10286         this.disabledDatesRE = new RegExp(re + ")");
10287     }
10288 };
10289
10290 Roo.extend(Roo.DatePicker, Roo.Component, {
10291     /**
10292      * @cfg {String} todayText
10293      * The text to display on the button that selects the current date (defaults to "Today")
10294      */
10295     todayText : "Today",
10296     /**
10297      * @cfg {String} okText
10298      * The text to display on the ok button
10299      */
10300     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10301     /**
10302      * @cfg {String} cancelText
10303      * The text to display on the cancel button
10304      */
10305     cancelText : "Cancel",
10306     /**
10307      * @cfg {String} todayTip
10308      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10309      */
10310     todayTip : "{0} (Spacebar)",
10311     /**
10312      * @cfg {Date} minDate
10313      * Minimum allowable date (JavaScript date object, defaults to null)
10314      */
10315     minDate : null,
10316     /**
10317      * @cfg {Date} maxDate
10318      * Maximum allowable date (JavaScript date object, defaults to null)
10319      */
10320     maxDate : null,
10321     /**
10322      * @cfg {String} minText
10323      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10324      */
10325     minText : "This date is before the minimum date",
10326     /**
10327      * @cfg {String} maxText
10328      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10329      */
10330     maxText : "This date is after the maximum date",
10331     /**
10332      * @cfg {String} format
10333      * The default date format string which can be overriden for localization support.  The format must be
10334      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10335      */
10336     format : "m/d/y",
10337     /**
10338      * @cfg {Array} disabledDays
10339      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10340      */
10341     disabledDays : null,
10342     /**
10343      * @cfg {String} disabledDaysText
10344      * The tooltip to display when the date falls on a disabled day (defaults to "")
10345      */
10346     disabledDaysText : "",
10347     /**
10348      * @cfg {RegExp} disabledDatesRE
10349      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10350      */
10351     disabledDatesRE : null,
10352     /**
10353      * @cfg {String} disabledDatesText
10354      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10355      */
10356     disabledDatesText : "",
10357     /**
10358      * @cfg {Boolean} constrainToViewport
10359      * True to constrain the date picker to the viewport (defaults to true)
10360      */
10361     constrainToViewport : true,
10362     /**
10363      * @cfg {Array} monthNames
10364      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10365      */
10366     monthNames : Date.monthNames,
10367     /**
10368      * @cfg {Array} dayNames
10369      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10370      */
10371     dayNames : Date.dayNames,
10372     /**
10373      * @cfg {String} nextText
10374      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10375      */
10376     nextText: 'Next Month (Control+Right)',
10377     /**
10378      * @cfg {String} prevText
10379      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10380      */
10381     prevText: 'Previous Month (Control+Left)',
10382     /**
10383      * @cfg {String} monthYearText
10384      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10385      */
10386     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10387     /**
10388      * @cfg {Number} startDay
10389      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10390      */
10391     startDay : 0,
10392     /**
10393      * @cfg {Bool} showClear
10394      * Show a clear button (usefull for date form elements that can be blank.)
10395      */
10396     
10397     showClear: false,
10398     
10399     /**
10400      * Sets the value of the date field
10401      * @param {Date} value The date to set
10402      */
10403     setValue : function(value){
10404         var old = this.value;
10405         
10406         if (typeof(value) == 'string') {
10407          
10408             value = Date.parseDate(value, this.format);
10409         }
10410         if (!value) {
10411             value = new Date();
10412         }
10413         
10414         this.value = value.clearTime(true);
10415         if(this.el){
10416             this.update(this.value);
10417         }
10418     },
10419
10420     /**
10421      * Gets the current selected value of the date field
10422      * @return {Date} The selected date
10423      */
10424     getValue : function(){
10425         return this.value;
10426     },
10427
10428     // private
10429     focus : function(){
10430         if(this.el){
10431             this.update(this.activeDate);
10432         }
10433     },
10434
10435     // privateval
10436     onRender : function(container, position){
10437         
10438         var m = [
10439              '<table cellspacing="0">',
10440                 '<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>',
10441                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10442         var dn = this.dayNames;
10443         for(var i = 0; i < 7; i++){
10444             var d = this.startDay+i;
10445             if(d > 6){
10446                 d = d-7;
10447             }
10448             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10449         }
10450         m[m.length] = "</tr></thead><tbody><tr>";
10451         for(var i = 0; i < 42; i++) {
10452             if(i % 7 == 0 && i != 0){
10453                 m[m.length] = "</tr><tr>";
10454             }
10455             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10456         }
10457         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10458             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10459
10460         var el = document.createElement("div");
10461         el.className = "x-date-picker";
10462         el.innerHTML = m.join("");
10463
10464         container.dom.insertBefore(el, position);
10465
10466         this.el = Roo.get(el);
10467         this.eventEl = Roo.get(el.firstChild);
10468
10469         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10470             handler: this.showPrevMonth,
10471             scope: this,
10472             preventDefault:true,
10473             stopDefault:true
10474         });
10475
10476         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10477             handler: this.showNextMonth,
10478             scope: this,
10479             preventDefault:true,
10480             stopDefault:true
10481         });
10482
10483         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10484
10485         this.monthPicker = this.el.down('div.x-date-mp');
10486         this.monthPicker.enableDisplayMode('block');
10487         
10488         var kn = new Roo.KeyNav(this.eventEl, {
10489             "left" : function(e){
10490                 e.ctrlKey ?
10491                     this.showPrevMonth() :
10492                     this.update(this.activeDate.add("d", -1));
10493             },
10494
10495             "right" : function(e){
10496                 e.ctrlKey ?
10497                     this.showNextMonth() :
10498                     this.update(this.activeDate.add("d", 1));
10499             },
10500
10501             "up" : function(e){
10502                 e.ctrlKey ?
10503                     this.showNextYear() :
10504                     this.update(this.activeDate.add("d", -7));
10505             },
10506
10507             "down" : function(e){
10508                 e.ctrlKey ?
10509                     this.showPrevYear() :
10510                     this.update(this.activeDate.add("d", 7));
10511             },
10512
10513             "pageUp" : function(e){
10514                 this.showNextMonth();
10515             },
10516
10517             "pageDown" : function(e){
10518                 this.showPrevMonth();
10519             },
10520
10521             "enter" : function(e){
10522                 e.stopPropagation();
10523                 return true;
10524             },
10525
10526             scope : this
10527         });
10528
10529         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10530
10531         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10532
10533         this.el.unselectable();
10534         
10535         this.cells = this.el.select("table.x-date-inner tbody td");
10536         this.textNodes = this.el.query("table.x-date-inner tbody span");
10537
10538         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10539             text: "&#160;",
10540             tooltip: this.monthYearText
10541         });
10542
10543         this.mbtn.on('click', this.showMonthPicker, this);
10544         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10545
10546
10547         var today = (new Date()).dateFormat(this.format);
10548         
10549         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10550         if (this.showClear) {
10551             baseTb.add( new Roo.Toolbar.Fill());
10552         }
10553         baseTb.add({
10554             text: String.format(this.todayText, today),
10555             tooltip: String.format(this.todayTip, today),
10556             handler: this.selectToday,
10557             scope: this
10558         });
10559         
10560         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10561             
10562         //});
10563         if (this.showClear) {
10564             
10565             baseTb.add( new Roo.Toolbar.Fill());
10566             baseTb.add({
10567                 text: '&#160;',
10568                 cls: 'x-btn-icon x-btn-clear',
10569                 handler: function() {
10570                     //this.value = '';
10571                     this.fireEvent("select", this, '');
10572                 },
10573                 scope: this
10574             });
10575         }
10576         
10577         
10578         if(Roo.isIE){
10579             this.el.repaint();
10580         }
10581         this.update(this.value);
10582     },
10583
10584     createMonthPicker : function(){
10585         if(!this.monthPicker.dom.firstChild){
10586             var buf = ['<table border="0" cellspacing="0">'];
10587             for(var i = 0; i < 6; i++){
10588                 buf.push(
10589                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10590                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10591                     i == 0 ?
10592                     '<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>' :
10593                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10594                 );
10595             }
10596             buf.push(
10597                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10598                     this.okText,
10599                     '</button><button type="button" class="x-date-mp-cancel">',
10600                     this.cancelText,
10601                     '</button></td></tr>',
10602                 '</table>'
10603             );
10604             this.monthPicker.update(buf.join(''));
10605             this.monthPicker.on('click', this.onMonthClick, this);
10606             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10607
10608             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10609             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10610
10611             this.mpMonths.each(function(m, a, i){
10612                 i += 1;
10613                 if((i%2) == 0){
10614                     m.dom.xmonth = 5 + Math.round(i * .5);
10615                 }else{
10616                     m.dom.xmonth = Math.round((i-1) * .5);
10617                 }
10618             });
10619         }
10620     },
10621
10622     showMonthPicker : function(){
10623         this.createMonthPicker();
10624         var size = this.el.getSize();
10625         this.monthPicker.setSize(size);
10626         this.monthPicker.child('table').setSize(size);
10627
10628         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10629         this.updateMPMonth(this.mpSelMonth);
10630         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10631         this.updateMPYear(this.mpSelYear);
10632
10633         this.monthPicker.slideIn('t', {duration:.2});
10634     },
10635
10636     updateMPYear : function(y){
10637         this.mpyear = y;
10638         var ys = this.mpYears.elements;
10639         for(var i = 1; i <= 10; i++){
10640             var td = ys[i-1], y2;
10641             if((i%2) == 0){
10642                 y2 = y + Math.round(i * .5);
10643                 td.firstChild.innerHTML = y2;
10644                 td.xyear = y2;
10645             }else{
10646                 y2 = y - (5-Math.round(i * .5));
10647                 td.firstChild.innerHTML = y2;
10648                 td.xyear = y2;
10649             }
10650             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10651         }
10652     },
10653
10654     updateMPMonth : function(sm){
10655         this.mpMonths.each(function(m, a, i){
10656             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10657         });
10658     },
10659
10660     selectMPMonth: function(m){
10661         
10662     },
10663
10664     onMonthClick : function(e, t){
10665         e.stopEvent();
10666         var el = new Roo.Element(t), pn;
10667         if(el.is('button.x-date-mp-cancel')){
10668             this.hideMonthPicker();
10669         }
10670         else if(el.is('button.x-date-mp-ok')){
10671             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10672             this.hideMonthPicker();
10673         }
10674         else if(pn = el.up('td.x-date-mp-month', 2)){
10675             this.mpMonths.removeClass('x-date-mp-sel');
10676             pn.addClass('x-date-mp-sel');
10677             this.mpSelMonth = pn.dom.xmonth;
10678         }
10679         else if(pn = el.up('td.x-date-mp-year', 2)){
10680             this.mpYears.removeClass('x-date-mp-sel');
10681             pn.addClass('x-date-mp-sel');
10682             this.mpSelYear = pn.dom.xyear;
10683         }
10684         else if(el.is('a.x-date-mp-prev')){
10685             this.updateMPYear(this.mpyear-10);
10686         }
10687         else if(el.is('a.x-date-mp-next')){
10688             this.updateMPYear(this.mpyear+10);
10689         }
10690     },
10691
10692     onMonthDblClick : function(e, t){
10693         e.stopEvent();
10694         var el = new Roo.Element(t), pn;
10695         if(pn = el.up('td.x-date-mp-month', 2)){
10696             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10697             this.hideMonthPicker();
10698         }
10699         else if(pn = el.up('td.x-date-mp-year', 2)){
10700             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10701             this.hideMonthPicker();
10702         }
10703     },
10704
10705     hideMonthPicker : function(disableAnim){
10706         if(this.monthPicker){
10707             if(disableAnim === true){
10708                 this.monthPicker.hide();
10709             }else{
10710                 this.monthPicker.slideOut('t', {duration:.2});
10711             }
10712         }
10713     },
10714
10715     // private
10716     showPrevMonth : function(e){
10717         this.update(this.activeDate.add("mo", -1));
10718     },
10719
10720     // private
10721     showNextMonth : function(e){
10722         this.update(this.activeDate.add("mo", 1));
10723     },
10724
10725     // private
10726     showPrevYear : function(){
10727         this.update(this.activeDate.add("y", -1));
10728     },
10729
10730     // private
10731     showNextYear : function(){
10732         this.update(this.activeDate.add("y", 1));
10733     },
10734
10735     // private
10736     handleMouseWheel : function(e){
10737         var delta = e.getWheelDelta();
10738         if(delta > 0){
10739             this.showPrevMonth();
10740             e.stopEvent();
10741         } else if(delta < 0){
10742             this.showNextMonth();
10743             e.stopEvent();
10744         }
10745     },
10746
10747     // private
10748     handleDateClick : function(e, t){
10749         e.stopEvent();
10750         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10751             this.setValue(new Date(t.dateValue));
10752             this.fireEvent("select", this, this.value);
10753         }
10754     },
10755
10756     // private
10757     selectToday : function(){
10758         this.setValue(new Date().clearTime());
10759         this.fireEvent("select", this, this.value);
10760     },
10761
10762     // private
10763     update : function(date)
10764     {
10765         var vd = this.activeDate;
10766         this.activeDate = date;
10767         if(vd && this.el){
10768             var t = date.getTime();
10769             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10770                 this.cells.removeClass("x-date-selected");
10771                 this.cells.each(function(c){
10772                    if(c.dom.firstChild.dateValue == t){
10773                        c.addClass("x-date-selected");
10774                        setTimeout(function(){
10775                             try{c.dom.firstChild.focus();}catch(e){}
10776                        }, 50);
10777                        return false;
10778                    }
10779                 });
10780                 return;
10781             }
10782         }
10783         
10784         var days = date.getDaysInMonth();
10785         var firstOfMonth = date.getFirstDateOfMonth();
10786         var startingPos = firstOfMonth.getDay()-this.startDay;
10787
10788         if(startingPos <= this.startDay){
10789             startingPos += 7;
10790         }
10791
10792         var pm = date.add("mo", -1);
10793         var prevStart = pm.getDaysInMonth()-startingPos;
10794
10795         var cells = this.cells.elements;
10796         var textEls = this.textNodes;
10797         days += startingPos;
10798
10799         // convert everything to numbers so it's fast
10800         var day = 86400000;
10801         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10802         var today = new Date().clearTime().getTime();
10803         var sel = date.clearTime().getTime();
10804         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10805         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10806         var ddMatch = this.disabledDatesRE;
10807         var ddText = this.disabledDatesText;
10808         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10809         var ddaysText = this.disabledDaysText;
10810         var format = this.format;
10811
10812         var setCellClass = function(cal, cell){
10813             cell.title = "";
10814             var t = d.getTime();
10815             cell.firstChild.dateValue = t;
10816             if(t == today){
10817                 cell.className += " x-date-today";
10818                 cell.title = cal.todayText;
10819             }
10820             if(t == sel){
10821                 cell.className += " x-date-selected";
10822                 setTimeout(function(){
10823                     try{cell.firstChild.focus();}catch(e){}
10824                 }, 50);
10825             }
10826             // disabling
10827             if(t < min) {
10828                 cell.className = " x-date-disabled";
10829                 cell.title = cal.minText;
10830                 return;
10831             }
10832             if(t > max) {
10833                 cell.className = " x-date-disabled";
10834                 cell.title = cal.maxText;
10835                 return;
10836             }
10837             if(ddays){
10838                 if(ddays.indexOf(d.getDay()) != -1){
10839                     cell.title = ddaysText;
10840                     cell.className = " x-date-disabled";
10841                 }
10842             }
10843             if(ddMatch && format){
10844                 var fvalue = d.dateFormat(format);
10845                 if(ddMatch.test(fvalue)){
10846                     cell.title = ddText.replace("%0", fvalue);
10847                     cell.className = " x-date-disabled";
10848                 }
10849             }
10850         };
10851
10852         var i = 0;
10853         for(; i < startingPos; i++) {
10854             textEls[i].innerHTML = (++prevStart);
10855             d.setDate(d.getDate()+1);
10856             cells[i].className = "x-date-prevday";
10857             setCellClass(this, cells[i]);
10858         }
10859         for(; i < days; i++){
10860             intDay = i - startingPos + 1;
10861             textEls[i].innerHTML = (intDay);
10862             d.setDate(d.getDate()+1);
10863             cells[i].className = "x-date-active";
10864             setCellClass(this, cells[i]);
10865         }
10866         var extraDays = 0;
10867         for(; i < 42; i++) {
10868              textEls[i].innerHTML = (++extraDays);
10869              d.setDate(d.getDate()+1);
10870              cells[i].className = "x-date-nextday";
10871              setCellClass(this, cells[i]);
10872         }
10873
10874         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10875         this.fireEvent('monthchange', this, date);
10876         
10877         if(!this.internalRender){
10878             var main = this.el.dom.firstChild;
10879             var w = main.offsetWidth;
10880             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10881             Roo.fly(main).setWidth(w);
10882             this.internalRender = true;
10883             // opera does not respect the auto grow header center column
10884             // then, after it gets a width opera refuses to recalculate
10885             // without a second pass
10886             if(Roo.isOpera && !this.secondPass){
10887                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10888                 this.secondPass = true;
10889                 this.update.defer(10, this, [date]);
10890             }
10891         }
10892         
10893         
10894     }
10895 });        /*
10896  * Based on:
10897  * Ext JS Library 1.1.1
10898  * Copyright(c) 2006-2007, Ext JS, LLC.
10899  *
10900  * Originally Released Under LGPL - original licence link has changed is not relivant.
10901  *
10902  * Fork - LGPL
10903  * <script type="text/javascript">
10904  */
10905 /**
10906  * @class Roo.TabPanel
10907  * @extends Roo.util.Observable
10908  * A lightweight tab container.
10909  * <br><br>
10910  * Usage:
10911  * <pre><code>
10912 // basic tabs 1, built from existing content
10913 var tabs = new Roo.TabPanel("tabs1");
10914 tabs.addTab("script", "View Script");
10915 tabs.addTab("markup", "View Markup");
10916 tabs.activate("script");
10917
10918 // more advanced tabs, built from javascript
10919 var jtabs = new Roo.TabPanel("jtabs");
10920 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10921
10922 // set up the UpdateManager
10923 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10924 var updater = tab2.getUpdateManager();
10925 updater.setDefaultUrl("ajax1.htm");
10926 tab2.on('activate', updater.refresh, updater, true);
10927
10928 // Use setUrl for Ajax loading
10929 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10930 tab3.setUrl("ajax2.htm", null, true);
10931
10932 // Disabled tab
10933 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10934 tab4.disable();
10935
10936 jtabs.activate("jtabs-1");
10937  * </code></pre>
10938  * @constructor
10939  * Create a new TabPanel.
10940  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10941  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10942  */
10943 Roo.TabPanel = function(container, config){
10944     /**
10945     * The container element for this TabPanel.
10946     * @type Roo.Element
10947     */
10948     this.el = Roo.get(container, true);
10949     if(config){
10950         if(typeof config == "boolean"){
10951             this.tabPosition = config ? "bottom" : "top";
10952         }else{
10953             Roo.apply(this, config);
10954         }
10955     }
10956     if(this.tabPosition == "bottom"){
10957         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10958         this.el.addClass("x-tabs-bottom");
10959     }
10960     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10961     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10962     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10963     if(Roo.isIE){
10964         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10965     }
10966     if(this.tabPosition != "bottom"){
10967         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10968          * @type Roo.Element
10969          */
10970         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10971         this.el.addClass("x-tabs-top");
10972     }
10973     this.items = [];
10974
10975     this.bodyEl.setStyle("position", "relative");
10976
10977     this.active = null;
10978     this.activateDelegate = this.activate.createDelegate(this);
10979
10980     this.addEvents({
10981         /**
10982          * @event tabchange
10983          * Fires when the active tab changes
10984          * @param {Roo.TabPanel} this
10985          * @param {Roo.TabPanelItem} activePanel The new active tab
10986          */
10987         "tabchange": true,
10988         /**
10989          * @event beforetabchange
10990          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10991          * @param {Roo.TabPanel} this
10992          * @param {Object} e Set cancel to true on this object to cancel the tab change
10993          * @param {Roo.TabPanelItem} tab The tab being changed to
10994          */
10995         "beforetabchange" : true
10996     });
10997
10998     Roo.EventManager.onWindowResize(this.onResize, this);
10999     this.cpad = this.el.getPadding("lr");
11000     this.hiddenCount = 0;
11001
11002
11003     // toolbar on the tabbar support...
11004     if (this.toolbar) {
11005         var tcfg = this.toolbar;
11006         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
11007         this.toolbar = new Roo.Toolbar(tcfg);
11008         if (Roo.isSafari) {
11009             var tbl = tcfg.container.child('table', true);
11010             tbl.setAttribute('width', '100%');
11011         }
11012         
11013     }
11014    
11015
11016
11017     Roo.TabPanel.superclass.constructor.call(this);
11018 };
11019
11020 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
11021     /*
11022      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
11023      */
11024     tabPosition : "top",
11025     /*
11026      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
11027      */
11028     currentTabWidth : 0,
11029     /*
11030      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
11031      */
11032     minTabWidth : 40,
11033     /*
11034      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
11035      */
11036     maxTabWidth : 250,
11037     /*
11038      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
11039      */
11040     preferredTabWidth : 175,
11041     /*
11042      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
11043      */
11044     resizeTabs : false,
11045     /*
11046      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
11047      */
11048     monitorResize : true,
11049     /*
11050      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
11051      */
11052     toolbar : false,
11053
11054     /**
11055      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
11056      * @param {String} id The id of the div to use <b>or create</b>
11057      * @param {String} text The text for the tab
11058      * @param {String} content (optional) Content to put in the TabPanelItem body
11059      * @param {Boolean} closable (optional) True to create a close icon on the tab
11060      * @return {Roo.TabPanelItem} The created TabPanelItem
11061      */
11062     addTab : function(id, text, content, closable){
11063         var item = new Roo.TabPanelItem(this, id, text, closable);
11064         this.addTabItem(item);
11065         if(content){
11066             item.setContent(content);
11067         }
11068         return item;
11069     },
11070
11071     /**
11072      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11073      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11074      * @return {Roo.TabPanelItem}
11075      */
11076     getTab : function(id){
11077         return this.items[id];
11078     },
11079
11080     /**
11081      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11082      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11083      */
11084     hideTab : function(id){
11085         var t = this.items[id];
11086         if(!t.isHidden()){
11087            t.setHidden(true);
11088            this.hiddenCount++;
11089            this.autoSizeTabs();
11090         }
11091     },
11092
11093     /**
11094      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11095      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11096      */
11097     unhideTab : function(id){
11098         var t = this.items[id];
11099         if(t.isHidden()){
11100            t.setHidden(false);
11101            this.hiddenCount--;
11102            this.autoSizeTabs();
11103         }
11104     },
11105
11106     /**
11107      * Adds an existing {@link Roo.TabPanelItem}.
11108      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11109      */
11110     addTabItem : function(item){
11111         this.items[item.id] = item;
11112         this.items.push(item);
11113         if(this.resizeTabs){
11114            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11115            this.autoSizeTabs();
11116         }else{
11117             item.autoSize();
11118         }
11119     },
11120
11121     /**
11122      * Removes a {@link Roo.TabPanelItem}.
11123      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11124      */
11125     removeTab : function(id){
11126         var items = this.items;
11127         var tab = items[id];
11128         if(!tab) { return; }
11129         var index = items.indexOf(tab);
11130         if(this.active == tab && items.length > 1){
11131             var newTab = this.getNextAvailable(index);
11132             if(newTab) {
11133                 newTab.activate();
11134             }
11135         }
11136         this.stripEl.dom.removeChild(tab.pnode.dom);
11137         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11138             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11139         }
11140         items.splice(index, 1);
11141         delete this.items[tab.id];
11142         tab.fireEvent("close", tab);
11143         tab.purgeListeners();
11144         this.autoSizeTabs();
11145     },
11146
11147     getNextAvailable : function(start){
11148         var items = this.items;
11149         var index = start;
11150         // look for a next tab that will slide over to
11151         // replace the one being removed
11152         while(index < items.length){
11153             var item = items[++index];
11154             if(item && !item.isHidden()){
11155                 return item;
11156             }
11157         }
11158         // if one isn't found select the previous tab (on the left)
11159         index = start;
11160         while(index >= 0){
11161             var item = items[--index];
11162             if(item && !item.isHidden()){
11163                 return item;
11164             }
11165         }
11166         return null;
11167     },
11168
11169     /**
11170      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11171      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11172      */
11173     disableTab : function(id){
11174         var tab = this.items[id];
11175         if(tab && this.active != tab){
11176             tab.disable();
11177         }
11178     },
11179
11180     /**
11181      * Enables a {@link Roo.TabPanelItem} that is disabled.
11182      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11183      */
11184     enableTab : function(id){
11185         var tab = this.items[id];
11186         tab.enable();
11187     },
11188
11189     /**
11190      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11191      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11192      * @return {Roo.TabPanelItem} The TabPanelItem.
11193      */
11194     activate : function(id){
11195         var tab = this.items[id];
11196         if(!tab){
11197             return null;
11198         }
11199         if(tab == this.active || tab.disabled){
11200             return tab;
11201         }
11202         var e = {};
11203         this.fireEvent("beforetabchange", this, e, tab);
11204         if(e.cancel !== true && !tab.disabled){
11205             if(this.active){
11206                 this.active.hide();
11207             }
11208             this.active = this.items[id];
11209             this.active.show();
11210             this.fireEvent("tabchange", this, this.active);
11211         }
11212         return tab;
11213     },
11214
11215     /**
11216      * Gets the active {@link Roo.TabPanelItem}.
11217      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11218      */
11219     getActiveTab : function(){
11220         return this.active;
11221     },
11222
11223     /**
11224      * Updates the tab body element to fit the height of the container element
11225      * for overflow scrolling
11226      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11227      */
11228     syncHeight : function(targetHeight){
11229         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11230         var bm = this.bodyEl.getMargins();
11231         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11232         this.bodyEl.setHeight(newHeight);
11233         return newHeight;
11234     },
11235
11236     onResize : function(){
11237         if(this.monitorResize){
11238             this.autoSizeTabs();
11239         }
11240     },
11241
11242     /**
11243      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11244      */
11245     beginUpdate : function(){
11246         this.updating = true;
11247     },
11248
11249     /**
11250      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11251      */
11252     endUpdate : function(){
11253         this.updating = false;
11254         this.autoSizeTabs();
11255     },
11256
11257     /**
11258      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11259      */
11260     autoSizeTabs : function(){
11261         var count = this.items.length;
11262         var vcount = count - this.hiddenCount;
11263         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11264         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11265         var availWidth = Math.floor(w / vcount);
11266         var b = this.stripBody;
11267         if(b.getWidth() > w){
11268             var tabs = this.items;
11269             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11270             if(availWidth < this.minTabWidth){
11271                 /*if(!this.sleft){    // incomplete scrolling code
11272                     this.createScrollButtons();
11273                 }
11274                 this.showScroll();
11275                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11276             }
11277         }else{
11278             if(this.currentTabWidth < this.preferredTabWidth){
11279                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11280             }
11281         }
11282     },
11283
11284     /**
11285      * Returns the number of tabs in this TabPanel.
11286      * @return {Number}
11287      */
11288      getCount : function(){
11289          return this.items.length;
11290      },
11291
11292     /**
11293      * Resizes all the tabs to the passed width
11294      * @param {Number} The new width
11295      */
11296     setTabWidth : function(width){
11297         this.currentTabWidth = width;
11298         for(var i = 0, len = this.items.length; i < len; i++) {
11299                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11300         }
11301     },
11302
11303     /**
11304      * Destroys this TabPanel
11305      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11306      */
11307     destroy : function(removeEl){
11308         Roo.EventManager.removeResizeListener(this.onResize, this);
11309         for(var i = 0, len = this.items.length; i < len; i++){
11310             this.items[i].purgeListeners();
11311         }
11312         if(removeEl === true){
11313             this.el.update("");
11314             this.el.remove();
11315         }
11316     }
11317 });
11318
11319 /**
11320  * @class Roo.TabPanelItem
11321  * @extends Roo.util.Observable
11322  * Represents an individual item (tab plus body) in a TabPanel.
11323  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11324  * @param {String} id The id of this TabPanelItem
11325  * @param {String} text The text for the tab of this TabPanelItem
11326  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11327  */
11328 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11329     /**
11330      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11331      * @type Roo.TabPanel
11332      */
11333     this.tabPanel = tabPanel;
11334     /**
11335      * The id for this TabPanelItem
11336      * @type String
11337      */
11338     this.id = id;
11339     /** @private */
11340     this.disabled = false;
11341     /** @private */
11342     this.text = text;
11343     /** @private */
11344     this.loaded = false;
11345     this.closable = closable;
11346
11347     /**
11348      * The body element for this TabPanelItem.
11349      * @type Roo.Element
11350      */
11351     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11352     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11353     this.bodyEl.setStyle("display", "block");
11354     this.bodyEl.setStyle("zoom", "1");
11355     this.hideAction();
11356
11357     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11358     /** @private */
11359     this.el = Roo.get(els.el, true);
11360     this.inner = Roo.get(els.inner, true);
11361     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11362     this.pnode = Roo.get(els.el.parentNode, true);
11363     this.el.on("mousedown", this.onTabMouseDown, this);
11364     this.el.on("click", this.onTabClick, this);
11365     /** @private */
11366     if(closable){
11367         var c = Roo.get(els.close, true);
11368         c.dom.title = this.closeText;
11369         c.addClassOnOver("close-over");
11370         c.on("click", this.closeClick, this);
11371      }
11372
11373     this.addEvents({
11374          /**
11375          * @event activate
11376          * Fires when this tab becomes the active tab.
11377          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11378          * @param {Roo.TabPanelItem} this
11379          */
11380         "activate": true,
11381         /**
11382          * @event beforeclose
11383          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11384          * @param {Roo.TabPanelItem} this
11385          * @param {Object} e Set cancel to true on this object to cancel the close.
11386          */
11387         "beforeclose": true,
11388         /**
11389          * @event close
11390          * Fires when this tab is closed.
11391          * @param {Roo.TabPanelItem} this
11392          */
11393          "close": true,
11394         /**
11395          * @event deactivate
11396          * Fires when this tab is no longer the active tab.
11397          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11398          * @param {Roo.TabPanelItem} this
11399          */
11400          "deactivate" : true
11401     });
11402     this.hidden = false;
11403
11404     Roo.TabPanelItem.superclass.constructor.call(this);
11405 };
11406
11407 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11408     purgeListeners : function(){
11409        Roo.util.Observable.prototype.purgeListeners.call(this);
11410        this.el.removeAllListeners();
11411     },
11412     /**
11413      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11414      */
11415     show : function(){
11416         this.pnode.addClass("on");
11417         this.showAction();
11418         if(Roo.isOpera){
11419             this.tabPanel.stripWrap.repaint();
11420         }
11421         this.fireEvent("activate", this.tabPanel, this);
11422     },
11423
11424     /**
11425      * Returns true if this tab is the active tab.
11426      * @return {Boolean}
11427      */
11428     isActive : function(){
11429         return this.tabPanel.getActiveTab() == this;
11430     },
11431
11432     /**
11433      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11434      */
11435     hide : function(){
11436         this.pnode.removeClass("on");
11437         this.hideAction();
11438         this.fireEvent("deactivate", this.tabPanel, this);
11439     },
11440
11441     hideAction : function(){
11442         this.bodyEl.hide();
11443         this.bodyEl.setStyle("position", "absolute");
11444         this.bodyEl.setLeft("-20000px");
11445         this.bodyEl.setTop("-20000px");
11446     },
11447
11448     showAction : function(){
11449         this.bodyEl.setStyle("position", "relative");
11450         this.bodyEl.setTop("");
11451         this.bodyEl.setLeft("");
11452         this.bodyEl.show();
11453     },
11454
11455     /**
11456      * Set the tooltip for the tab.
11457      * @param {String} tooltip The tab's tooltip
11458      */
11459     setTooltip : function(text){
11460         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11461             this.textEl.dom.qtip = text;
11462             this.textEl.dom.removeAttribute('title');
11463         }else{
11464             this.textEl.dom.title = text;
11465         }
11466     },
11467
11468     onTabClick : function(e){
11469         e.preventDefault();
11470         this.tabPanel.activate(this.id);
11471     },
11472
11473     onTabMouseDown : function(e){
11474         e.preventDefault();
11475         this.tabPanel.activate(this.id);
11476     },
11477
11478     getWidth : function(){
11479         return this.inner.getWidth();
11480     },
11481
11482     setWidth : function(width){
11483         var iwidth = width - this.pnode.getPadding("lr");
11484         this.inner.setWidth(iwidth);
11485         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11486         this.pnode.setWidth(width);
11487     },
11488
11489     /**
11490      * Show or hide the tab
11491      * @param {Boolean} hidden True to hide or false to show.
11492      */
11493     setHidden : function(hidden){
11494         this.hidden = hidden;
11495         this.pnode.setStyle("display", hidden ? "none" : "");
11496     },
11497
11498     /**
11499      * Returns true if this tab is "hidden"
11500      * @return {Boolean}
11501      */
11502     isHidden : function(){
11503         return this.hidden;
11504     },
11505
11506     /**
11507      * Returns the text for this tab
11508      * @return {String}
11509      */
11510     getText : function(){
11511         return this.text;
11512     },
11513
11514     autoSize : function(){
11515         //this.el.beginMeasure();
11516         this.textEl.setWidth(1);
11517         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11518         //this.el.endMeasure();
11519     },
11520
11521     /**
11522      * Sets the text for the tab (Note: this also sets the tooltip text)
11523      * @param {String} text The tab's text and tooltip
11524      */
11525     setText : function(text){
11526         this.text = text;
11527         this.textEl.update(text);
11528         this.setTooltip(text);
11529         if(!this.tabPanel.resizeTabs){
11530             this.autoSize();
11531         }
11532     },
11533     /**
11534      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11535      */
11536     activate : function(){
11537         this.tabPanel.activate(this.id);
11538     },
11539
11540     /**
11541      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11542      */
11543     disable : function(){
11544         if(this.tabPanel.active != this){
11545             this.disabled = true;
11546             this.pnode.addClass("disabled");
11547         }
11548     },
11549
11550     /**
11551      * Enables this TabPanelItem if it was previously disabled.
11552      */
11553     enable : function(){
11554         this.disabled = false;
11555         this.pnode.removeClass("disabled");
11556     },
11557
11558     /**
11559      * Sets the content for this TabPanelItem.
11560      * @param {String} content The content
11561      * @param {Boolean} loadScripts true to look for and load scripts
11562      */
11563     setContent : function(content, loadScripts){
11564         this.bodyEl.update(content, loadScripts);
11565     },
11566
11567     /**
11568      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11569      * @return {Roo.UpdateManager} The UpdateManager
11570      */
11571     getUpdateManager : function(){
11572         return this.bodyEl.getUpdateManager();
11573     },
11574
11575     /**
11576      * Set a URL to be used to load the content for this TabPanelItem.
11577      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11578      * @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)
11579      * @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)
11580      * @return {Roo.UpdateManager} The UpdateManager
11581      */
11582     setUrl : function(url, params, loadOnce){
11583         if(this.refreshDelegate){
11584             this.un('activate', this.refreshDelegate);
11585         }
11586         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11587         this.on("activate", this.refreshDelegate);
11588         return this.bodyEl.getUpdateManager();
11589     },
11590
11591     /** @private */
11592     _handleRefresh : function(url, params, loadOnce){
11593         if(!loadOnce || !this.loaded){
11594             var updater = this.bodyEl.getUpdateManager();
11595             updater.update(url, params, this._setLoaded.createDelegate(this));
11596         }
11597     },
11598
11599     /**
11600      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11601      *   Will fail silently if the setUrl method has not been called.
11602      *   This does not activate the panel, just updates its content.
11603      */
11604     refresh : function(){
11605         if(this.refreshDelegate){
11606            this.loaded = false;
11607            this.refreshDelegate();
11608         }
11609     },
11610
11611     /** @private */
11612     _setLoaded : function(){
11613         this.loaded = true;
11614     },
11615
11616     /** @private */
11617     closeClick : function(e){
11618         var o = {};
11619         e.stopEvent();
11620         this.fireEvent("beforeclose", this, o);
11621         if(o.cancel !== true){
11622             this.tabPanel.removeTab(this.id);
11623         }
11624     },
11625     /**
11626      * The text displayed in the tooltip for the close icon.
11627      * @type String
11628      */
11629     closeText : "Close this tab"
11630 });
11631
11632 /** @private */
11633 Roo.TabPanel.prototype.createStrip = function(container){
11634     var strip = document.createElement("div");
11635     strip.className = "x-tabs-wrap";
11636     container.appendChild(strip);
11637     return strip;
11638 };
11639 /** @private */
11640 Roo.TabPanel.prototype.createStripList = function(strip){
11641     // div wrapper for retard IE
11642     // returns the "tr" element.
11643     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11644         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11645         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11646     return strip.firstChild.firstChild.firstChild.firstChild;
11647 };
11648 /** @private */
11649 Roo.TabPanel.prototype.createBody = function(container){
11650     var body = document.createElement("div");
11651     Roo.id(body, "tab-body");
11652     Roo.fly(body).addClass("x-tabs-body");
11653     container.appendChild(body);
11654     return body;
11655 };
11656 /** @private */
11657 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11658     var body = Roo.getDom(id);
11659     if(!body){
11660         body = document.createElement("div");
11661         body.id = id;
11662     }
11663     Roo.fly(body).addClass("x-tabs-item-body");
11664     bodyEl.insertBefore(body, bodyEl.firstChild);
11665     return body;
11666 };
11667 /** @private */
11668 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11669     var td = document.createElement("td");
11670     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11671     //stripEl.appendChild(td);
11672     if(closable){
11673         td.className = "x-tabs-closable";
11674         if(!this.closeTpl){
11675             this.closeTpl = new Roo.Template(
11676                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11677                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11678                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11679             );
11680         }
11681         var el = this.closeTpl.overwrite(td, {"text": text});
11682         var close = el.getElementsByTagName("div")[0];
11683         var inner = el.getElementsByTagName("em")[0];
11684         return {"el": el, "close": close, "inner": inner};
11685     } else {
11686         if(!this.tabTpl){
11687             this.tabTpl = new Roo.Template(
11688                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11689                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11690             );
11691         }
11692         var el = this.tabTpl.overwrite(td, {"text": text});
11693         var inner = el.getElementsByTagName("em")[0];
11694         return {"el": el, "inner": inner};
11695     }
11696 };/*
11697  * Based on:
11698  * Ext JS Library 1.1.1
11699  * Copyright(c) 2006-2007, Ext JS, LLC.
11700  *
11701  * Originally Released Under LGPL - original licence link has changed is not relivant.
11702  *
11703  * Fork - LGPL
11704  * <script type="text/javascript">
11705  */
11706
11707 /**
11708  * @class Roo.Button
11709  * @extends Roo.util.Observable
11710  * Simple Button class
11711  * @cfg {String} text The button text
11712  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11713  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11714  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11715  * @cfg {Object} scope The scope of the handler
11716  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11717  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11718  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11719  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11720  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11721  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11722    applies if enableToggle = true)
11723  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11724  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11725   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11726  * @constructor
11727  * Create a new button
11728  * @param {Object} config The config object
11729  */
11730 Roo.Button = function(renderTo, config)
11731 {
11732     if (!config) {
11733         config = renderTo;
11734         renderTo = config.renderTo || false;
11735     }
11736     
11737     Roo.apply(this, config);
11738     this.addEvents({
11739         /**
11740              * @event click
11741              * Fires when this button is clicked
11742              * @param {Button} this
11743              * @param {EventObject} e The click event
11744              */
11745             "click" : true,
11746         /**
11747              * @event toggle
11748              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11749              * @param {Button} this
11750              * @param {Boolean} pressed
11751              */
11752             "toggle" : true,
11753         /**
11754              * @event mouseover
11755              * Fires when the mouse hovers over the button
11756              * @param {Button} this
11757              * @param {Event} e The event object
11758              */
11759         'mouseover' : true,
11760         /**
11761              * @event mouseout
11762              * Fires when the mouse exits the button
11763              * @param {Button} this
11764              * @param {Event} e The event object
11765              */
11766         'mouseout': true,
11767          /**
11768              * @event render
11769              * Fires when the button is rendered
11770              * @param {Button} this
11771              */
11772         'render': true
11773     });
11774     if(this.menu){
11775         this.menu = Roo.menu.MenuMgr.get(this.menu);
11776     }
11777     // register listeners first!!  - so render can be captured..
11778     Roo.util.Observable.call(this);
11779     if(renderTo){
11780         this.render(renderTo);
11781     }
11782     
11783   
11784 };
11785
11786 Roo.extend(Roo.Button, Roo.util.Observable, {
11787     /**
11788      * 
11789      */
11790     
11791     /**
11792      * Read-only. True if this button is hidden
11793      * @type Boolean
11794      */
11795     hidden : false,
11796     /**
11797      * Read-only. True if this button is disabled
11798      * @type Boolean
11799      */
11800     disabled : false,
11801     /**
11802      * Read-only. True if this button is pressed (only if enableToggle = true)
11803      * @type Boolean
11804      */
11805     pressed : false,
11806
11807     /**
11808      * @cfg {Number} tabIndex 
11809      * The DOM tabIndex for this button (defaults to undefined)
11810      */
11811     tabIndex : undefined,
11812
11813     /**
11814      * @cfg {Boolean} enableToggle
11815      * True to enable pressed/not pressed toggling (defaults to false)
11816      */
11817     enableToggle: false,
11818     /**
11819      * @cfg {Mixed} menu
11820      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11821      */
11822     menu : undefined,
11823     /**
11824      * @cfg {String} menuAlign
11825      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11826      */
11827     menuAlign : "tl-bl?",
11828
11829     /**
11830      * @cfg {String} iconCls
11831      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11832      */
11833     iconCls : undefined,
11834     /**
11835      * @cfg {String} type
11836      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11837      */
11838     type : 'button',
11839
11840     // private
11841     menuClassTarget: 'tr',
11842
11843     /**
11844      * @cfg {String} clickEvent
11845      * The type of event to map to the button's event handler (defaults to 'click')
11846      */
11847     clickEvent : 'click',
11848
11849     /**
11850      * @cfg {Boolean} handleMouseEvents
11851      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11852      */
11853     handleMouseEvents : true,
11854
11855     /**
11856      * @cfg {String} tooltipType
11857      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11858      */
11859     tooltipType : 'qtip',
11860
11861     /**
11862      * @cfg {String} cls
11863      * A CSS class to apply to the button's main element.
11864      */
11865     
11866     /**
11867      * @cfg {Roo.Template} template (Optional)
11868      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11869      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11870      * require code modifications if required elements (e.g. a button) aren't present.
11871      */
11872
11873     // private
11874     render : function(renderTo){
11875         var btn;
11876         if(this.hideParent){
11877             this.parentEl = Roo.get(renderTo);
11878         }
11879         if(!this.dhconfig){
11880             if(!this.template){
11881                 if(!Roo.Button.buttonTemplate){
11882                     // hideous table template
11883                     Roo.Button.buttonTemplate = new Roo.Template(
11884                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11885                         '<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>',
11886                         "</tr></tbody></table>");
11887                 }
11888                 this.template = Roo.Button.buttonTemplate;
11889             }
11890             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11891             var btnEl = btn.child("button:first");
11892             btnEl.on('focus', this.onFocus, this);
11893             btnEl.on('blur', this.onBlur, this);
11894             if(this.cls){
11895                 btn.addClass(this.cls);
11896             }
11897             if(this.icon){
11898                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11899             }
11900             if(this.iconCls){
11901                 btnEl.addClass(this.iconCls);
11902                 if(!this.cls){
11903                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11904                 }
11905             }
11906             if(this.tabIndex !== undefined){
11907                 btnEl.dom.tabIndex = this.tabIndex;
11908             }
11909             if(this.tooltip){
11910                 if(typeof this.tooltip == 'object'){
11911                     Roo.QuickTips.tips(Roo.apply({
11912                           target: btnEl.id
11913                     }, this.tooltip));
11914                 } else {
11915                     btnEl.dom[this.tooltipType] = this.tooltip;
11916                 }
11917             }
11918         }else{
11919             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11920         }
11921         this.el = btn;
11922         if(this.id){
11923             this.el.dom.id = this.el.id = this.id;
11924         }
11925         if(this.menu){
11926             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11927             this.menu.on("show", this.onMenuShow, this);
11928             this.menu.on("hide", this.onMenuHide, this);
11929         }
11930         btn.addClass("x-btn");
11931         if(Roo.isIE && !Roo.isIE7){
11932             this.autoWidth.defer(1, this);
11933         }else{
11934             this.autoWidth();
11935         }
11936         if(this.handleMouseEvents){
11937             btn.on("mouseover", this.onMouseOver, this);
11938             btn.on("mouseout", this.onMouseOut, this);
11939             btn.on("mousedown", this.onMouseDown, this);
11940         }
11941         btn.on(this.clickEvent, this.onClick, this);
11942         //btn.on("mouseup", this.onMouseUp, this);
11943         if(this.hidden){
11944             this.hide();
11945         }
11946         if(this.disabled){
11947             this.disable();
11948         }
11949         Roo.ButtonToggleMgr.register(this);
11950         if(this.pressed){
11951             this.el.addClass("x-btn-pressed");
11952         }
11953         if(this.repeat){
11954             var repeater = new Roo.util.ClickRepeater(btn,
11955                 typeof this.repeat == "object" ? this.repeat : {}
11956             );
11957             repeater.on("click", this.onClick,  this);
11958         }
11959         
11960         this.fireEvent('render', this);
11961         
11962     },
11963     /**
11964      * Returns the button's underlying element
11965      * @return {Roo.Element} The element
11966      */
11967     getEl : function(){
11968         return this.el;  
11969     },
11970     
11971     /**
11972      * Destroys this Button and removes any listeners.
11973      */
11974     destroy : function(){
11975         Roo.ButtonToggleMgr.unregister(this);
11976         this.el.removeAllListeners();
11977         this.purgeListeners();
11978         this.el.remove();
11979     },
11980
11981     // private
11982     autoWidth : function(){
11983         if(this.el){
11984             this.el.setWidth("auto");
11985             if(Roo.isIE7 && Roo.isStrict){
11986                 var ib = this.el.child('button');
11987                 if(ib && ib.getWidth() > 20){
11988                     ib.clip();
11989                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11990                 }
11991             }
11992             if(this.minWidth){
11993                 if(this.hidden){
11994                     this.el.beginMeasure();
11995                 }
11996                 if(this.el.getWidth() < this.minWidth){
11997                     this.el.setWidth(this.minWidth);
11998                 }
11999                 if(this.hidden){
12000                     this.el.endMeasure();
12001                 }
12002             }
12003         }
12004     },
12005
12006     /**
12007      * Assigns this button's click handler
12008      * @param {Function} handler The function to call when the button is clicked
12009      * @param {Object} scope (optional) Scope for the function passed in
12010      */
12011     setHandler : function(handler, scope){
12012         this.handler = handler;
12013         this.scope = scope;  
12014     },
12015     
12016     /**
12017      * Sets this button's text
12018      * @param {String} text The button text
12019      */
12020     setText : function(text){
12021         this.text = text;
12022         if(this.el){
12023             this.el.child("td.x-btn-center button.x-btn-text").update(text);
12024         }
12025         this.autoWidth();
12026     },
12027     
12028     /**
12029      * Gets the text for this button
12030      * @return {String} The button text
12031      */
12032     getText : function(){
12033         return this.text;  
12034     },
12035     
12036     /**
12037      * Show this button
12038      */
12039     show: function(){
12040         this.hidden = false;
12041         if(this.el){
12042             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
12043         }
12044     },
12045     
12046     /**
12047      * Hide this button
12048      */
12049     hide: function(){
12050         this.hidden = true;
12051         if(this.el){
12052             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
12053         }
12054     },
12055     
12056     /**
12057      * Convenience function for boolean show/hide
12058      * @param {Boolean} visible True to show, false to hide
12059      */
12060     setVisible: function(visible){
12061         if(visible) {
12062             this.show();
12063         }else{
12064             this.hide();
12065         }
12066     },
12067     
12068     /**
12069      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
12070      * @param {Boolean} state (optional) Force a particular state
12071      */
12072     toggle : function(state){
12073         state = state === undefined ? !this.pressed : state;
12074         if(state != this.pressed){
12075             if(state){
12076                 this.el.addClass("x-btn-pressed");
12077                 this.pressed = true;
12078                 this.fireEvent("toggle", this, true);
12079             }else{
12080                 this.el.removeClass("x-btn-pressed");
12081                 this.pressed = false;
12082                 this.fireEvent("toggle", this, false);
12083             }
12084             if(this.toggleHandler){
12085                 this.toggleHandler.call(this.scope || this, this, state);
12086             }
12087         }
12088     },
12089     
12090     /**
12091      * Focus the button
12092      */
12093     focus : function(){
12094         this.el.child('button:first').focus();
12095     },
12096     
12097     /**
12098      * Disable this button
12099      */
12100     disable : function(){
12101         if(this.el){
12102             this.el.addClass("x-btn-disabled");
12103         }
12104         this.disabled = true;
12105     },
12106     
12107     /**
12108      * Enable this button
12109      */
12110     enable : function(){
12111         if(this.el){
12112             this.el.removeClass("x-btn-disabled");
12113         }
12114         this.disabled = false;
12115     },
12116
12117     /**
12118      * Convenience function for boolean enable/disable
12119      * @param {Boolean} enabled True to enable, false to disable
12120      */
12121     setDisabled : function(v){
12122         this[v !== true ? "enable" : "disable"]();
12123     },
12124
12125     // private
12126     onClick : function(e){
12127         if(e){
12128             e.preventDefault();
12129         }
12130         if(e.button != 0){
12131             return;
12132         }
12133         if(!this.disabled){
12134             if(this.enableToggle){
12135                 this.toggle();
12136             }
12137             if(this.menu && !this.menu.isVisible()){
12138                 this.menu.show(this.el, this.menuAlign);
12139             }
12140             this.fireEvent("click", this, e);
12141             if(this.handler){
12142                 this.el.removeClass("x-btn-over");
12143                 this.handler.call(this.scope || this, this, e);
12144             }
12145         }
12146     },
12147     // private
12148     onMouseOver : function(e){
12149         if(!this.disabled){
12150             this.el.addClass("x-btn-over");
12151             this.fireEvent('mouseover', this, e);
12152         }
12153     },
12154     // private
12155     onMouseOut : function(e){
12156         if(!e.within(this.el,  true)){
12157             this.el.removeClass("x-btn-over");
12158             this.fireEvent('mouseout', this, e);
12159         }
12160     },
12161     // private
12162     onFocus : function(e){
12163         if(!this.disabled){
12164             this.el.addClass("x-btn-focus");
12165         }
12166     },
12167     // private
12168     onBlur : function(e){
12169         this.el.removeClass("x-btn-focus");
12170     },
12171     // private
12172     onMouseDown : function(e){
12173         if(!this.disabled && e.button == 0){
12174             this.el.addClass("x-btn-click");
12175             Roo.get(document).on('mouseup', this.onMouseUp, this);
12176         }
12177     },
12178     // private
12179     onMouseUp : function(e){
12180         if(e.button == 0){
12181             this.el.removeClass("x-btn-click");
12182             Roo.get(document).un('mouseup', this.onMouseUp, this);
12183         }
12184     },
12185     // private
12186     onMenuShow : function(e){
12187         this.el.addClass("x-btn-menu-active");
12188     },
12189     // private
12190     onMenuHide : function(e){
12191         this.el.removeClass("x-btn-menu-active");
12192     }   
12193 });
12194
12195 // Private utility class used by Button
12196 Roo.ButtonToggleMgr = function(){
12197    var groups = {};
12198    
12199    function toggleGroup(btn, state){
12200        if(state){
12201            var g = groups[btn.toggleGroup];
12202            for(var i = 0, l = g.length; i < l; i++){
12203                if(g[i] != btn){
12204                    g[i].toggle(false);
12205                }
12206            }
12207        }
12208    }
12209    
12210    return {
12211        register : function(btn){
12212            if(!btn.toggleGroup){
12213                return;
12214            }
12215            var g = groups[btn.toggleGroup];
12216            if(!g){
12217                g = groups[btn.toggleGroup] = [];
12218            }
12219            g.push(btn);
12220            btn.on("toggle", toggleGroup);
12221        },
12222        
12223        unregister : function(btn){
12224            if(!btn.toggleGroup){
12225                return;
12226            }
12227            var g = groups[btn.toggleGroup];
12228            if(g){
12229                g.remove(btn);
12230                btn.un("toggle", toggleGroup);
12231            }
12232        }
12233    };
12234 }();/*
12235  * Based on:
12236  * Ext JS Library 1.1.1
12237  * Copyright(c) 2006-2007, Ext JS, LLC.
12238  *
12239  * Originally Released Under LGPL - original licence link has changed is not relivant.
12240  *
12241  * Fork - LGPL
12242  * <script type="text/javascript">
12243  */
12244  
12245 /**
12246  * @class Roo.SplitButton
12247  * @extends Roo.Button
12248  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12249  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12250  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12251  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12252  * @cfg {String} arrowTooltip The title attribute of the arrow
12253  * @constructor
12254  * Create a new menu button
12255  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12256  * @param {Object} config The config object
12257  */
12258 Roo.SplitButton = function(renderTo, config){
12259     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12260     /**
12261      * @event arrowclick
12262      * Fires when this button's arrow is clicked
12263      * @param {SplitButton} this
12264      * @param {EventObject} e The click event
12265      */
12266     this.addEvents({"arrowclick":true});
12267 };
12268
12269 Roo.extend(Roo.SplitButton, Roo.Button, {
12270     render : function(renderTo){
12271         // this is one sweet looking template!
12272         var tpl = new Roo.Template(
12273             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12274             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12275             '<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>',
12276             "</tbody></table></td><td>",
12277             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12278             '<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>',
12279             "</tbody></table></td></tr></table>"
12280         );
12281         var btn = tpl.append(renderTo, [this.text, this.type], true);
12282         var btnEl = btn.child("button");
12283         if(this.cls){
12284             btn.addClass(this.cls);
12285         }
12286         if(this.icon){
12287             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12288         }
12289         if(this.iconCls){
12290             btnEl.addClass(this.iconCls);
12291             if(!this.cls){
12292                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12293             }
12294         }
12295         this.el = btn;
12296         if(this.handleMouseEvents){
12297             btn.on("mouseover", this.onMouseOver, this);
12298             btn.on("mouseout", this.onMouseOut, this);
12299             btn.on("mousedown", this.onMouseDown, this);
12300             btn.on("mouseup", this.onMouseUp, this);
12301         }
12302         btn.on(this.clickEvent, this.onClick, this);
12303         if(this.tooltip){
12304             if(typeof this.tooltip == 'object'){
12305                 Roo.QuickTips.tips(Roo.apply({
12306                       target: btnEl.id
12307                 }, this.tooltip));
12308             } else {
12309                 btnEl.dom[this.tooltipType] = this.tooltip;
12310             }
12311         }
12312         if(this.arrowTooltip){
12313             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12314         }
12315         if(this.hidden){
12316             this.hide();
12317         }
12318         if(this.disabled){
12319             this.disable();
12320         }
12321         if(this.pressed){
12322             this.el.addClass("x-btn-pressed");
12323         }
12324         if(Roo.isIE && !Roo.isIE7){
12325             this.autoWidth.defer(1, this);
12326         }else{
12327             this.autoWidth();
12328         }
12329         if(this.menu){
12330             this.menu.on("show", this.onMenuShow, this);
12331             this.menu.on("hide", this.onMenuHide, this);
12332         }
12333         this.fireEvent('render', this);
12334     },
12335
12336     // private
12337     autoWidth : function(){
12338         if(this.el){
12339             var tbl = this.el.child("table:first");
12340             var tbl2 = this.el.child("table:last");
12341             this.el.setWidth("auto");
12342             tbl.setWidth("auto");
12343             if(Roo.isIE7 && Roo.isStrict){
12344                 var ib = this.el.child('button:first');
12345                 if(ib && ib.getWidth() > 20){
12346                     ib.clip();
12347                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12348                 }
12349             }
12350             if(this.minWidth){
12351                 if(this.hidden){
12352                     this.el.beginMeasure();
12353                 }
12354                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12355                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12356                 }
12357                 if(this.hidden){
12358                     this.el.endMeasure();
12359                 }
12360             }
12361             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12362         } 
12363     },
12364     /**
12365      * Sets this button's click handler
12366      * @param {Function} handler The function to call when the button is clicked
12367      * @param {Object} scope (optional) Scope for the function passed above
12368      */
12369     setHandler : function(handler, scope){
12370         this.handler = handler;
12371         this.scope = scope;  
12372     },
12373     
12374     /**
12375      * Sets this button's arrow click handler
12376      * @param {Function} handler The function to call when the arrow is clicked
12377      * @param {Object} scope (optional) Scope for the function passed above
12378      */
12379     setArrowHandler : function(handler, scope){
12380         this.arrowHandler = handler;
12381         this.scope = scope;  
12382     },
12383     
12384     /**
12385      * Focus the button
12386      */
12387     focus : function(){
12388         if(this.el){
12389             this.el.child("button:first").focus();
12390         }
12391     },
12392
12393     // private
12394     onClick : function(e){
12395         e.preventDefault();
12396         if(!this.disabled){
12397             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12398                 if(this.menu && !this.menu.isVisible()){
12399                     this.menu.show(this.el, this.menuAlign);
12400                 }
12401                 this.fireEvent("arrowclick", this, e);
12402                 if(this.arrowHandler){
12403                     this.arrowHandler.call(this.scope || this, this, e);
12404                 }
12405             }else{
12406                 this.fireEvent("click", this, e);
12407                 if(this.handler){
12408                     this.handler.call(this.scope || this, this, e);
12409                 }
12410             }
12411         }
12412     },
12413     // private
12414     onMouseDown : function(e){
12415         if(!this.disabled){
12416             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12417         }
12418     },
12419     // private
12420     onMouseUp : function(e){
12421         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12422     }   
12423 });
12424
12425
12426 // backwards compat
12427 Roo.MenuButton = Roo.SplitButton;/*
12428  * Based on:
12429  * Ext JS Library 1.1.1
12430  * Copyright(c) 2006-2007, Ext JS, LLC.
12431  *
12432  * Originally Released Under LGPL - original licence link has changed is not relivant.
12433  *
12434  * Fork - LGPL
12435  * <script type="text/javascript">
12436  */
12437
12438 /**
12439  * @class Roo.Toolbar
12440  * Basic Toolbar class.
12441  * @constructor
12442  * Creates a new Toolbar
12443  * @param {Object} container The config object
12444  */ 
12445 Roo.Toolbar = function(container, buttons, config)
12446 {
12447     /// old consturctor format still supported..
12448     if(container instanceof Array){ // omit the container for later rendering
12449         buttons = container;
12450         config = buttons;
12451         container = null;
12452     }
12453     if (typeof(container) == 'object' && container.xtype) {
12454         config = container;
12455         container = config.container;
12456         buttons = config.buttons || []; // not really - use items!!
12457     }
12458     var xitems = [];
12459     if (config && config.items) {
12460         xitems = config.items;
12461         delete config.items;
12462     }
12463     Roo.apply(this, config);
12464     this.buttons = buttons;
12465     
12466     if(container){
12467         this.render(container);
12468     }
12469     this.xitems = xitems;
12470     Roo.each(xitems, function(b) {
12471         this.add(b);
12472     }, this);
12473     
12474 };
12475
12476 Roo.Toolbar.prototype = {
12477     /**
12478      * @cfg {Array} items
12479      * array of button configs or elements to add (will be converted to a MixedCollection)
12480      */
12481     
12482     /**
12483      * @cfg {String/HTMLElement/Element} container
12484      * The id or element that will contain the toolbar
12485      */
12486     // private
12487     render : function(ct){
12488         this.el = Roo.get(ct);
12489         if(this.cls){
12490             this.el.addClass(this.cls);
12491         }
12492         // using a table allows for vertical alignment
12493         // 100% width is needed by Safari...
12494         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12495         this.tr = this.el.child("tr", true);
12496         var autoId = 0;
12497         this.items = new Roo.util.MixedCollection(false, function(o){
12498             return o.id || ("item" + (++autoId));
12499         });
12500         if(this.buttons){
12501             this.add.apply(this, this.buttons);
12502             delete this.buttons;
12503         }
12504     },
12505
12506     /**
12507      * Adds element(s) to the toolbar -- this function takes a variable number of 
12508      * arguments of mixed type and adds them to the toolbar.
12509      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12510      * <ul>
12511      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12512      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12513      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12514      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12515      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12516      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12517      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12518      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12519      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12520      * </ul>
12521      * @param {Mixed} arg2
12522      * @param {Mixed} etc.
12523      */
12524     add : function(){
12525         var a = arguments, l = a.length;
12526         for(var i = 0; i < l; i++){
12527             this._add(a[i]);
12528         }
12529     },
12530     // private..
12531     _add : function(el) {
12532         
12533         if (el.xtype) {
12534             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12535         }
12536         
12537         if (el.applyTo){ // some kind of form field
12538             return this.addField(el);
12539         } 
12540         if (el.render){ // some kind of Toolbar.Item
12541             return this.addItem(el);
12542         }
12543         if (typeof el == "string"){ // string
12544             if(el == "separator" || el == "-"){
12545                 return this.addSeparator();
12546             }
12547             if (el == " "){
12548                 return this.addSpacer();
12549             }
12550             if(el == "->"){
12551                 return this.addFill();
12552             }
12553             return this.addText(el);
12554             
12555         }
12556         if(el.tagName){ // element
12557             return this.addElement(el);
12558         }
12559         if(typeof el == "object"){ // must be button config?
12560             return this.addButton(el);
12561         }
12562         // and now what?!?!
12563         return false;
12564         
12565     },
12566     
12567     /**
12568      * Add an Xtype element
12569      * @param {Object} xtype Xtype Object
12570      * @return {Object} created Object
12571      */
12572     addxtype : function(e){
12573         return this.add(e);  
12574     },
12575     
12576     /**
12577      * Returns the Element for this toolbar.
12578      * @return {Roo.Element}
12579      */
12580     getEl : function(){
12581         return this.el;  
12582     },
12583     
12584     /**
12585      * Adds a separator
12586      * @return {Roo.Toolbar.Item} The separator item
12587      */
12588     addSeparator : function(){
12589         return this.addItem(new Roo.Toolbar.Separator());
12590     },
12591
12592     /**
12593      * Adds a spacer element
12594      * @return {Roo.Toolbar.Spacer} The spacer item
12595      */
12596     addSpacer : function(){
12597         return this.addItem(new Roo.Toolbar.Spacer());
12598     },
12599
12600     /**
12601      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12602      * @return {Roo.Toolbar.Fill} The fill item
12603      */
12604     addFill : function(){
12605         return this.addItem(new Roo.Toolbar.Fill());
12606     },
12607
12608     /**
12609      * Adds any standard HTML element to the toolbar
12610      * @param {String/HTMLElement/Element} el The element or id of the element to add
12611      * @return {Roo.Toolbar.Item} The element's item
12612      */
12613     addElement : function(el){
12614         return this.addItem(new Roo.Toolbar.Item(el));
12615     },
12616     /**
12617      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12618      * @type Roo.util.MixedCollection  
12619      */
12620     items : false,
12621      
12622     /**
12623      * Adds any Toolbar.Item or subclass
12624      * @param {Roo.Toolbar.Item} item
12625      * @return {Roo.Toolbar.Item} The item
12626      */
12627     addItem : function(item){
12628         var td = this.nextBlock();
12629         item.render(td);
12630         this.items.add(item);
12631         return item;
12632     },
12633     
12634     /**
12635      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12636      * @param {Object/Array} config A button config or array of configs
12637      * @return {Roo.Toolbar.Button/Array}
12638      */
12639     addButton : function(config){
12640         if(config instanceof Array){
12641             var buttons = [];
12642             for(var i = 0, len = config.length; i < len; i++) {
12643                 buttons.push(this.addButton(config[i]));
12644             }
12645             return buttons;
12646         }
12647         var b = config;
12648         if(!(config instanceof Roo.Toolbar.Button)){
12649             b = config.split ?
12650                 new Roo.Toolbar.SplitButton(config) :
12651                 new Roo.Toolbar.Button(config);
12652         }
12653         var td = this.nextBlock();
12654         b.render(td);
12655         this.items.add(b);
12656         return b;
12657     },
12658     
12659     /**
12660      * Adds text to the toolbar
12661      * @param {String} text The text to add
12662      * @return {Roo.Toolbar.Item} The element's item
12663      */
12664     addText : function(text){
12665         return this.addItem(new Roo.Toolbar.TextItem(text));
12666     },
12667     
12668     /**
12669      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12670      * @param {Number} index The index where the item is to be inserted
12671      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12672      * @return {Roo.Toolbar.Button/Item}
12673      */
12674     insertButton : function(index, item){
12675         if(item instanceof Array){
12676             var buttons = [];
12677             for(var i = 0, len = item.length; i < len; i++) {
12678                buttons.push(this.insertButton(index + i, item[i]));
12679             }
12680             return buttons;
12681         }
12682         if (!(item instanceof Roo.Toolbar.Button)){
12683            item = new Roo.Toolbar.Button(item);
12684         }
12685         var td = document.createElement("td");
12686         this.tr.insertBefore(td, this.tr.childNodes[index]);
12687         item.render(td);
12688         this.items.insert(index, item);
12689         return item;
12690     },
12691     
12692     /**
12693      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12694      * @param {Object} config
12695      * @return {Roo.Toolbar.Item} The element's item
12696      */
12697     addDom : function(config, returnEl){
12698         var td = this.nextBlock();
12699         Roo.DomHelper.overwrite(td, config);
12700         var ti = new Roo.Toolbar.Item(td.firstChild);
12701         ti.render(td);
12702         this.items.add(ti);
12703         return ti;
12704     },
12705
12706     /**
12707      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12708      * @type Roo.util.MixedCollection  
12709      */
12710     fields : false,
12711     
12712     /**
12713      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12714      * Note: the field should not have been rendered yet. For a field that has already been
12715      * rendered, use {@link #addElement}.
12716      * @param {Roo.form.Field} field
12717      * @return {Roo.ToolbarItem}
12718      */
12719      
12720       
12721     addField : function(field) {
12722         if (!this.fields) {
12723             var autoId = 0;
12724             this.fields = new Roo.util.MixedCollection(false, function(o){
12725                 return o.id || ("item" + (++autoId));
12726             });
12727
12728         }
12729         
12730         var td = this.nextBlock();
12731         field.render(td);
12732         var ti = new Roo.Toolbar.Item(td.firstChild);
12733         ti.render(td);
12734         this.items.add(ti);
12735         this.fields.add(field);
12736         return ti;
12737     },
12738     /**
12739      * Hide the toolbar
12740      * @method hide
12741      */
12742      
12743       
12744     hide : function()
12745     {
12746         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12747         this.el.child('div').hide();
12748     },
12749     /**
12750      * Show the toolbar
12751      * @method show
12752      */
12753     show : function()
12754     {
12755         this.el.child('div').show();
12756     },
12757       
12758     // private
12759     nextBlock : function(){
12760         var td = document.createElement("td");
12761         this.tr.appendChild(td);
12762         return td;
12763     },
12764
12765     // private
12766     destroy : function(){
12767         if(this.items){ // rendered?
12768             Roo.destroy.apply(Roo, this.items.items);
12769         }
12770         if(this.fields){ // rendered?
12771             Roo.destroy.apply(Roo, this.fields.items);
12772         }
12773         Roo.Element.uncache(this.el, this.tr);
12774     }
12775 };
12776
12777 /**
12778  * @class Roo.Toolbar.Item
12779  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12780  * @constructor
12781  * Creates a new Item
12782  * @param {HTMLElement} el 
12783  */
12784 Roo.Toolbar.Item = function(el){
12785     this.el = Roo.getDom(el);
12786     this.id = Roo.id(this.el);
12787     this.hidden = false;
12788 };
12789
12790 Roo.Toolbar.Item.prototype = {
12791     
12792     /**
12793      * Get this item's HTML Element
12794      * @return {HTMLElement}
12795      */
12796     getEl : function(){
12797        return this.el;  
12798     },
12799
12800     // private
12801     render : function(td){
12802         this.td = td;
12803         td.appendChild(this.el);
12804     },
12805     
12806     /**
12807      * Removes and destroys this item.
12808      */
12809     destroy : function(){
12810         this.td.parentNode.removeChild(this.td);
12811     },
12812     
12813     /**
12814      * Shows this item.
12815      */
12816     show: function(){
12817         this.hidden = false;
12818         this.td.style.display = "";
12819     },
12820     
12821     /**
12822      * Hides this item.
12823      */
12824     hide: function(){
12825         this.hidden = true;
12826         this.td.style.display = "none";
12827     },
12828     
12829     /**
12830      * Convenience function for boolean show/hide.
12831      * @param {Boolean} visible true to show/false to hide
12832      */
12833     setVisible: function(visible){
12834         if(visible) {
12835             this.show();
12836         }else{
12837             this.hide();
12838         }
12839     },
12840     
12841     /**
12842      * Try to focus this item.
12843      */
12844     focus : function(){
12845         Roo.fly(this.el).focus();
12846     },
12847     
12848     /**
12849      * Disables this item.
12850      */
12851     disable : function(){
12852         Roo.fly(this.td).addClass("x-item-disabled");
12853         this.disabled = true;
12854         this.el.disabled = true;
12855     },
12856     
12857     /**
12858      * Enables this item.
12859      */
12860     enable : function(){
12861         Roo.fly(this.td).removeClass("x-item-disabled");
12862         this.disabled = false;
12863         this.el.disabled = false;
12864     }
12865 };
12866
12867
12868 /**
12869  * @class Roo.Toolbar.Separator
12870  * @extends Roo.Toolbar.Item
12871  * A simple toolbar separator class
12872  * @constructor
12873  * Creates a new Separator
12874  */
12875 Roo.Toolbar.Separator = function(){
12876     var s = document.createElement("span");
12877     s.className = "ytb-sep";
12878     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12879 };
12880 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12881     enable:Roo.emptyFn,
12882     disable:Roo.emptyFn,
12883     focus:Roo.emptyFn
12884 });
12885
12886 /**
12887  * @class Roo.Toolbar.Spacer
12888  * @extends Roo.Toolbar.Item
12889  * A simple element that adds extra horizontal space to a toolbar.
12890  * @constructor
12891  * Creates a new Spacer
12892  */
12893 Roo.Toolbar.Spacer = function(){
12894     var s = document.createElement("div");
12895     s.className = "ytb-spacer";
12896     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12897 };
12898 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12899     enable:Roo.emptyFn,
12900     disable:Roo.emptyFn,
12901     focus:Roo.emptyFn
12902 });
12903
12904 /**
12905  * @class Roo.Toolbar.Fill
12906  * @extends Roo.Toolbar.Spacer
12907  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12908  * @constructor
12909  * Creates a new Spacer
12910  */
12911 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12912     // private
12913     render : function(td){
12914         td.style.width = '100%';
12915         Roo.Toolbar.Fill.superclass.render.call(this, td);
12916     }
12917 });
12918
12919 /**
12920  * @class Roo.Toolbar.TextItem
12921  * @extends Roo.Toolbar.Item
12922  * A simple class that renders text directly into a toolbar.
12923  * @constructor
12924  * Creates a new TextItem
12925  * @param {String} text
12926  */
12927 Roo.Toolbar.TextItem = function(text){
12928     if (typeof(text) == 'object') {
12929         text = text.text;
12930     }
12931     var s = document.createElement("span");
12932     s.className = "ytb-text";
12933     s.innerHTML = text;
12934     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12935 };
12936 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12937     enable:Roo.emptyFn,
12938     disable:Roo.emptyFn,
12939     focus:Roo.emptyFn
12940 });
12941
12942 /**
12943  * @class Roo.Toolbar.Button
12944  * @extends Roo.Button
12945  * A button that renders into a toolbar.
12946  * @constructor
12947  * Creates a new Button
12948  * @param {Object} config A standard {@link Roo.Button} config object
12949  */
12950 Roo.Toolbar.Button = function(config){
12951     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12952 };
12953 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12954     render : function(td){
12955         this.td = td;
12956         Roo.Toolbar.Button.superclass.render.call(this, td);
12957     },
12958     
12959     /**
12960      * Removes and destroys this button
12961      */
12962     destroy : function(){
12963         Roo.Toolbar.Button.superclass.destroy.call(this);
12964         this.td.parentNode.removeChild(this.td);
12965     },
12966     
12967     /**
12968      * Shows this button
12969      */
12970     show: function(){
12971         this.hidden = false;
12972         this.td.style.display = "";
12973     },
12974     
12975     /**
12976      * Hides this button
12977      */
12978     hide: function(){
12979         this.hidden = true;
12980         this.td.style.display = "none";
12981     },
12982
12983     /**
12984      * Disables this item
12985      */
12986     disable : function(){
12987         Roo.fly(this.td).addClass("x-item-disabled");
12988         this.disabled = true;
12989     },
12990
12991     /**
12992      * Enables this item
12993      */
12994     enable : function(){
12995         Roo.fly(this.td).removeClass("x-item-disabled");
12996         this.disabled = false;
12997     }
12998 });
12999 // backwards compat
13000 Roo.ToolbarButton = Roo.Toolbar.Button;
13001
13002 /**
13003  * @class Roo.Toolbar.SplitButton
13004  * @extends Roo.SplitButton
13005  * A menu button that renders into a toolbar.
13006  * @constructor
13007  * Creates a new SplitButton
13008  * @param {Object} config A standard {@link Roo.SplitButton} config object
13009  */
13010 Roo.Toolbar.SplitButton = function(config){
13011     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
13012 };
13013 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
13014     render : function(td){
13015         this.td = td;
13016         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
13017     },
13018     
13019     /**
13020      * Removes and destroys this button
13021      */
13022     destroy : function(){
13023         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
13024         this.td.parentNode.removeChild(this.td);
13025     },
13026     
13027     /**
13028      * Shows this button
13029      */
13030     show: function(){
13031         this.hidden = false;
13032         this.td.style.display = "";
13033     },
13034     
13035     /**
13036      * Hides this button
13037      */
13038     hide: function(){
13039         this.hidden = true;
13040         this.td.style.display = "none";
13041     }
13042 });
13043
13044 // backwards compat
13045 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
13046  * Based on:
13047  * Ext JS Library 1.1.1
13048  * Copyright(c) 2006-2007, Ext JS, LLC.
13049  *
13050  * Originally Released Under LGPL - original licence link has changed is not relivant.
13051  *
13052  * Fork - LGPL
13053  * <script type="text/javascript">
13054  */
13055  
13056 /**
13057  * @class Roo.PagingToolbar
13058  * @extends Roo.Toolbar
13059  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
13060  * @constructor
13061  * Create a new PagingToolbar
13062  * @param {Object} config The config object
13063  */
13064 Roo.PagingToolbar = function(el, ds, config)
13065 {
13066     // old args format still supported... - xtype is prefered..
13067     if (typeof(el) == 'object' && el.xtype) {
13068         // created from xtype...
13069         config = el;
13070         ds = el.dataSource;
13071         el = config.container;
13072     }
13073     var items = [];
13074     if (config.items) {
13075         items = config.items;
13076         config.items = [];
13077     }
13078     
13079     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13080     this.ds = ds;
13081     this.cursor = 0;
13082     this.renderButtons(this.el);
13083     this.bind(ds);
13084     
13085     // supprot items array.
13086    
13087     Roo.each(items, function(e) {
13088         this.add(Roo.factory(e));
13089     },this);
13090     
13091 };
13092
13093 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13094     /**
13095      * @cfg {Roo.data.Store} dataSource
13096      * The underlying data store providing the paged data
13097      */
13098     /**
13099      * @cfg {String/HTMLElement/Element} container
13100      * container The id or element that will contain the toolbar
13101      */
13102     /**
13103      * @cfg {Boolean} displayInfo
13104      * True to display the displayMsg (defaults to false)
13105      */
13106     /**
13107      * @cfg {Number} pageSize
13108      * The number of records to display per page (defaults to 20)
13109      */
13110     pageSize: 20,
13111     /**
13112      * @cfg {String} displayMsg
13113      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13114      */
13115     displayMsg : 'Displaying {0} - {1} of {2}',
13116     /**
13117      * @cfg {String} emptyMsg
13118      * The message to display when no records are found (defaults to "No data to display")
13119      */
13120     emptyMsg : 'No data to display',
13121     /**
13122      * Customizable piece of the default paging text (defaults to "Page")
13123      * @type String
13124      */
13125     beforePageText : "Page",
13126     /**
13127      * Customizable piece of the default paging text (defaults to "of %0")
13128      * @type String
13129      */
13130     afterPageText : "of {0}",
13131     /**
13132      * Customizable piece of the default paging text (defaults to "First Page")
13133      * @type String
13134      */
13135     firstText : "First Page",
13136     /**
13137      * Customizable piece of the default paging text (defaults to "Previous Page")
13138      * @type String
13139      */
13140     prevText : "Previous Page",
13141     /**
13142      * Customizable piece of the default paging text (defaults to "Next Page")
13143      * @type String
13144      */
13145     nextText : "Next Page",
13146     /**
13147      * Customizable piece of the default paging text (defaults to "Last Page")
13148      * @type String
13149      */
13150     lastText : "Last Page",
13151     /**
13152      * Customizable piece of the default paging text (defaults to "Refresh")
13153      * @type String
13154      */
13155     refreshText : "Refresh",
13156
13157     // private
13158     renderButtons : function(el){
13159         Roo.PagingToolbar.superclass.render.call(this, el);
13160         this.first = this.addButton({
13161             tooltip: this.firstText,
13162             cls: "x-btn-icon x-grid-page-first",
13163             disabled: true,
13164             handler: this.onClick.createDelegate(this, ["first"])
13165         });
13166         this.prev = this.addButton({
13167             tooltip: this.prevText,
13168             cls: "x-btn-icon x-grid-page-prev",
13169             disabled: true,
13170             handler: this.onClick.createDelegate(this, ["prev"])
13171         });
13172         //this.addSeparator();
13173         this.add(this.beforePageText);
13174         this.field = Roo.get(this.addDom({
13175            tag: "input",
13176            type: "text",
13177            size: "3",
13178            value: "1",
13179            cls: "x-grid-page-number"
13180         }).el);
13181         this.field.on("keydown", this.onPagingKeydown, this);
13182         this.field.on("focus", function(){this.dom.select();});
13183         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13184         this.field.setHeight(18);
13185         //this.addSeparator();
13186         this.next = this.addButton({
13187             tooltip: this.nextText,
13188             cls: "x-btn-icon x-grid-page-next",
13189             disabled: true,
13190             handler: this.onClick.createDelegate(this, ["next"])
13191         });
13192         this.last = this.addButton({
13193             tooltip: this.lastText,
13194             cls: "x-btn-icon x-grid-page-last",
13195             disabled: true,
13196             handler: this.onClick.createDelegate(this, ["last"])
13197         });
13198         //this.addSeparator();
13199         this.loading = this.addButton({
13200             tooltip: this.refreshText,
13201             cls: "x-btn-icon x-grid-loading",
13202             handler: this.onClick.createDelegate(this, ["refresh"])
13203         });
13204
13205         if(this.displayInfo){
13206             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13207         }
13208     },
13209
13210     // private
13211     updateInfo : function(){
13212         if(this.displayEl){
13213             var count = this.ds.getCount();
13214             var msg = count == 0 ?
13215                 this.emptyMsg :
13216                 String.format(
13217                     this.displayMsg,
13218                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13219                 );
13220             this.displayEl.update(msg);
13221         }
13222     },
13223
13224     // private
13225     onLoad : function(ds, r, o){
13226        this.cursor = o.params ? o.params.start : 0;
13227        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13228
13229        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13230        this.field.dom.value = ap;
13231        this.first.setDisabled(ap == 1);
13232        this.prev.setDisabled(ap == 1);
13233        this.next.setDisabled(ap == ps);
13234        this.last.setDisabled(ap == ps);
13235        this.loading.enable();
13236        this.updateInfo();
13237     },
13238
13239     // private
13240     getPageData : function(){
13241         var total = this.ds.getTotalCount();
13242         return {
13243             total : total,
13244             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13245             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13246         };
13247     },
13248
13249     // private
13250     onLoadError : function(){
13251         this.loading.enable();
13252     },
13253
13254     // private
13255     onPagingKeydown : function(e){
13256         var k = e.getKey();
13257         var d = this.getPageData();
13258         if(k == e.RETURN){
13259             var v = this.field.dom.value, pageNum;
13260             if(!v || isNaN(pageNum = parseInt(v, 10))){
13261                 this.field.dom.value = d.activePage;
13262                 return;
13263             }
13264             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13265             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13266             e.stopEvent();
13267         }
13268         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))
13269         {
13270           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13271           this.field.dom.value = pageNum;
13272           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13273           e.stopEvent();
13274         }
13275         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13276         {
13277           var v = this.field.dom.value, pageNum; 
13278           var increment = (e.shiftKey) ? 10 : 1;
13279           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13280             increment *= -1;
13281           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13282             this.field.dom.value = d.activePage;
13283             return;
13284           }
13285           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13286           {
13287             this.field.dom.value = parseInt(v, 10) + increment;
13288             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13289             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13290           }
13291           e.stopEvent();
13292         }
13293     },
13294
13295     // private
13296     beforeLoad : function(){
13297         if(this.loading){
13298             this.loading.disable();
13299         }
13300     },
13301
13302     // private
13303     onClick : function(which){
13304         var ds = this.ds;
13305         switch(which){
13306             case "first":
13307                 ds.load({params:{start: 0, limit: this.pageSize}});
13308             break;
13309             case "prev":
13310                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13311             break;
13312             case "next":
13313                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13314             break;
13315             case "last":
13316                 var total = ds.getTotalCount();
13317                 var extra = total % this.pageSize;
13318                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13319                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13320             break;
13321             case "refresh":
13322                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13323             break;
13324         }
13325     },
13326
13327     /**
13328      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13329      * @param {Roo.data.Store} store The data store to unbind
13330      */
13331     unbind : function(ds){
13332         ds.un("beforeload", this.beforeLoad, this);
13333         ds.un("load", this.onLoad, this);
13334         ds.un("loadexception", this.onLoadError, this);
13335         ds.un("remove", this.updateInfo, this);
13336         ds.un("add", this.updateInfo, this);
13337         this.ds = undefined;
13338     },
13339
13340     /**
13341      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13342      * @param {Roo.data.Store} store The data store to bind
13343      */
13344     bind : function(ds){
13345         ds.on("beforeload", this.beforeLoad, this);
13346         ds.on("load", this.onLoad, this);
13347         ds.on("loadexception", this.onLoadError, this);
13348         ds.on("remove", this.updateInfo, this);
13349         ds.on("add", this.updateInfo, this);
13350         this.ds = ds;
13351     }
13352 });/*
13353  * Based on:
13354  * Ext JS Library 1.1.1
13355  * Copyright(c) 2006-2007, Ext JS, LLC.
13356  *
13357  * Originally Released Under LGPL - original licence link has changed is not relivant.
13358  *
13359  * Fork - LGPL
13360  * <script type="text/javascript">
13361  */
13362
13363 /**
13364  * @class Roo.Resizable
13365  * @extends Roo.util.Observable
13366  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13367  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13368  * 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
13369  * the element will be wrapped for you automatically.</p>
13370  * <p>Here is the list of valid resize handles:</p>
13371  * <pre>
13372 Value   Description
13373 ------  -------------------
13374  'n'     north
13375  's'     south
13376  'e'     east
13377  'w'     west
13378  'nw'    northwest
13379  'sw'    southwest
13380  'se'    southeast
13381  'ne'    northeast
13382  'hd'    horizontal drag
13383  'all'   all
13384 </pre>
13385  * <p>Here's an example showing the creation of a typical Resizable:</p>
13386  * <pre><code>
13387 var resizer = new Roo.Resizable("element-id", {
13388     handles: 'all',
13389     minWidth: 200,
13390     minHeight: 100,
13391     maxWidth: 500,
13392     maxHeight: 400,
13393     pinned: true
13394 });
13395 resizer.on("resize", myHandler);
13396 </code></pre>
13397  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13398  * resizer.east.setDisplayed(false);</p>
13399  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13400  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13401  * resize operation's new size (defaults to [0, 0])
13402  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13403  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13404  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13405  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13406  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13407  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13408  * @cfg {Number} width The width of the element in pixels (defaults to null)
13409  * @cfg {Number} height The height of the element in pixels (defaults to null)
13410  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13411  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13412  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13413  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13414  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13415  * in favor of the handles config option (defaults to false)
13416  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13417  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13418  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13419  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13420  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13421  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13422  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13423  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13424  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13425  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13426  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13427  * @constructor
13428  * Create a new resizable component
13429  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13430  * @param {Object} config configuration options
13431   */
13432 Roo.Resizable = function(el, config)
13433 {
13434     this.el = Roo.get(el);
13435
13436     if(config && config.wrap){
13437         config.resizeChild = this.el;
13438         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13439         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13440         this.el.setStyle("overflow", "hidden");
13441         this.el.setPositioning(config.resizeChild.getPositioning());
13442         config.resizeChild.clearPositioning();
13443         if(!config.width || !config.height){
13444             var csize = config.resizeChild.getSize();
13445             this.el.setSize(csize.width, csize.height);
13446         }
13447         if(config.pinned && !config.adjustments){
13448             config.adjustments = "auto";
13449         }
13450     }
13451
13452     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13453     this.proxy.unselectable();
13454     this.proxy.enableDisplayMode('block');
13455
13456     Roo.apply(this, config);
13457
13458     if(this.pinned){
13459         this.disableTrackOver = true;
13460         this.el.addClass("x-resizable-pinned");
13461     }
13462     // if the element isn't positioned, make it relative
13463     var position = this.el.getStyle("position");
13464     if(position != "absolute" && position != "fixed"){
13465         this.el.setStyle("position", "relative");
13466     }
13467     if(!this.handles){ // no handles passed, must be legacy style
13468         this.handles = 's,e,se';
13469         if(this.multiDirectional){
13470             this.handles += ',n,w';
13471         }
13472     }
13473     if(this.handles == "all"){
13474         this.handles = "n s e w ne nw se sw";
13475     }
13476     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13477     var ps = Roo.Resizable.positions;
13478     for(var i = 0, len = hs.length; i < len; i++){
13479         if(hs[i] && ps[hs[i]]){
13480             var pos = ps[hs[i]];
13481             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13482         }
13483     }
13484     // legacy
13485     this.corner = this.southeast;
13486     
13487     // updateBox = the box can move..
13488     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13489         this.updateBox = true;
13490     }
13491
13492     this.activeHandle = null;
13493
13494     if(this.resizeChild){
13495         if(typeof this.resizeChild == "boolean"){
13496             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13497         }else{
13498             this.resizeChild = Roo.get(this.resizeChild, true);
13499         }
13500     }
13501     
13502     if(this.adjustments == "auto"){
13503         var rc = this.resizeChild;
13504         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13505         if(rc && (hw || hn)){
13506             rc.position("relative");
13507             rc.setLeft(hw ? hw.el.getWidth() : 0);
13508             rc.setTop(hn ? hn.el.getHeight() : 0);
13509         }
13510         this.adjustments = [
13511             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13512             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13513         ];
13514     }
13515
13516     if(this.draggable){
13517         this.dd = this.dynamic ?
13518             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13519         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13520     }
13521
13522     // public events
13523     this.addEvents({
13524         /**
13525          * @event beforeresize
13526          * Fired before resize is allowed. Set enabled to false to cancel resize.
13527          * @param {Roo.Resizable} this
13528          * @param {Roo.EventObject} e The mousedown event
13529          */
13530         "beforeresize" : true,
13531         /**
13532          * @event resizing
13533          * Fired a resizing.
13534          * @param {Roo.Resizable} this
13535          * @param {Number} width The new width
13536          * @param {Number} height The new height
13537          * @param {Roo.EventObject} e The mouseup event
13538          */
13539         "resizing" : true,
13540         /**
13541          * @event resize
13542          * Fired after a resize.
13543          * @param {Roo.Resizable} this
13544          * @param {Number} width The new width
13545          * @param {Number} height The new height
13546          * @param {Roo.EventObject} e The mouseup event
13547          */
13548         "resize" : true
13549     });
13550
13551     if(this.width !== null && this.height !== null){
13552         this.resizeTo(this.width, this.height);
13553     }else{
13554         this.updateChildSize();
13555     }
13556     if(Roo.isIE){
13557         this.el.dom.style.zoom = 1;
13558     }
13559     Roo.Resizable.superclass.constructor.call(this);
13560 };
13561
13562 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13563         resizeChild : false,
13564         adjustments : [0, 0],
13565         minWidth : 5,
13566         minHeight : 5,
13567         maxWidth : 10000,
13568         maxHeight : 10000,
13569         enabled : true,
13570         animate : false,
13571         duration : .35,
13572         dynamic : false,
13573         handles : false,
13574         multiDirectional : false,
13575         disableTrackOver : false,
13576         easing : 'easeOutStrong',
13577         widthIncrement : 0,
13578         heightIncrement : 0,
13579         pinned : false,
13580         width : null,
13581         height : null,
13582         preserveRatio : false,
13583         transparent: false,
13584         minX: 0,
13585         minY: 0,
13586         draggable: false,
13587
13588         /**
13589          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13590          */
13591         constrainTo: undefined,
13592         /**
13593          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13594          */
13595         resizeRegion: undefined,
13596
13597
13598     /**
13599      * Perform a manual resize
13600      * @param {Number} width
13601      * @param {Number} height
13602      */
13603     resizeTo : function(width, height){
13604         this.el.setSize(width, height);
13605         this.updateChildSize();
13606         this.fireEvent("resize", this, width, height, null);
13607     },
13608
13609     // private
13610     startSizing : function(e, handle){
13611         this.fireEvent("beforeresize", this, e);
13612         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13613
13614             if(!this.overlay){
13615                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13616                 this.overlay.unselectable();
13617                 this.overlay.enableDisplayMode("block");
13618                 this.overlay.on("mousemove", this.onMouseMove, this);
13619                 this.overlay.on("mouseup", this.onMouseUp, this);
13620             }
13621             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13622
13623             this.resizing = true;
13624             this.startBox = this.el.getBox();
13625             this.startPoint = e.getXY();
13626             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13627                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13628
13629             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13630             this.overlay.show();
13631
13632             if(this.constrainTo) {
13633                 var ct = Roo.get(this.constrainTo);
13634                 this.resizeRegion = ct.getRegion().adjust(
13635                     ct.getFrameWidth('t'),
13636                     ct.getFrameWidth('l'),
13637                     -ct.getFrameWidth('b'),
13638                     -ct.getFrameWidth('r')
13639                 );
13640             }
13641
13642             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13643             this.proxy.show();
13644             this.proxy.setBox(this.startBox);
13645             if(!this.dynamic){
13646                 this.proxy.setStyle('visibility', 'visible');
13647             }
13648         }
13649     },
13650
13651     // private
13652     onMouseDown : function(handle, e){
13653         if(this.enabled){
13654             e.stopEvent();
13655             this.activeHandle = handle;
13656             this.startSizing(e, handle);
13657         }
13658     },
13659
13660     // private
13661     onMouseUp : function(e){
13662         var size = this.resizeElement();
13663         this.resizing = false;
13664         this.handleOut();
13665         this.overlay.hide();
13666         this.proxy.hide();
13667         this.fireEvent("resize", this, size.width, size.height, e);
13668     },
13669
13670     // private
13671     updateChildSize : function(){
13672         
13673         if(this.resizeChild){
13674             var el = this.el;
13675             var child = this.resizeChild;
13676             var adj = this.adjustments;
13677             if(el.dom.offsetWidth){
13678                 var b = el.getSize(true);
13679                 child.setSize(b.width+adj[0], b.height+adj[1]);
13680             }
13681             // Second call here for IE
13682             // The first call enables instant resizing and
13683             // the second call corrects scroll bars if they
13684             // exist
13685             if(Roo.isIE){
13686                 setTimeout(function(){
13687                     if(el.dom.offsetWidth){
13688                         var b = el.getSize(true);
13689                         child.setSize(b.width+adj[0], b.height+adj[1]);
13690                     }
13691                 }, 10);
13692             }
13693         }
13694     },
13695
13696     // private
13697     snap : function(value, inc, min){
13698         if(!inc || !value) return value;
13699         var newValue = value;
13700         var m = value % inc;
13701         if(m > 0){
13702             if(m > (inc/2)){
13703                 newValue = value + (inc-m);
13704             }else{
13705                 newValue = value - m;
13706             }
13707         }
13708         return Math.max(min, newValue);
13709     },
13710
13711     // private
13712     resizeElement : function(){
13713         var box = this.proxy.getBox();
13714         if(this.updateBox){
13715             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13716         }else{
13717             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13718         }
13719         this.updateChildSize();
13720         if(!this.dynamic){
13721             this.proxy.hide();
13722         }
13723         return box;
13724     },
13725
13726     // private
13727     constrain : function(v, diff, m, mx){
13728         if(v - diff < m){
13729             diff = v - m;
13730         }else if(v - diff > mx){
13731             diff = mx - v;
13732         }
13733         return diff;
13734     },
13735
13736     // private
13737     onMouseMove : function(e){
13738         
13739         if(this.enabled){
13740             try{// try catch so if something goes wrong the user doesn't get hung
13741
13742             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13743                 return;
13744             }
13745
13746             //var curXY = this.startPoint;
13747             var curSize = this.curSize || this.startBox;
13748             var x = this.startBox.x, y = this.startBox.y;
13749             var ox = x, oy = y;
13750             var w = curSize.width, h = curSize.height;
13751             var ow = w, oh = h;
13752             var mw = this.minWidth, mh = this.minHeight;
13753             var mxw = this.maxWidth, mxh = this.maxHeight;
13754             var wi = this.widthIncrement;
13755             var hi = this.heightIncrement;
13756
13757             var eventXY = e.getXY();
13758             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13759             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13760
13761             var pos = this.activeHandle.position;
13762
13763             switch(pos){
13764                 case "east":
13765                     w += diffX;
13766                     w = Math.min(Math.max(mw, w), mxw);
13767                     break;
13768              
13769                 case "south":
13770                     h += diffY;
13771                     h = Math.min(Math.max(mh, h), mxh);
13772                     break;
13773                 case "southeast":
13774                     w += diffX;
13775                     h += diffY;
13776                     w = Math.min(Math.max(mw, w), mxw);
13777                     h = Math.min(Math.max(mh, h), mxh);
13778                     break;
13779                 case "north":
13780                     diffY = this.constrain(h, diffY, mh, mxh);
13781                     y += diffY;
13782                     h -= diffY;
13783                     break;
13784                 case "hdrag":
13785                     
13786                     if (wi) {
13787                         var adiffX = Math.abs(diffX);
13788                         var sub = (adiffX % wi); // how much 
13789                         if (sub > (wi/2)) { // far enough to snap
13790                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13791                         } else {
13792                             // remove difference.. 
13793                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13794                         }
13795                     }
13796                     x += diffX;
13797                     x = Math.max(this.minX, x);
13798                     break;
13799                 case "west":
13800                     diffX = this.constrain(w, diffX, mw, mxw);
13801                     x += diffX;
13802                     w -= diffX;
13803                     break;
13804                 case "northeast":
13805                     w += diffX;
13806                     w = Math.min(Math.max(mw, w), mxw);
13807                     diffY = this.constrain(h, diffY, mh, mxh);
13808                     y += diffY;
13809                     h -= diffY;
13810                     break;
13811                 case "northwest":
13812                     diffX = this.constrain(w, diffX, mw, mxw);
13813                     diffY = this.constrain(h, diffY, mh, mxh);
13814                     y += diffY;
13815                     h -= diffY;
13816                     x += diffX;
13817                     w -= diffX;
13818                     break;
13819                case "southwest":
13820                     diffX = this.constrain(w, diffX, mw, mxw);
13821                     h += diffY;
13822                     h = Math.min(Math.max(mh, h), mxh);
13823                     x += diffX;
13824                     w -= diffX;
13825                     break;
13826             }
13827
13828             var sw = this.snap(w, wi, mw);
13829             var sh = this.snap(h, hi, mh);
13830             if(sw != w || sh != h){
13831                 switch(pos){
13832                     case "northeast":
13833                         y -= sh - h;
13834                     break;
13835                     case "north":
13836                         y -= sh - h;
13837                         break;
13838                     case "southwest":
13839                         x -= sw - w;
13840                     break;
13841                     case "west":
13842                         x -= sw - w;
13843                         break;
13844                     case "northwest":
13845                         x -= sw - w;
13846                         y -= sh - h;
13847                     break;
13848                 }
13849                 w = sw;
13850                 h = sh;
13851             }
13852
13853             if(this.preserveRatio){
13854                 switch(pos){
13855                     case "southeast":
13856                     case "east":
13857                         h = oh * (w/ow);
13858                         h = Math.min(Math.max(mh, h), mxh);
13859                         w = ow * (h/oh);
13860                        break;
13861                     case "south":
13862                         w = ow * (h/oh);
13863                         w = Math.min(Math.max(mw, w), mxw);
13864                         h = oh * (w/ow);
13865                         break;
13866                     case "northeast":
13867                         w = ow * (h/oh);
13868                         w = Math.min(Math.max(mw, w), mxw);
13869                         h = oh * (w/ow);
13870                     break;
13871                     case "north":
13872                         var tw = w;
13873                         w = ow * (h/oh);
13874                         w = Math.min(Math.max(mw, w), mxw);
13875                         h = oh * (w/ow);
13876                         x += (tw - w) / 2;
13877                         break;
13878                     case "southwest":
13879                         h = oh * (w/ow);
13880                         h = Math.min(Math.max(mh, h), mxh);
13881                         var tw = w;
13882                         w = ow * (h/oh);
13883                         x += tw - w;
13884                         break;
13885                     case "west":
13886                         var th = h;
13887                         h = oh * (w/ow);
13888                         h = Math.min(Math.max(mh, h), mxh);
13889                         y += (th - h) / 2;
13890                         var tw = w;
13891                         w = ow * (h/oh);
13892                         x += tw - w;
13893                        break;
13894                     case "northwest":
13895                         var tw = w;
13896                         var th = h;
13897                         h = oh * (w/ow);
13898                         h = Math.min(Math.max(mh, h), mxh);
13899                         w = ow * (h/oh);
13900                         y += th - h;
13901                         x += tw - w;
13902                        break;
13903
13904                 }
13905             }
13906             if (pos == 'hdrag') {
13907                 w = ow;
13908             }
13909             this.proxy.setBounds(x, y, w, h);
13910             if(this.dynamic){
13911                 this.resizeElement();
13912             }
13913             }catch(e){}
13914         }
13915         this.fireEvent("resizing", this, x, y, w, h, e);
13916     },
13917
13918     // private
13919     handleOver : function(){
13920         if(this.enabled){
13921             this.el.addClass("x-resizable-over");
13922         }
13923     },
13924
13925     // private
13926     handleOut : function(){
13927         if(!this.resizing){
13928             this.el.removeClass("x-resizable-over");
13929         }
13930     },
13931
13932     /**
13933      * Returns the element this component is bound to.
13934      * @return {Roo.Element}
13935      */
13936     getEl : function(){
13937         return this.el;
13938     },
13939
13940     /**
13941      * Returns the resizeChild element (or null).
13942      * @return {Roo.Element}
13943      */
13944     getResizeChild : function(){
13945         return this.resizeChild;
13946     },
13947     groupHandler : function()
13948     {
13949         
13950     },
13951     /**
13952      * Destroys this resizable. If the element was wrapped and
13953      * removeEl is not true then the element remains.
13954      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13955      */
13956     destroy : function(removeEl){
13957         this.proxy.remove();
13958         if(this.overlay){
13959             this.overlay.removeAllListeners();
13960             this.overlay.remove();
13961         }
13962         var ps = Roo.Resizable.positions;
13963         for(var k in ps){
13964             if(typeof ps[k] != "function" && this[ps[k]]){
13965                 var h = this[ps[k]];
13966                 h.el.removeAllListeners();
13967                 h.el.remove();
13968             }
13969         }
13970         if(removeEl){
13971             this.el.update("");
13972             this.el.remove();
13973         }
13974     }
13975 });
13976
13977 // private
13978 // hash to map config positions to true positions
13979 Roo.Resizable.positions = {
13980     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13981     hd: "hdrag"
13982 };
13983
13984 // private
13985 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13986     if(!this.tpl){
13987         // only initialize the template if resizable is used
13988         var tpl = Roo.DomHelper.createTemplate(
13989             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13990         );
13991         tpl.compile();
13992         Roo.Resizable.Handle.prototype.tpl = tpl;
13993     }
13994     this.position = pos;
13995     this.rz = rz;
13996     // show north drag fro topdra
13997     var handlepos = pos == 'hdrag' ? 'north' : pos;
13998     
13999     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
14000     if (pos == 'hdrag') {
14001         this.el.setStyle('cursor', 'pointer');
14002     }
14003     this.el.unselectable();
14004     if(transparent){
14005         this.el.setOpacity(0);
14006     }
14007     this.el.on("mousedown", this.onMouseDown, this);
14008     if(!disableTrackOver){
14009         this.el.on("mouseover", this.onMouseOver, this);
14010         this.el.on("mouseout", this.onMouseOut, this);
14011     }
14012 };
14013
14014 // private
14015 Roo.Resizable.Handle.prototype = {
14016     afterResize : function(rz){
14017         // do nothing
14018     },
14019     // private
14020     onMouseDown : function(e){
14021         this.rz.onMouseDown(this, e);
14022     },
14023     // private
14024     onMouseOver : function(e){
14025         this.rz.handleOver(this, e);
14026     },
14027     // private
14028     onMouseOut : function(e){
14029         this.rz.handleOut(this, e);
14030     }
14031 };/*
14032  * Based on:
14033  * Ext JS Library 1.1.1
14034  * Copyright(c) 2006-2007, Ext JS, LLC.
14035  *
14036  * Originally Released Under LGPL - original licence link has changed is not relivant.
14037  *
14038  * Fork - LGPL
14039  * <script type="text/javascript">
14040  */
14041
14042 /**
14043  * @class Roo.Editor
14044  * @extends Roo.Component
14045  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
14046  * @constructor
14047  * Create a new Editor
14048  * @param {Roo.form.Field} field The Field object (or descendant)
14049  * @param {Object} config The config object
14050  */
14051 Roo.Editor = function(field, config){
14052     Roo.Editor.superclass.constructor.call(this, config);
14053     this.field = field;
14054     this.addEvents({
14055         /**
14056              * @event beforestartedit
14057              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14058              * false from the handler of this event.
14059              * @param {Editor} this
14060              * @param {Roo.Element} boundEl The underlying element bound to this editor
14061              * @param {Mixed} value The field value being set
14062              */
14063         "beforestartedit" : true,
14064         /**
14065              * @event startedit
14066              * Fires when this editor is displayed
14067              * @param {Roo.Element} boundEl The underlying element bound to this editor
14068              * @param {Mixed} value The starting field value
14069              */
14070         "startedit" : true,
14071         /**
14072              * @event beforecomplete
14073              * Fires after a change has been made to the field, but before the change is reflected in the underlying
14074              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
14075              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
14076              * event will not fire since no edit actually occurred.
14077              * @param {Editor} this
14078              * @param {Mixed} value The current field value
14079              * @param {Mixed} startValue The original field value
14080              */
14081         "beforecomplete" : true,
14082         /**
14083              * @event complete
14084              * Fires after editing is complete and any changed value has been written to the underlying field.
14085              * @param {Editor} this
14086              * @param {Mixed} value The current field value
14087              * @param {Mixed} startValue The original field value
14088              */
14089         "complete" : true,
14090         /**
14091          * @event specialkey
14092          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14093          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14094          * @param {Roo.form.Field} this
14095          * @param {Roo.EventObject} e The event object
14096          */
14097         "specialkey" : true
14098     });
14099 };
14100
14101 Roo.extend(Roo.Editor, Roo.Component, {
14102     /**
14103      * @cfg {Boolean/String} autosize
14104      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14105      * or "height" to adopt the height only (defaults to false)
14106      */
14107     /**
14108      * @cfg {Boolean} revertInvalid
14109      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14110      * validation fails (defaults to true)
14111      */
14112     /**
14113      * @cfg {Boolean} ignoreNoChange
14114      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14115      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14116      * will never be ignored.
14117      */
14118     /**
14119      * @cfg {Boolean} hideEl
14120      * False to keep the bound element visible while the editor is displayed (defaults to true)
14121      */
14122     /**
14123      * @cfg {Mixed} value
14124      * The data value of the underlying field (defaults to "")
14125      */
14126     value : "",
14127     /**
14128      * @cfg {String} alignment
14129      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14130      */
14131     alignment: "c-c?",
14132     /**
14133      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14134      * for bottom-right shadow (defaults to "frame")
14135      */
14136     shadow : "frame",
14137     /**
14138      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14139      */
14140     constrain : false,
14141     /**
14142      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14143      */
14144     completeOnEnter : false,
14145     /**
14146      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14147      */
14148     cancelOnEsc : false,
14149     /**
14150      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14151      */
14152     updateEl : false,
14153
14154     // private
14155     onRender : function(ct, position){
14156         this.el = new Roo.Layer({
14157             shadow: this.shadow,
14158             cls: "x-editor",
14159             parentEl : ct,
14160             shim : this.shim,
14161             shadowOffset:4,
14162             id: this.id,
14163             constrain: this.constrain
14164         });
14165         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14166         if(this.field.msgTarget != 'title'){
14167             this.field.msgTarget = 'qtip';
14168         }
14169         this.field.render(this.el);
14170         if(Roo.isGecko){
14171             this.field.el.dom.setAttribute('autocomplete', 'off');
14172         }
14173         this.field.on("specialkey", this.onSpecialKey, this);
14174         if(this.swallowKeys){
14175             this.field.el.swallowEvent(['keydown','keypress']);
14176         }
14177         this.field.show();
14178         this.field.on("blur", this.onBlur, this);
14179         if(this.field.grow){
14180             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14181         }
14182     },
14183
14184     onSpecialKey : function(field, e)
14185     {
14186         //Roo.log('editor onSpecialKey');
14187         if(this.completeOnEnter && e.getKey() == e.ENTER){
14188             e.stopEvent();
14189             this.completeEdit();
14190             return;
14191         }
14192         // do not fire special key otherwise it might hide close the editor...
14193         if(e.getKey() == e.ENTER){    
14194             return;
14195         }
14196         if(this.cancelOnEsc && e.getKey() == e.ESC){
14197             this.cancelEdit();
14198             return;
14199         } 
14200         this.fireEvent('specialkey', field, e);
14201     
14202     },
14203
14204     /**
14205      * Starts the editing process and shows the editor.
14206      * @param {String/HTMLElement/Element} el The element to edit
14207      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14208       * to the innerHTML of el.
14209      */
14210     startEdit : function(el, value){
14211         if(this.editing){
14212             this.completeEdit();
14213         }
14214         this.boundEl = Roo.get(el);
14215         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14216         if(!this.rendered){
14217             this.render(this.parentEl || document.body);
14218         }
14219         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14220             return;
14221         }
14222         this.startValue = v;
14223         this.field.setValue(v);
14224         if(this.autoSize){
14225             var sz = this.boundEl.getSize();
14226             switch(this.autoSize){
14227                 case "width":
14228                 this.setSize(sz.width,  "");
14229                 break;
14230                 case "height":
14231                 this.setSize("",  sz.height);
14232                 break;
14233                 default:
14234                 this.setSize(sz.width,  sz.height);
14235             }
14236         }
14237         this.el.alignTo(this.boundEl, this.alignment);
14238         this.editing = true;
14239         if(Roo.QuickTips){
14240             Roo.QuickTips.disable();
14241         }
14242         this.show();
14243     },
14244
14245     /**
14246      * Sets the height and width of this editor.
14247      * @param {Number} width The new width
14248      * @param {Number} height The new height
14249      */
14250     setSize : function(w, h){
14251         this.field.setSize(w, h);
14252         if(this.el){
14253             this.el.sync();
14254         }
14255     },
14256
14257     /**
14258      * Realigns the editor to the bound field based on the current alignment config value.
14259      */
14260     realign : function(){
14261         this.el.alignTo(this.boundEl, this.alignment);
14262     },
14263
14264     /**
14265      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14266      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14267      */
14268     completeEdit : function(remainVisible){
14269         if(!this.editing){
14270             return;
14271         }
14272         var v = this.getValue();
14273         if(this.revertInvalid !== false && !this.field.isValid()){
14274             v = this.startValue;
14275             this.cancelEdit(true);
14276         }
14277         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14278             this.editing = false;
14279             this.hide();
14280             return;
14281         }
14282         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14283             this.editing = false;
14284             if(this.updateEl && this.boundEl){
14285                 this.boundEl.update(v);
14286             }
14287             if(remainVisible !== true){
14288                 this.hide();
14289             }
14290             this.fireEvent("complete", this, v, this.startValue);
14291         }
14292     },
14293
14294     // private
14295     onShow : function(){
14296         this.el.show();
14297         if(this.hideEl !== false){
14298             this.boundEl.hide();
14299         }
14300         this.field.show();
14301         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14302             this.fixIEFocus = true;
14303             this.deferredFocus.defer(50, this);
14304         }else{
14305             this.field.focus();
14306         }
14307         this.fireEvent("startedit", this.boundEl, this.startValue);
14308     },
14309
14310     deferredFocus : function(){
14311         if(this.editing){
14312             this.field.focus();
14313         }
14314     },
14315
14316     /**
14317      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14318      * reverted to the original starting value.
14319      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14320      * cancel (defaults to false)
14321      */
14322     cancelEdit : function(remainVisible){
14323         if(this.editing){
14324             this.setValue(this.startValue);
14325             if(remainVisible !== true){
14326                 this.hide();
14327             }
14328         }
14329     },
14330
14331     // private
14332     onBlur : function(){
14333         if(this.allowBlur !== true && this.editing){
14334             this.completeEdit();
14335         }
14336     },
14337
14338     // private
14339     onHide : function(){
14340         if(this.editing){
14341             this.completeEdit();
14342             return;
14343         }
14344         this.field.blur();
14345         if(this.field.collapse){
14346             this.field.collapse();
14347         }
14348         this.el.hide();
14349         if(this.hideEl !== false){
14350             this.boundEl.show();
14351         }
14352         if(Roo.QuickTips){
14353             Roo.QuickTips.enable();
14354         }
14355     },
14356
14357     /**
14358      * Sets the data value of the editor
14359      * @param {Mixed} value Any valid value supported by the underlying field
14360      */
14361     setValue : function(v){
14362         this.field.setValue(v);
14363     },
14364
14365     /**
14366      * Gets the data value of the editor
14367      * @return {Mixed} The data value
14368      */
14369     getValue : function(){
14370         return this.field.getValue();
14371     }
14372 });/*
14373  * Based on:
14374  * Ext JS Library 1.1.1
14375  * Copyright(c) 2006-2007, Ext JS, LLC.
14376  *
14377  * Originally Released Under LGPL - original licence link has changed is not relivant.
14378  *
14379  * Fork - LGPL
14380  * <script type="text/javascript">
14381  */
14382  
14383 /**
14384  * @class Roo.BasicDialog
14385  * @extends Roo.util.Observable
14386  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14387  * <pre><code>
14388 var dlg = new Roo.BasicDialog("my-dlg", {
14389     height: 200,
14390     width: 300,
14391     minHeight: 100,
14392     minWidth: 150,
14393     modal: true,
14394     proxyDrag: true,
14395     shadow: true
14396 });
14397 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14398 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14399 dlg.addButton('Cancel', dlg.hide, dlg);
14400 dlg.show();
14401 </code></pre>
14402   <b>A Dialog should always be a direct child of the body element.</b>
14403  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14404  * @cfg {String} title Default text to display in the title bar (defaults to null)
14405  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14406  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14407  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14408  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14409  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14410  * (defaults to null with no animation)
14411  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14412  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14413  * property for valid values (defaults to 'all')
14414  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14415  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14416  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14417  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14418  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14419  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14420  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14421  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14422  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14423  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14424  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14425  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14426  * draggable = true (defaults to false)
14427  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14428  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14429  * shadow (defaults to false)
14430  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14431  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14432  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14433  * @cfg {Array} buttons Array of buttons
14434  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14435  * @constructor
14436  * Create a new BasicDialog.
14437  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14438  * @param {Object} config Configuration options
14439  */
14440 Roo.BasicDialog = function(el, config){
14441     this.el = Roo.get(el);
14442     var dh = Roo.DomHelper;
14443     if(!this.el && config && config.autoCreate){
14444         if(typeof config.autoCreate == "object"){
14445             if(!config.autoCreate.id){
14446                 config.autoCreate.id = el;
14447             }
14448             this.el = dh.append(document.body,
14449                         config.autoCreate, true);
14450         }else{
14451             this.el = dh.append(document.body,
14452                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14453         }
14454     }
14455     el = this.el;
14456     el.setDisplayed(true);
14457     el.hide = this.hideAction;
14458     this.id = el.id;
14459     el.addClass("x-dlg");
14460
14461     Roo.apply(this, config);
14462
14463     this.proxy = el.createProxy("x-dlg-proxy");
14464     this.proxy.hide = this.hideAction;
14465     this.proxy.setOpacity(.5);
14466     this.proxy.hide();
14467
14468     if(config.width){
14469         el.setWidth(config.width);
14470     }
14471     if(config.height){
14472         el.setHeight(config.height);
14473     }
14474     this.size = el.getSize();
14475     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14476         this.xy = [config.x,config.y];
14477     }else{
14478         this.xy = el.getCenterXY(true);
14479     }
14480     /** The header element @type Roo.Element */
14481     this.header = el.child("> .x-dlg-hd");
14482     /** The body element @type Roo.Element */
14483     this.body = el.child("> .x-dlg-bd");
14484     /** The footer element @type Roo.Element */
14485     this.footer = el.child("> .x-dlg-ft");
14486
14487     if(!this.header){
14488         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14489     }
14490     if(!this.body){
14491         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14492     }
14493
14494     this.header.unselectable();
14495     if(this.title){
14496         this.header.update(this.title);
14497     }
14498     // this element allows the dialog to be focused for keyboard event
14499     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14500     this.focusEl.swallowEvent("click", true);
14501
14502     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14503
14504     // wrap the body and footer for special rendering
14505     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14506     if(this.footer){
14507         this.bwrap.dom.appendChild(this.footer.dom);
14508     }
14509
14510     this.bg = this.el.createChild({
14511         tag: "div", cls:"x-dlg-bg",
14512         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14513     });
14514     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14515
14516
14517     if(this.autoScroll !== false && !this.autoTabs){
14518         this.body.setStyle("overflow", "auto");
14519     }
14520
14521     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14522
14523     if(this.closable !== false){
14524         this.el.addClass("x-dlg-closable");
14525         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14526         this.close.on("click", this.closeClick, this);
14527         this.close.addClassOnOver("x-dlg-close-over");
14528     }
14529     if(this.collapsible !== false){
14530         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14531         this.collapseBtn.on("click", this.collapseClick, this);
14532         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14533         this.header.on("dblclick", this.collapseClick, this);
14534     }
14535     if(this.resizable !== false){
14536         this.el.addClass("x-dlg-resizable");
14537         this.resizer = new Roo.Resizable(el, {
14538             minWidth: this.minWidth || 80,
14539             minHeight:this.minHeight || 80,
14540             handles: this.resizeHandles || "all",
14541             pinned: true
14542         });
14543         this.resizer.on("beforeresize", this.beforeResize, this);
14544         this.resizer.on("resize", this.onResize, this);
14545     }
14546     if(this.draggable !== false){
14547         el.addClass("x-dlg-draggable");
14548         if (!this.proxyDrag) {
14549             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14550         }
14551         else {
14552             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14553         }
14554         dd.setHandleElId(this.header.id);
14555         dd.endDrag = this.endMove.createDelegate(this);
14556         dd.startDrag = this.startMove.createDelegate(this);
14557         dd.onDrag = this.onDrag.createDelegate(this);
14558         dd.scroll = false;
14559         this.dd = dd;
14560     }
14561     if(this.modal){
14562         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14563         this.mask.enableDisplayMode("block");
14564         this.mask.hide();
14565         this.el.addClass("x-dlg-modal");
14566     }
14567     if(this.shadow){
14568         this.shadow = new Roo.Shadow({
14569             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14570             offset : this.shadowOffset
14571         });
14572     }else{
14573         this.shadowOffset = 0;
14574     }
14575     if(Roo.useShims && this.shim !== false){
14576         this.shim = this.el.createShim();
14577         this.shim.hide = this.hideAction;
14578         this.shim.hide();
14579     }else{
14580         this.shim = false;
14581     }
14582     if(this.autoTabs){
14583         this.initTabs();
14584     }
14585     if (this.buttons) { 
14586         var bts= this.buttons;
14587         this.buttons = [];
14588         Roo.each(bts, function(b) {
14589             this.addButton(b);
14590         }, this);
14591     }
14592     
14593     
14594     this.addEvents({
14595         /**
14596          * @event keydown
14597          * Fires when a key is pressed
14598          * @param {Roo.BasicDialog} this
14599          * @param {Roo.EventObject} e
14600          */
14601         "keydown" : true,
14602         /**
14603          * @event move
14604          * Fires when this dialog is moved by the user.
14605          * @param {Roo.BasicDialog} this
14606          * @param {Number} x The new page X
14607          * @param {Number} y The new page Y
14608          */
14609         "move" : true,
14610         /**
14611          * @event resize
14612          * Fires when this dialog is resized by the user.
14613          * @param {Roo.BasicDialog} this
14614          * @param {Number} width The new width
14615          * @param {Number} height The new height
14616          */
14617         "resize" : true,
14618         /**
14619          * @event beforehide
14620          * Fires before this dialog is hidden.
14621          * @param {Roo.BasicDialog} this
14622          */
14623         "beforehide" : true,
14624         /**
14625          * @event hide
14626          * Fires when this dialog is hidden.
14627          * @param {Roo.BasicDialog} this
14628          */
14629         "hide" : true,
14630         /**
14631          * @event beforeshow
14632          * Fires before this dialog is shown.
14633          * @param {Roo.BasicDialog} this
14634          */
14635         "beforeshow" : true,
14636         /**
14637          * @event show
14638          * Fires when this dialog is shown.
14639          * @param {Roo.BasicDialog} this
14640          */
14641         "show" : true
14642     });
14643     el.on("keydown", this.onKeyDown, this);
14644     el.on("mousedown", this.toFront, this);
14645     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14646     this.el.hide();
14647     Roo.DialogManager.register(this);
14648     Roo.BasicDialog.superclass.constructor.call(this);
14649 };
14650
14651 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14652     shadowOffset: Roo.isIE ? 6 : 5,
14653     minHeight: 80,
14654     minWidth: 200,
14655     minButtonWidth: 75,
14656     defaultButton: null,
14657     buttonAlign: "right",
14658     tabTag: 'div',
14659     firstShow: true,
14660
14661     /**
14662      * Sets the dialog title text
14663      * @param {String} text The title text to display
14664      * @return {Roo.BasicDialog} this
14665      */
14666     setTitle : function(text){
14667         this.header.update(text);
14668         return this;
14669     },
14670
14671     // private
14672     closeClick : function(){
14673         this.hide();
14674     },
14675
14676     // private
14677     collapseClick : function(){
14678         this[this.collapsed ? "expand" : "collapse"]();
14679     },
14680
14681     /**
14682      * Collapses the dialog to its minimized state (only the title bar is visible).
14683      * Equivalent to the user clicking the collapse dialog button.
14684      */
14685     collapse : function(){
14686         if(!this.collapsed){
14687             this.collapsed = true;
14688             this.el.addClass("x-dlg-collapsed");
14689             this.restoreHeight = this.el.getHeight();
14690             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14691         }
14692     },
14693
14694     /**
14695      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14696      * clicking the expand dialog button.
14697      */
14698     expand : function(){
14699         if(this.collapsed){
14700             this.collapsed = false;
14701             this.el.removeClass("x-dlg-collapsed");
14702             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14703         }
14704     },
14705
14706     /**
14707      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14708      * @return {Roo.TabPanel} The tabs component
14709      */
14710     initTabs : function(){
14711         var tabs = this.getTabs();
14712         while(tabs.getTab(0)){
14713             tabs.removeTab(0);
14714         }
14715         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14716             var dom = el.dom;
14717             tabs.addTab(Roo.id(dom), dom.title);
14718             dom.title = "";
14719         });
14720         tabs.activate(0);
14721         return tabs;
14722     },
14723
14724     // private
14725     beforeResize : function(){
14726         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14727     },
14728
14729     // private
14730     onResize : function(){
14731         this.refreshSize();
14732         this.syncBodyHeight();
14733         this.adjustAssets();
14734         this.focus();
14735         this.fireEvent("resize", this, this.size.width, this.size.height);
14736     },
14737
14738     // private
14739     onKeyDown : function(e){
14740         if(this.isVisible()){
14741             this.fireEvent("keydown", this, e);
14742         }
14743     },
14744
14745     /**
14746      * Resizes the dialog.
14747      * @param {Number} width
14748      * @param {Number} height
14749      * @return {Roo.BasicDialog} this
14750      */
14751     resizeTo : function(width, height){
14752         this.el.setSize(width, height);
14753         this.size = {width: width, height: height};
14754         this.syncBodyHeight();
14755         if(this.fixedcenter){
14756             this.center();
14757         }
14758         if(this.isVisible()){
14759             this.constrainXY();
14760             this.adjustAssets();
14761         }
14762         this.fireEvent("resize", this, width, height);
14763         return this;
14764     },
14765
14766
14767     /**
14768      * Resizes the dialog to fit the specified content size.
14769      * @param {Number} width
14770      * @param {Number} height
14771      * @return {Roo.BasicDialog} this
14772      */
14773     setContentSize : function(w, h){
14774         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14775         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14776         //if(!this.el.isBorderBox()){
14777             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14778             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14779         //}
14780         if(this.tabs){
14781             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14782             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14783         }
14784         this.resizeTo(w, h);
14785         return this;
14786     },
14787
14788     /**
14789      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14790      * executed in response to a particular key being pressed while the dialog is active.
14791      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14792      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14793      * @param {Function} fn The function to call
14794      * @param {Object} scope (optional) The scope of the function
14795      * @return {Roo.BasicDialog} this
14796      */
14797     addKeyListener : function(key, fn, scope){
14798         var keyCode, shift, ctrl, alt;
14799         if(typeof key == "object" && !(key instanceof Array)){
14800             keyCode = key["key"];
14801             shift = key["shift"];
14802             ctrl = key["ctrl"];
14803             alt = key["alt"];
14804         }else{
14805             keyCode = key;
14806         }
14807         var handler = function(dlg, e){
14808             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14809                 var k = e.getKey();
14810                 if(keyCode instanceof Array){
14811                     for(var i = 0, len = keyCode.length; i < len; i++){
14812                         if(keyCode[i] == k){
14813                           fn.call(scope || window, dlg, k, e);
14814                           return;
14815                         }
14816                     }
14817                 }else{
14818                     if(k == keyCode){
14819                         fn.call(scope || window, dlg, k, e);
14820                     }
14821                 }
14822             }
14823         };
14824         this.on("keydown", handler);
14825         return this;
14826     },
14827
14828     /**
14829      * Returns the TabPanel component (creates it if it doesn't exist).
14830      * Note: If you wish to simply check for the existence of tabs without creating them,
14831      * check for a null 'tabs' property.
14832      * @return {Roo.TabPanel} The tabs component
14833      */
14834     getTabs : function(){
14835         if(!this.tabs){
14836             this.el.addClass("x-dlg-auto-tabs");
14837             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14838             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14839         }
14840         return this.tabs;
14841     },
14842
14843     /**
14844      * Adds a button to the footer section of the dialog.
14845      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14846      * object or a valid Roo.DomHelper element config
14847      * @param {Function} handler The function called when the button is clicked
14848      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14849      * @return {Roo.Button} The new button
14850      */
14851     addButton : function(config, handler, scope){
14852         var dh = Roo.DomHelper;
14853         if(!this.footer){
14854             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14855         }
14856         if(!this.btnContainer){
14857             var tb = this.footer.createChild({
14858
14859                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14860                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14861             }, null, true);
14862             this.btnContainer = tb.firstChild.firstChild.firstChild;
14863         }
14864         var bconfig = {
14865             handler: handler,
14866             scope: scope,
14867             minWidth: this.minButtonWidth,
14868             hideParent:true
14869         };
14870         if(typeof config == "string"){
14871             bconfig.text = config;
14872         }else{
14873             if(config.tag){
14874                 bconfig.dhconfig = config;
14875             }else{
14876                 Roo.apply(bconfig, config);
14877             }
14878         }
14879         var fc = false;
14880         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14881             bconfig.position = Math.max(0, bconfig.position);
14882             fc = this.btnContainer.childNodes[bconfig.position];
14883         }
14884          
14885         var btn = new Roo.Button(
14886             fc ? 
14887                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14888                 : this.btnContainer.appendChild(document.createElement("td")),
14889             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14890             bconfig
14891         );
14892         this.syncBodyHeight();
14893         if(!this.buttons){
14894             /**
14895              * Array of all the buttons that have been added to this dialog via addButton
14896              * @type Array
14897              */
14898             this.buttons = [];
14899         }
14900         this.buttons.push(btn);
14901         return btn;
14902     },
14903
14904     /**
14905      * Sets the default button to be focused when the dialog is displayed.
14906      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14907      * @return {Roo.BasicDialog} this
14908      */
14909     setDefaultButton : function(btn){
14910         this.defaultButton = btn;
14911         return this;
14912     },
14913
14914     // private
14915     getHeaderFooterHeight : function(safe){
14916         var height = 0;
14917         if(this.header){
14918            height += this.header.getHeight();
14919         }
14920         if(this.footer){
14921            var fm = this.footer.getMargins();
14922             height += (this.footer.getHeight()+fm.top+fm.bottom);
14923         }
14924         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14925         height += this.centerBg.getPadding("tb");
14926         return height;
14927     },
14928
14929     // private
14930     syncBodyHeight : function()
14931     {
14932         var bd = this.body, // the text
14933             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
14934             bw = this.bwrap;
14935         var height = this.size.height - this.getHeaderFooterHeight(false);
14936         bd.setHeight(height-bd.getMargins("tb"));
14937         var hh = this.header.getHeight();
14938         var h = this.size.height-hh;
14939         cb.setHeight(h);
14940         
14941         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14942         bw.setHeight(h-cb.getPadding("tb"));
14943         
14944         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14945         bd.setWidth(bw.getWidth(true));
14946         if(this.tabs){
14947             this.tabs.syncHeight();
14948             if(Roo.isIE){
14949                 this.tabs.el.repaint();
14950             }
14951         }
14952     },
14953
14954     /**
14955      * Restores the previous state of the dialog if Roo.state is configured.
14956      * @return {Roo.BasicDialog} this
14957      */
14958     restoreState : function(){
14959         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14960         if(box && box.width){
14961             this.xy = [box.x, box.y];
14962             this.resizeTo(box.width, box.height);
14963         }
14964         return this;
14965     },
14966
14967     // private
14968     beforeShow : function(){
14969         this.expand();
14970         if(this.fixedcenter){
14971             this.xy = this.el.getCenterXY(true);
14972         }
14973         if(this.modal){
14974             Roo.get(document.body).addClass("x-body-masked");
14975             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14976             this.mask.show();
14977         }
14978         this.constrainXY();
14979     },
14980
14981     // private
14982     animShow : function(){
14983         var b = Roo.get(this.animateTarget).getBox();
14984         this.proxy.setSize(b.width, b.height);
14985         this.proxy.setLocation(b.x, b.y);
14986         this.proxy.show();
14987         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14988                     true, .35, this.showEl.createDelegate(this));
14989     },
14990
14991     /**
14992      * Shows the dialog.
14993      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14994      * @return {Roo.BasicDialog} this
14995      */
14996     show : function(animateTarget){
14997         if (this.fireEvent("beforeshow", this) === false){
14998             return;
14999         }
15000         if(this.syncHeightBeforeShow){
15001             this.syncBodyHeight();
15002         }else if(this.firstShow){
15003             this.firstShow = false;
15004             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
15005         }
15006         this.animateTarget = animateTarget || this.animateTarget;
15007         if(!this.el.isVisible()){
15008             this.beforeShow();
15009             if(this.animateTarget && Roo.get(this.animateTarget)){
15010                 this.animShow();
15011             }else{
15012                 this.showEl();
15013             }
15014         }
15015         return this;
15016     },
15017
15018     // private
15019     showEl : function(){
15020         this.proxy.hide();
15021         this.el.setXY(this.xy);
15022         this.el.show();
15023         this.adjustAssets(true);
15024         this.toFront();
15025         this.focus();
15026         // IE peekaboo bug - fix found by Dave Fenwick
15027         if(Roo.isIE){
15028             this.el.repaint();
15029         }
15030         this.fireEvent("show", this);
15031     },
15032
15033     /**
15034      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
15035      * dialog itself will receive focus.
15036      */
15037     focus : function(){
15038         if(this.defaultButton){
15039             this.defaultButton.focus();
15040         }else{
15041             this.focusEl.focus();
15042         }
15043     },
15044
15045     // private
15046     constrainXY : function(){
15047         if(this.constraintoviewport !== false){
15048             if(!this.viewSize){
15049                 if(this.container){
15050                     var s = this.container.getSize();
15051                     this.viewSize = [s.width, s.height];
15052                 }else{
15053                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
15054                 }
15055             }
15056             var s = Roo.get(this.container||document).getScroll();
15057
15058             var x = this.xy[0], y = this.xy[1];
15059             var w = this.size.width, h = this.size.height;
15060             var vw = this.viewSize[0], vh = this.viewSize[1];
15061             // only move it if it needs it
15062             var moved = false;
15063             // first validate right/bottom
15064             if(x + w > vw+s.left){
15065                 x = vw - w;
15066                 moved = true;
15067             }
15068             if(y + h > vh+s.top){
15069                 y = vh - h;
15070                 moved = true;
15071             }
15072             // then make sure top/left isn't negative
15073             if(x < s.left){
15074                 x = s.left;
15075                 moved = true;
15076             }
15077             if(y < s.top){
15078                 y = s.top;
15079                 moved = true;
15080             }
15081             if(moved){
15082                 // cache xy
15083                 this.xy = [x, y];
15084                 if(this.isVisible()){
15085                     this.el.setLocation(x, y);
15086                     this.adjustAssets();
15087                 }
15088             }
15089         }
15090     },
15091
15092     // private
15093     onDrag : function(){
15094         if(!this.proxyDrag){
15095             this.xy = this.el.getXY();
15096             this.adjustAssets();
15097         }
15098     },
15099
15100     // private
15101     adjustAssets : function(doShow){
15102         var x = this.xy[0], y = this.xy[1];
15103         var w = this.size.width, h = this.size.height;
15104         if(doShow === true){
15105             if(this.shadow){
15106                 this.shadow.show(this.el);
15107             }
15108             if(this.shim){
15109                 this.shim.show();
15110             }
15111         }
15112         if(this.shadow && this.shadow.isVisible()){
15113             this.shadow.show(this.el);
15114         }
15115         if(this.shim && this.shim.isVisible()){
15116             this.shim.setBounds(x, y, w, h);
15117         }
15118     },
15119
15120     // private
15121     adjustViewport : function(w, h){
15122         if(!w || !h){
15123             w = Roo.lib.Dom.getViewWidth();
15124             h = Roo.lib.Dom.getViewHeight();
15125         }
15126         // cache the size
15127         this.viewSize = [w, h];
15128         if(this.modal && this.mask.isVisible()){
15129             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15130             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15131         }
15132         if(this.isVisible()){
15133             this.constrainXY();
15134         }
15135     },
15136
15137     /**
15138      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15139      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15140      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15141      */
15142     destroy : function(removeEl){
15143         if(this.isVisible()){
15144             this.animateTarget = null;
15145             this.hide();
15146         }
15147         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15148         if(this.tabs){
15149             this.tabs.destroy(removeEl);
15150         }
15151         Roo.destroy(
15152              this.shim,
15153              this.proxy,
15154              this.resizer,
15155              this.close,
15156              this.mask
15157         );
15158         if(this.dd){
15159             this.dd.unreg();
15160         }
15161         if(this.buttons){
15162            for(var i = 0, len = this.buttons.length; i < len; i++){
15163                this.buttons[i].destroy();
15164            }
15165         }
15166         this.el.removeAllListeners();
15167         if(removeEl === true){
15168             this.el.update("");
15169             this.el.remove();
15170         }
15171         Roo.DialogManager.unregister(this);
15172     },
15173
15174     // private
15175     startMove : function(){
15176         if(this.proxyDrag){
15177             this.proxy.show();
15178         }
15179         if(this.constraintoviewport !== false){
15180             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15181         }
15182     },
15183
15184     // private
15185     endMove : function(){
15186         if(!this.proxyDrag){
15187             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15188         }else{
15189             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15190             this.proxy.hide();
15191         }
15192         this.refreshSize();
15193         this.adjustAssets();
15194         this.focus();
15195         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15196     },
15197
15198     /**
15199      * Brings this dialog to the front of any other visible dialogs
15200      * @return {Roo.BasicDialog} this
15201      */
15202     toFront : function(){
15203         Roo.DialogManager.bringToFront(this);
15204         return this;
15205     },
15206
15207     /**
15208      * Sends this dialog to the back (under) of any other visible dialogs
15209      * @return {Roo.BasicDialog} this
15210      */
15211     toBack : function(){
15212         Roo.DialogManager.sendToBack(this);
15213         return this;
15214     },
15215
15216     /**
15217      * Centers this dialog in the viewport
15218      * @return {Roo.BasicDialog} this
15219      */
15220     center : function(){
15221         var xy = this.el.getCenterXY(true);
15222         this.moveTo(xy[0], xy[1]);
15223         return this;
15224     },
15225
15226     /**
15227      * Moves the dialog's top-left corner to the specified point
15228      * @param {Number} x
15229      * @param {Number} y
15230      * @return {Roo.BasicDialog} this
15231      */
15232     moveTo : function(x, y){
15233         this.xy = [x,y];
15234         if(this.isVisible()){
15235             this.el.setXY(this.xy);
15236             this.adjustAssets();
15237         }
15238         return this;
15239     },
15240
15241     /**
15242      * Aligns the dialog to the specified element
15243      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15244      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15245      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15246      * @return {Roo.BasicDialog} this
15247      */
15248     alignTo : function(element, position, offsets){
15249         this.xy = this.el.getAlignToXY(element, position, offsets);
15250         if(this.isVisible()){
15251             this.el.setXY(this.xy);
15252             this.adjustAssets();
15253         }
15254         return this;
15255     },
15256
15257     /**
15258      * Anchors an element to another element and realigns it when the window is resized.
15259      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15260      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15261      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15262      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15263      * is a number, it is used as the buffer delay (defaults to 50ms).
15264      * @return {Roo.BasicDialog} this
15265      */
15266     anchorTo : function(el, alignment, offsets, monitorScroll){
15267         var action = function(){
15268             this.alignTo(el, alignment, offsets);
15269         };
15270         Roo.EventManager.onWindowResize(action, this);
15271         var tm = typeof monitorScroll;
15272         if(tm != 'undefined'){
15273             Roo.EventManager.on(window, 'scroll', action, this,
15274                 {buffer: tm == 'number' ? monitorScroll : 50});
15275         }
15276         action.call(this);
15277         return this;
15278     },
15279
15280     /**
15281      * Returns true if the dialog is visible
15282      * @return {Boolean}
15283      */
15284     isVisible : function(){
15285         return this.el.isVisible();
15286     },
15287
15288     // private
15289     animHide : function(callback){
15290         var b = Roo.get(this.animateTarget).getBox();
15291         this.proxy.show();
15292         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15293         this.el.hide();
15294         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15295                     this.hideEl.createDelegate(this, [callback]));
15296     },
15297
15298     /**
15299      * Hides the dialog.
15300      * @param {Function} callback (optional) Function to call when the dialog is hidden
15301      * @return {Roo.BasicDialog} this
15302      */
15303     hide : function(callback){
15304         if (this.fireEvent("beforehide", this) === false){
15305             return;
15306         }
15307         if(this.shadow){
15308             this.shadow.hide();
15309         }
15310         if(this.shim) {
15311           this.shim.hide();
15312         }
15313         // sometimes animateTarget seems to get set.. causing problems...
15314         // this just double checks..
15315         if(this.animateTarget && Roo.get(this.animateTarget)) {
15316            this.animHide(callback);
15317         }else{
15318             this.el.hide();
15319             this.hideEl(callback);
15320         }
15321         return this;
15322     },
15323
15324     // private
15325     hideEl : function(callback){
15326         this.proxy.hide();
15327         if(this.modal){
15328             this.mask.hide();
15329             Roo.get(document.body).removeClass("x-body-masked");
15330         }
15331         this.fireEvent("hide", this);
15332         if(typeof callback == "function"){
15333             callback();
15334         }
15335     },
15336
15337     // private
15338     hideAction : function(){
15339         this.setLeft("-10000px");
15340         this.setTop("-10000px");
15341         this.setStyle("visibility", "hidden");
15342     },
15343
15344     // private
15345     refreshSize : function(){
15346         this.size = this.el.getSize();
15347         this.xy = this.el.getXY();
15348         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15349     },
15350
15351     // private
15352     // z-index is managed by the DialogManager and may be overwritten at any time
15353     setZIndex : function(index){
15354         if(this.modal){
15355             this.mask.setStyle("z-index", index);
15356         }
15357         if(this.shim){
15358             this.shim.setStyle("z-index", ++index);
15359         }
15360         if(this.shadow){
15361             this.shadow.setZIndex(++index);
15362         }
15363         this.el.setStyle("z-index", ++index);
15364         if(this.proxy){
15365             this.proxy.setStyle("z-index", ++index);
15366         }
15367         if(this.resizer){
15368             this.resizer.proxy.setStyle("z-index", ++index);
15369         }
15370
15371         this.lastZIndex = index;
15372     },
15373
15374     /**
15375      * Returns the element for this dialog
15376      * @return {Roo.Element} The underlying dialog Element
15377      */
15378     getEl : function(){
15379         return this.el;
15380     }
15381 });
15382
15383 /**
15384  * @class Roo.DialogManager
15385  * Provides global access to BasicDialogs that have been created and
15386  * support for z-indexing (layering) multiple open dialogs.
15387  */
15388 Roo.DialogManager = function(){
15389     var list = {};
15390     var accessList = [];
15391     var front = null;
15392
15393     // private
15394     var sortDialogs = function(d1, d2){
15395         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15396     };
15397
15398     // private
15399     var orderDialogs = function(){
15400         accessList.sort(sortDialogs);
15401         var seed = Roo.DialogManager.zseed;
15402         for(var i = 0, len = accessList.length; i < len; i++){
15403             var dlg = accessList[i];
15404             if(dlg){
15405                 dlg.setZIndex(seed + (i*10));
15406             }
15407         }
15408     };
15409
15410     return {
15411         /**
15412          * The starting z-index for BasicDialogs (defaults to 9000)
15413          * @type Number The z-index value
15414          */
15415         zseed : 9000,
15416
15417         // private
15418         register : function(dlg){
15419             list[dlg.id] = dlg;
15420             accessList.push(dlg);
15421         },
15422
15423         // private
15424         unregister : function(dlg){
15425             delete list[dlg.id];
15426             var i=0;
15427             var len=0;
15428             if(!accessList.indexOf){
15429                 for(  i = 0, len = accessList.length; i < len; i++){
15430                     if(accessList[i] == dlg){
15431                         accessList.splice(i, 1);
15432                         return;
15433                     }
15434                 }
15435             }else{
15436                  i = accessList.indexOf(dlg);
15437                 if(i != -1){
15438                     accessList.splice(i, 1);
15439                 }
15440             }
15441         },
15442
15443         /**
15444          * Gets a registered dialog by id
15445          * @param {String/Object} id The id of the dialog or a dialog
15446          * @return {Roo.BasicDialog} this
15447          */
15448         get : function(id){
15449             return typeof id == "object" ? id : list[id];
15450         },
15451
15452         /**
15453          * Brings the specified dialog to the front
15454          * @param {String/Object} dlg The id of the dialog or a dialog
15455          * @return {Roo.BasicDialog} this
15456          */
15457         bringToFront : function(dlg){
15458             dlg = this.get(dlg);
15459             if(dlg != front){
15460                 front = dlg;
15461                 dlg._lastAccess = new Date().getTime();
15462                 orderDialogs();
15463             }
15464             return dlg;
15465         },
15466
15467         /**
15468          * Sends the specified dialog to the back
15469          * @param {String/Object} dlg The id of the dialog or a dialog
15470          * @return {Roo.BasicDialog} this
15471          */
15472         sendToBack : function(dlg){
15473             dlg = this.get(dlg);
15474             dlg._lastAccess = -(new Date().getTime());
15475             orderDialogs();
15476             return dlg;
15477         },
15478
15479         /**
15480          * Hides all dialogs
15481          */
15482         hideAll : function(){
15483             for(var id in list){
15484                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15485                     list[id].hide();
15486                 }
15487             }
15488         }
15489     };
15490 }();
15491
15492 /**
15493  * @class Roo.LayoutDialog
15494  * @extends Roo.BasicDialog
15495  * Dialog which provides adjustments for working with a layout in a Dialog.
15496  * Add your necessary layout config options to the dialog's config.<br>
15497  * Example usage (including a nested layout):
15498  * <pre><code>
15499 if(!dialog){
15500     dialog = new Roo.LayoutDialog("download-dlg", {
15501         modal: true,
15502         width:600,
15503         height:450,
15504         shadow:true,
15505         minWidth:500,
15506         minHeight:350,
15507         autoTabs:true,
15508         proxyDrag:true,
15509         // layout config merges with the dialog config
15510         center:{
15511             tabPosition: "top",
15512             alwaysShowTabs: true
15513         }
15514     });
15515     dialog.addKeyListener(27, dialog.hide, dialog);
15516     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15517     dialog.addButton("Build It!", this.getDownload, this);
15518
15519     // we can even add nested layouts
15520     var innerLayout = new Roo.BorderLayout("dl-inner", {
15521         east: {
15522             initialSize: 200,
15523             autoScroll:true,
15524             split:true
15525         },
15526         center: {
15527             autoScroll:true
15528         }
15529     });
15530     innerLayout.beginUpdate();
15531     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15532     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15533     innerLayout.endUpdate(true);
15534
15535     var layout = dialog.getLayout();
15536     layout.beginUpdate();
15537     layout.add("center", new Roo.ContentPanel("standard-panel",
15538                         {title: "Download the Source", fitToFrame:true}));
15539     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15540                {title: "Build your own roo.js"}));
15541     layout.getRegion("center").showPanel(sp);
15542     layout.endUpdate();
15543 }
15544 </code></pre>
15545     * @constructor
15546     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15547     * @param {Object} config configuration options
15548   */
15549 Roo.LayoutDialog = function(el, cfg){
15550     
15551     var config=  cfg;
15552     if (typeof(cfg) == 'undefined') {
15553         config = Roo.apply({}, el);
15554         // not sure why we use documentElement here.. - it should always be body.
15555         // IE7 borks horribly if we use documentElement.
15556         // webkit also does not like documentElement - it creates a body element...
15557         el = Roo.get( document.body || document.documentElement ).createChild();
15558         //config.autoCreate = true;
15559     }
15560     
15561     
15562     config.autoTabs = false;
15563     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15564     this.body.setStyle({overflow:"hidden", position:"relative"});
15565     this.layout = new Roo.BorderLayout(this.body.dom, config);
15566     this.layout.monitorWindowResize = false;
15567     this.el.addClass("x-dlg-auto-layout");
15568     // fix case when center region overwrites center function
15569     this.center = Roo.BasicDialog.prototype.center;
15570     this.on("show", this.layout.layout, this.layout, true);
15571     if (config.items) {
15572         var xitems = config.items;
15573         delete config.items;
15574         Roo.each(xitems, this.addxtype, this);
15575     }
15576     
15577     
15578 };
15579 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15580     /**
15581      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15582      * @deprecated
15583      */
15584     endUpdate : function(){
15585         this.layout.endUpdate();
15586     },
15587
15588     /**
15589      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15590      *  @deprecated
15591      */
15592     beginUpdate : function(){
15593         this.layout.beginUpdate();
15594     },
15595
15596     /**
15597      * Get the BorderLayout for this dialog
15598      * @return {Roo.BorderLayout}
15599      */
15600     getLayout : function(){
15601         return this.layout;
15602     },
15603
15604     showEl : function(){
15605         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15606         if(Roo.isIE7){
15607             this.layout.layout();
15608         }
15609     },
15610
15611     // private
15612     // Use the syncHeightBeforeShow config option to control this automatically
15613     syncBodyHeight : function(){
15614         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15615         if(this.layout){this.layout.layout();}
15616     },
15617     
15618       /**
15619      * Add an xtype element (actually adds to the layout.)
15620      * @return {Object} xdata xtype object data.
15621      */
15622     
15623     addxtype : function(c) {
15624         return this.layout.addxtype(c);
15625     }
15626 });/*
15627  * Based on:
15628  * Ext JS Library 1.1.1
15629  * Copyright(c) 2006-2007, Ext JS, LLC.
15630  *
15631  * Originally Released Under LGPL - original licence link has changed is not relivant.
15632  *
15633  * Fork - LGPL
15634  * <script type="text/javascript">
15635  */
15636  
15637 /**
15638  * @class Roo.MessageBox
15639  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15640  * Example usage:
15641  *<pre><code>
15642 // Basic alert:
15643 Roo.Msg.alert('Status', 'Changes saved successfully.');
15644
15645 // Prompt for user data:
15646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15647     if (btn == 'ok'){
15648         // process text value...
15649     }
15650 });
15651
15652 // Show a dialog using config options:
15653 Roo.Msg.show({
15654    title:'Save Changes?',
15655    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15656    buttons: Roo.Msg.YESNOCANCEL,
15657    fn: processResult,
15658    animEl: 'elId'
15659 });
15660 </code></pre>
15661  * @singleton
15662  */
15663 Roo.MessageBox = function(){
15664     var dlg, opt, mask, waitTimer;
15665     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15666     var buttons, activeTextEl, bwidth;
15667
15668     // private
15669     var handleButton = function(button){
15670         dlg.hide();
15671         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15672     };
15673
15674     // private
15675     var handleHide = function(){
15676         if(opt && opt.cls){
15677             dlg.el.removeClass(opt.cls);
15678         }
15679         if(waitTimer){
15680             Roo.TaskMgr.stop(waitTimer);
15681             waitTimer = null;
15682         }
15683     };
15684
15685     // private
15686     var updateButtons = function(b){
15687         var width = 0;
15688         if(!b){
15689             buttons["ok"].hide();
15690             buttons["cancel"].hide();
15691             buttons["yes"].hide();
15692             buttons["no"].hide();
15693             dlg.footer.dom.style.display = 'none';
15694             return width;
15695         }
15696         dlg.footer.dom.style.display = '';
15697         for(var k in buttons){
15698             if(typeof buttons[k] != "function"){
15699                 if(b[k]){
15700                     buttons[k].show();
15701                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15702                     width += buttons[k].el.getWidth()+15;
15703                 }else{
15704                     buttons[k].hide();
15705                 }
15706             }
15707         }
15708         return width;
15709     };
15710
15711     // private
15712     var handleEsc = function(d, k, e){
15713         if(opt && opt.closable !== false){
15714             dlg.hide();
15715         }
15716         if(e){
15717             e.stopEvent();
15718         }
15719     };
15720
15721     return {
15722         /**
15723          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15724          * @return {Roo.BasicDialog} The BasicDialog element
15725          */
15726         getDialog : function(){
15727            if(!dlg){
15728                 dlg = new Roo.BasicDialog("x-msg-box", {
15729                     autoCreate : true,
15730                     shadow: true,
15731                     draggable: true,
15732                     resizable:false,
15733                     constraintoviewport:false,
15734                     fixedcenter:true,
15735                     collapsible : false,
15736                     shim:true,
15737                     modal: true,
15738                     width:400, height:100,
15739                     buttonAlign:"center",
15740                     closeClick : function(){
15741                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15742                             handleButton("no");
15743                         }else{
15744                             handleButton("cancel");
15745                         }
15746                     }
15747                 });
15748                 dlg.on("hide", handleHide);
15749                 mask = dlg.mask;
15750                 dlg.addKeyListener(27, handleEsc);
15751                 buttons = {};
15752                 var bt = this.buttonText;
15753                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15754                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15755                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15756                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15757                 bodyEl = dlg.body.createChild({
15758
15759                     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>'
15760                 });
15761                 msgEl = bodyEl.dom.firstChild;
15762                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15763                 textboxEl.enableDisplayMode();
15764                 textboxEl.addKeyListener([10,13], function(){
15765                     if(dlg.isVisible() && opt && opt.buttons){
15766                         if(opt.buttons.ok){
15767                             handleButton("ok");
15768                         }else if(opt.buttons.yes){
15769                             handleButton("yes");
15770                         }
15771                     }
15772                 });
15773                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15774                 textareaEl.enableDisplayMode();
15775                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15776                 progressEl.enableDisplayMode();
15777                 var pf = progressEl.dom.firstChild;
15778                 if (pf) {
15779                     pp = Roo.get(pf.firstChild);
15780                     pp.setHeight(pf.offsetHeight);
15781                 }
15782                 
15783             }
15784             return dlg;
15785         },
15786
15787         /**
15788          * Updates the message box body text
15789          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15790          * the XHTML-compliant non-breaking space character '&amp;#160;')
15791          * @return {Roo.MessageBox} This message box
15792          */
15793         updateText : function(text){
15794             if(!dlg.isVisible() && !opt.width){
15795                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15796             }
15797             msgEl.innerHTML = text || '&#160;';
15798       
15799             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15800             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15801             var w = Math.max(
15802                     Math.min(opt.width || cw , this.maxWidth), 
15803                     Math.max(opt.minWidth || this.minWidth, bwidth)
15804             );
15805             if(opt.prompt){
15806                 activeTextEl.setWidth(w);
15807             }
15808             if(dlg.isVisible()){
15809                 dlg.fixedcenter = false;
15810             }
15811             // to big, make it scroll. = But as usual stupid IE does not support
15812             // !important..
15813             
15814             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15815                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15816                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15817             } else {
15818                 bodyEl.dom.style.height = '';
15819                 bodyEl.dom.style.overflowY = '';
15820             }
15821             if (cw > w) {
15822                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15823             } else {
15824                 bodyEl.dom.style.overflowX = '';
15825             }
15826             
15827             dlg.setContentSize(w, bodyEl.getHeight());
15828             if(dlg.isVisible()){
15829                 dlg.fixedcenter = true;
15830             }
15831             return this;
15832         },
15833
15834         /**
15835          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15836          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15837          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15838          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15839          * @return {Roo.MessageBox} This message box
15840          */
15841         updateProgress : function(value, text){
15842             if(text){
15843                 this.updateText(text);
15844             }
15845             if (pp) { // weird bug on my firefox - for some reason this is not defined
15846                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15847             }
15848             return this;
15849         },        
15850
15851         /**
15852          * Returns true if the message box is currently displayed
15853          * @return {Boolean} True if the message box is visible, else false
15854          */
15855         isVisible : function(){
15856             return dlg && dlg.isVisible();  
15857         },
15858
15859         /**
15860          * Hides the message box if it is displayed
15861          */
15862         hide : function(){
15863             if(this.isVisible()){
15864                 dlg.hide();
15865             }  
15866         },
15867
15868         /**
15869          * Displays a new message box, or reinitializes an existing message box, based on the config options
15870          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15871          * The following config object properties are supported:
15872          * <pre>
15873 Property    Type             Description
15874 ----------  ---------------  ------------------------------------------------------------------------------------
15875 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15876                                    closes (defaults to undefined)
15877 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15878                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15879 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15880                                    progress and wait dialogs will ignore this property and always hide the
15881                                    close button as they can only be closed programmatically.
15882 cls               String           A custom CSS class to apply to the message box element
15883 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15884                                    displayed (defaults to 75)
15885 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15886                                    function will be btn (the name of the button that was clicked, if applicable,
15887                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15888                                    Progress and wait dialogs will ignore this option since they do not respond to
15889                                    user actions and can only be closed programmatically, so any required function
15890                                    should be called by the same code after it closes the dialog.
15891 icon              String           A CSS class that provides a background image to be used as an icon for
15892                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15893 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15894 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15895 modal             Boolean          False to allow user interaction with the page while the message box is
15896                                    displayed (defaults to true)
15897 msg               String           A string that will replace the existing message box body text (defaults
15898                                    to the XHTML-compliant non-breaking space character '&#160;')
15899 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15900 progress          Boolean          True to display a progress bar (defaults to false)
15901 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15902 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15903 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15904 title             String           The title text
15905 value             String           The string value to set into the active textbox element if displayed
15906 wait              Boolean          True to display a progress bar (defaults to false)
15907 width             Number           The width of the dialog in pixels
15908 </pre>
15909          *
15910          * Example usage:
15911          * <pre><code>
15912 Roo.Msg.show({
15913    title: 'Address',
15914    msg: 'Please enter your address:',
15915    width: 300,
15916    buttons: Roo.MessageBox.OKCANCEL,
15917    multiline: true,
15918    fn: saveAddress,
15919    animEl: 'addAddressBtn'
15920 });
15921 </code></pre>
15922          * @param {Object} config Configuration options
15923          * @return {Roo.MessageBox} This message box
15924          */
15925         show : function(options)
15926         {
15927             
15928             // this causes nightmares if you show one dialog after another
15929             // especially on callbacks..
15930              
15931             if(this.isVisible()){
15932                 
15933                 this.hide();
15934                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15935                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15936                 Roo.log("New Dialog Message:" +  options.msg )
15937                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15938                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15939                 
15940             }
15941             var d = this.getDialog();
15942             opt = options;
15943             d.setTitle(opt.title || "&#160;");
15944             d.close.setDisplayed(opt.closable !== false);
15945             activeTextEl = textboxEl;
15946             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15947             if(opt.prompt){
15948                 if(opt.multiline){
15949                     textboxEl.hide();
15950                     textareaEl.show();
15951                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15952                         opt.multiline : this.defaultTextHeight);
15953                     activeTextEl = textareaEl;
15954                 }else{
15955                     textboxEl.show();
15956                     textareaEl.hide();
15957                 }
15958             }else{
15959                 textboxEl.hide();
15960                 textareaEl.hide();
15961             }
15962             progressEl.setDisplayed(opt.progress === true);
15963             this.updateProgress(0);
15964             activeTextEl.dom.value = opt.value || "";
15965             if(opt.prompt){
15966                 dlg.setDefaultButton(activeTextEl);
15967             }else{
15968                 var bs = opt.buttons;
15969                 var db = null;
15970                 if(bs && bs.ok){
15971                     db = buttons["ok"];
15972                 }else if(bs && bs.yes){
15973                     db = buttons["yes"];
15974                 }
15975                 dlg.setDefaultButton(db);
15976             }
15977             bwidth = updateButtons(opt.buttons);
15978             this.updateText(opt.msg);
15979             if(opt.cls){
15980                 d.el.addClass(opt.cls);
15981             }
15982             d.proxyDrag = opt.proxyDrag === true;
15983             d.modal = opt.modal !== false;
15984             d.mask = opt.modal !== false ? mask : false;
15985             if(!d.isVisible()){
15986                 // force it to the end of the z-index stack so it gets a cursor in FF
15987                 document.body.appendChild(dlg.el.dom);
15988                 d.animateTarget = null;
15989                 d.show(options.animEl);
15990             }
15991             return this;
15992         },
15993
15994         /**
15995          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15996          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15997          * and closing the message box when the process is complete.
15998          * @param {String} title The title bar text
15999          * @param {String} msg The message box body text
16000          * @return {Roo.MessageBox} This message box
16001          */
16002         progress : function(title, msg){
16003             this.show({
16004                 title : title,
16005                 msg : msg,
16006                 buttons: false,
16007                 progress:true,
16008                 closable:false,
16009                 minWidth: this.minProgressWidth,
16010                 modal : true
16011             });
16012             return this;
16013         },
16014
16015         /**
16016          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
16017          * If a callback function is passed it will be called after the user clicks the button, and the
16018          * id of the button that was clicked will be passed as the only parameter to the callback
16019          * (could also be the top-right close button).
16020          * @param {String} title The title bar text
16021          * @param {String} msg The message box body text
16022          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16023          * @param {Object} scope (optional) The scope of the callback function
16024          * @return {Roo.MessageBox} This message box
16025          */
16026         alert : function(title, msg, fn, scope){
16027             this.show({
16028                 title : title,
16029                 msg : msg,
16030                 buttons: this.OK,
16031                 fn: fn,
16032                 scope : scope,
16033                 modal : true
16034             });
16035             return this;
16036         },
16037
16038         /**
16039          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
16040          * interaction while waiting for a long-running process to complete that does not have defined intervals.
16041          * You are responsible for closing the message box when the process is complete.
16042          * @param {String} msg The message box body text
16043          * @param {String} title (optional) The title bar text
16044          * @return {Roo.MessageBox} This message box
16045          */
16046         wait : function(msg, title){
16047             this.show({
16048                 title : title,
16049                 msg : msg,
16050                 buttons: false,
16051                 closable:false,
16052                 progress:true,
16053                 modal:true,
16054                 width:300,
16055                 wait:true
16056             });
16057             waitTimer = Roo.TaskMgr.start({
16058                 run: function(i){
16059                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
16060                 },
16061                 interval: 1000
16062             });
16063             return this;
16064         },
16065
16066         /**
16067          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
16068          * If a callback function is passed it will be called after the user clicks either button, and the id of the
16069          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
16070          * @param {String} title The title bar text
16071          * @param {String} msg The message box body text
16072          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16073          * @param {Object} scope (optional) The scope of the callback function
16074          * @return {Roo.MessageBox} This message box
16075          */
16076         confirm : function(title, msg, fn, scope){
16077             this.show({
16078                 title : title,
16079                 msg : msg,
16080                 buttons: this.YESNO,
16081                 fn: fn,
16082                 scope : scope,
16083                 modal : true
16084             });
16085             return this;
16086         },
16087
16088         /**
16089          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
16090          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16091          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16092          * (could also be the top-right close button) and the text that was entered will be passed as the two
16093          * parameters to the callback.
16094          * @param {String} title The title bar text
16095          * @param {String} msg The message box body text
16096          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16097          * @param {Object} scope (optional) The scope of the callback function
16098          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16099          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16100          * @return {Roo.MessageBox} This message box
16101          */
16102         prompt : function(title, msg, fn, scope, multiline){
16103             this.show({
16104                 title : title,
16105                 msg : msg,
16106                 buttons: this.OKCANCEL,
16107                 fn: fn,
16108                 minWidth:250,
16109                 scope : scope,
16110                 prompt:true,
16111                 multiline: multiline,
16112                 modal : true
16113             });
16114             return this;
16115         },
16116
16117         /**
16118          * Button config that displays a single OK button
16119          * @type Object
16120          */
16121         OK : {ok:true},
16122         /**
16123          * Button config that displays Yes and No buttons
16124          * @type Object
16125          */
16126         YESNO : {yes:true, no:true},
16127         /**
16128          * Button config that displays OK and Cancel buttons
16129          * @type Object
16130          */
16131         OKCANCEL : {ok:true, cancel:true},
16132         /**
16133          * Button config that displays Yes, No and Cancel buttons
16134          * @type Object
16135          */
16136         YESNOCANCEL : {yes:true, no:true, cancel:true},
16137
16138         /**
16139          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16140          * @type Number
16141          */
16142         defaultTextHeight : 75,
16143         /**
16144          * The maximum width in pixels of the message box (defaults to 600)
16145          * @type Number
16146          */
16147         maxWidth : 600,
16148         /**
16149          * The minimum width in pixels of the message box (defaults to 100)
16150          * @type Number
16151          */
16152         minWidth : 100,
16153         /**
16154          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16155          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16156          * @type Number
16157          */
16158         minProgressWidth : 250,
16159         /**
16160          * An object containing the default button text strings that can be overriden for localized language support.
16161          * Supported properties are: ok, cancel, yes and no.
16162          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16163          * @type Object
16164          */
16165         buttonText : {
16166             ok : "OK",
16167             cancel : "Cancel",
16168             yes : "Yes",
16169             no : "No"
16170         }
16171     };
16172 }();
16173
16174 /**
16175  * Shorthand for {@link Roo.MessageBox}
16176  */
16177 Roo.Msg = Roo.MessageBox;/*
16178  * Based on:
16179  * Ext JS Library 1.1.1
16180  * Copyright(c) 2006-2007, Ext JS, LLC.
16181  *
16182  * Originally Released Under LGPL - original licence link has changed is not relivant.
16183  *
16184  * Fork - LGPL
16185  * <script type="text/javascript">
16186  */
16187 /**
16188  * @class Roo.QuickTips
16189  * Provides attractive and customizable tooltips for any element.
16190  * @singleton
16191  */
16192 Roo.QuickTips = function(){
16193     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16194     var ce, bd, xy, dd;
16195     var visible = false, disabled = true, inited = false;
16196     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16197     
16198     var onOver = function(e){
16199         if(disabled){
16200             return;
16201         }
16202         var t = e.getTarget();
16203         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16204             return;
16205         }
16206         if(ce && t == ce.el){
16207             clearTimeout(hideProc);
16208             return;
16209         }
16210         if(t && tagEls[t.id]){
16211             tagEls[t.id].el = t;
16212             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16213             return;
16214         }
16215         var ttp, et = Roo.fly(t);
16216         var ns = cfg.namespace;
16217         if(tm.interceptTitles && t.title){
16218             ttp = t.title;
16219             t.qtip = ttp;
16220             t.removeAttribute("title");
16221             e.preventDefault();
16222         }else{
16223             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16224         }
16225         if(ttp){
16226             showProc = show.defer(tm.showDelay, tm, [{
16227                 el: t, 
16228                 text: ttp, 
16229                 width: et.getAttributeNS(ns, cfg.width),
16230                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16231                 title: et.getAttributeNS(ns, cfg.title),
16232                     cls: et.getAttributeNS(ns, cfg.cls)
16233             }]);
16234         }
16235     };
16236     
16237     var onOut = function(e){
16238         clearTimeout(showProc);
16239         var t = e.getTarget();
16240         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16241             hideProc = setTimeout(hide, tm.hideDelay);
16242         }
16243     };
16244     
16245     var onMove = function(e){
16246         if(disabled){
16247             return;
16248         }
16249         xy = e.getXY();
16250         xy[1] += 18;
16251         if(tm.trackMouse && ce){
16252             el.setXY(xy);
16253         }
16254     };
16255     
16256     var onDown = function(e){
16257         clearTimeout(showProc);
16258         clearTimeout(hideProc);
16259         if(!e.within(el)){
16260             if(tm.hideOnClick){
16261                 hide();
16262                 tm.disable();
16263                 tm.enable.defer(100, tm);
16264             }
16265         }
16266     };
16267     
16268     var getPad = function(){
16269         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16270     };
16271
16272     var show = function(o){
16273         if(disabled){
16274             return;
16275         }
16276         clearTimeout(dismissProc);
16277         ce = o;
16278         if(removeCls){ // in case manually hidden
16279             el.removeClass(removeCls);
16280             removeCls = null;
16281         }
16282         if(ce.cls){
16283             el.addClass(ce.cls);
16284             removeCls = ce.cls;
16285         }
16286         if(ce.title){
16287             tipTitle.update(ce.title);
16288             tipTitle.show();
16289         }else{
16290             tipTitle.update('');
16291             tipTitle.hide();
16292         }
16293         el.dom.style.width  = tm.maxWidth+'px';
16294         //tipBody.dom.style.width = '';
16295         tipBodyText.update(o.text);
16296         var p = getPad(), w = ce.width;
16297         if(!w){
16298             var td = tipBodyText.dom;
16299             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16300             if(aw > tm.maxWidth){
16301                 w = tm.maxWidth;
16302             }else if(aw < tm.minWidth){
16303                 w = tm.minWidth;
16304             }else{
16305                 w = aw;
16306             }
16307         }
16308         //tipBody.setWidth(w);
16309         el.setWidth(parseInt(w, 10) + p);
16310         if(ce.autoHide === false){
16311             close.setDisplayed(true);
16312             if(dd){
16313                 dd.unlock();
16314             }
16315         }else{
16316             close.setDisplayed(false);
16317             if(dd){
16318                 dd.lock();
16319             }
16320         }
16321         if(xy){
16322             el.avoidY = xy[1]-18;
16323             el.setXY(xy);
16324         }
16325         if(tm.animate){
16326             el.setOpacity(.1);
16327             el.setStyle("visibility", "visible");
16328             el.fadeIn({callback: afterShow});
16329         }else{
16330             afterShow();
16331         }
16332     };
16333     
16334     var afterShow = function(){
16335         if(ce){
16336             el.show();
16337             esc.enable();
16338             if(tm.autoDismiss && ce.autoHide !== false){
16339                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16340             }
16341         }
16342     };
16343     
16344     var hide = function(noanim){
16345         clearTimeout(dismissProc);
16346         clearTimeout(hideProc);
16347         ce = null;
16348         if(el.isVisible()){
16349             esc.disable();
16350             if(noanim !== true && tm.animate){
16351                 el.fadeOut({callback: afterHide});
16352             }else{
16353                 afterHide();
16354             } 
16355         }
16356     };
16357     
16358     var afterHide = function(){
16359         el.hide();
16360         if(removeCls){
16361             el.removeClass(removeCls);
16362             removeCls = null;
16363         }
16364     };
16365     
16366     return {
16367         /**
16368         * @cfg {Number} minWidth
16369         * The minimum width of the quick tip (defaults to 40)
16370         */
16371        minWidth : 40,
16372         /**
16373         * @cfg {Number} maxWidth
16374         * The maximum width of the quick tip (defaults to 300)
16375         */
16376        maxWidth : 300,
16377         /**
16378         * @cfg {Boolean} interceptTitles
16379         * True to automatically use the element's DOM title value if available (defaults to false)
16380         */
16381        interceptTitles : false,
16382         /**
16383         * @cfg {Boolean} trackMouse
16384         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16385         */
16386        trackMouse : false,
16387         /**
16388         * @cfg {Boolean} hideOnClick
16389         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16390         */
16391        hideOnClick : true,
16392         /**
16393         * @cfg {Number} showDelay
16394         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16395         */
16396        showDelay : 500,
16397         /**
16398         * @cfg {Number} hideDelay
16399         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16400         */
16401        hideDelay : 200,
16402         /**
16403         * @cfg {Boolean} autoHide
16404         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16405         * Used in conjunction with hideDelay.
16406         */
16407        autoHide : true,
16408         /**
16409         * @cfg {Boolean}
16410         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16411         * (defaults to true).  Used in conjunction with autoDismissDelay.
16412         */
16413        autoDismiss : true,
16414         /**
16415         * @cfg {Number}
16416         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16417         */
16418        autoDismissDelay : 5000,
16419        /**
16420         * @cfg {Boolean} animate
16421         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16422         */
16423        animate : false,
16424
16425        /**
16426         * @cfg {String} title
16427         * Title text to display (defaults to '').  This can be any valid HTML markup.
16428         */
16429         title: '',
16430        /**
16431         * @cfg {String} text
16432         * Body text to display (defaults to '').  This can be any valid HTML markup.
16433         */
16434         text : '',
16435        /**
16436         * @cfg {String} cls
16437         * A CSS class to apply to the base quick tip element (defaults to '').
16438         */
16439         cls : '',
16440        /**
16441         * @cfg {Number} width
16442         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16443         * minWidth or maxWidth.
16444         */
16445         width : null,
16446
16447     /**
16448      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16449      * or display QuickTips in a page.
16450      */
16451        init : function(){
16452           tm = Roo.QuickTips;
16453           cfg = tm.tagConfig;
16454           if(!inited){
16455               if(!Roo.isReady){ // allow calling of init() before onReady
16456                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16457                   return;
16458               }
16459               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16460               el.fxDefaults = {stopFx: true};
16461               // maximum custom styling
16462               //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>');
16463               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>');              
16464               tipTitle = el.child('h3');
16465               tipTitle.enableDisplayMode("block");
16466               tipBody = el.child('div.x-tip-bd');
16467               tipBodyText = el.child('div.x-tip-bd-inner');
16468               //bdLeft = el.child('div.x-tip-bd-left');
16469               //bdRight = el.child('div.x-tip-bd-right');
16470               close = el.child('div.x-tip-close');
16471               close.enableDisplayMode("block");
16472               close.on("click", hide);
16473               var d = Roo.get(document);
16474               d.on("mousedown", onDown);
16475               d.on("mouseover", onOver);
16476               d.on("mouseout", onOut);
16477               d.on("mousemove", onMove);
16478               esc = d.addKeyListener(27, hide);
16479               esc.disable();
16480               if(Roo.dd.DD){
16481                   dd = el.initDD("default", null, {
16482                       onDrag : function(){
16483                           el.sync();  
16484                       }
16485                   });
16486                   dd.setHandleElId(tipTitle.id);
16487                   dd.lock();
16488               }
16489               inited = true;
16490           }
16491           this.enable(); 
16492        },
16493
16494     /**
16495      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16496      * are supported:
16497      * <pre>
16498 Property    Type                   Description
16499 ----------  ---------------------  ------------------------------------------------------------------------
16500 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16501      * </ul>
16502      * @param {Object} config The config object
16503      */
16504        register : function(config){
16505            var cs = config instanceof Array ? config : arguments;
16506            for(var i = 0, len = cs.length; i < len; i++) {
16507                var c = cs[i];
16508                var target = c.target;
16509                if(target){
16510                    if(target instanceof Array){
16511                        for(var j = 0, jlen = target.length; j < jlen; j++){
16512                            tagEls[target[j]] = c;
16513                        }
16514                    }else{
16515                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16516                    }
16517                }
16518            }
16519        },
16520
16521     /**
16522      * Removes this quick tip from its element and destroys it.
16523      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16524      */
16525        unregister : function(el){
16526            delete tagEls[Roo.id(el)];
16527        },
16528
16529     /**
16530      * Enable this quick tip.
16531      */
16532        enable : function(){
16533            if(inited && disabled){
16534                locks.pop();
16535                if(locks.length < 1){
16536                    disabled = false;
16537                }
16538            }
16539        },
16540
16541     /**
16542      * Disable this quick tip.
16543      */
16544        disable : function(){
16545           disabled = true;
16546           clearTimeout(showProc);
16547           clearTimeout(hideProc);
16548           clearTimeout(dismissProc);
16549           if(ce){
16550               hide(true);
16551           }
16552           locks.push(1);
16553        },
16554
16555     /**
16556      * Returns true if the quick tip is enabled, else false.
16557      */
16558        isEnabled : function(){
16559             return !disabled;
16560        },
16561
16562         // private
16563        tagConfig : {
16564            namespace : "ext",
16565            attribute : "qtip",
16566            width : "width",
16567            target : "target",
16568            title : "qtitle",
16569            hide : "hide",
16570            cls : "qclass"
16571        }
16572    };
16573 }();
16574
16575 // backwards compat
16576 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16577  * Based on:
16578  * Ext JS Library 1.1.1
16579  * Copyright(c) 2006-2007, Ext JS, LLC.
16580  *
16581  * Originally Released Under LGPL - original licence link has changed is not relivant.
16582  *
16583  * Fork - LGPL
16584  * <script type="text/javascript">
16585  */
16586  
16587
16588 /**
16589  * @class Roo.tree.TreePanel
16590  * @extends Roo.data.Tree
16591
16592  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16593  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16594  * @cfg {Boolean} enableDD true to enable drag and drop
16595  * @cfg {Boolean} enableDrag true to enable just drag
16596  * @cfg {Boolean} enableDrop true to enable just drop
16597  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16598  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16599  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16600  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16601  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16602  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16603  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16604  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16605  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16606  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16607  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16608  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16609  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16610  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16611  * @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>
16612  * @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>
16613  * 
16614  * @constructor
16615  * @param {String/HTMLElement/Element} el The container element
16616  * @param {Object} config
16617  */
16618 Roo.tree.TreePanel = function(el, config){
16619     var root = false;
16620     var loader = false;
16621     if (config.root) {
16622         root = config.root;
16623         delete config.root;
16624     }
16625     if (config.loader) {
16626         loader = config.loader;
16627         delete config.loader;
16628     }
16629     
16630     Roo.apply(this, config);
16631     Roo.tree.TreePanel.superclass.constructor.call(this);
16632     this.el = Roo.get(el);
16633     this.el.addClass('x-tree');
16634     //console.log(root);
16635     if (root) {
16636         this.setRootNode( Roo.factory(root, Roo.tree));
16637     }
16638     if (loader) {
16639         this.loader = Roo.factory(loader, Roo.tree);
16640     }
16641    /**
16642     * Read-only. The id of the container element becomes this TreePanel's id.
16643     */
16644     this.id = this.el.id;
16645     this.addEvents({
16646         /**
16647         * @event beforeload
16648         * Fires before a node is loaded, return false to cancel
16649         * @param {Node} node The node being loaded
16650         */
16651         "beforeload" : true,
16652         /**
16653         * @event load
16654         * Fires when a node is loaded
16655         * @param {Node} node The node that was loaded
16656         */
16657         "load" : true,
16658         /**
16659         * @event textchange
16660         * Fires when the text for a node is changed
16661         * @param {Node} node The node
16662         * @param {String} text The new text
16663         * @param {String} oldText The old text
16664         */
16665         "textchange" : true,
16666         /**
16667         * @event beforeexpand
16668         * Fires before a node is expanded, return false to cancel.
16669         * @param {Node} node The node
16670         * @param {Boolean} deep
16671         * @param {Boolean} anim
16672         */
16673         "beforeexpand" : true,
16674         /**
16675         * @event beforecollapse
16676         * Fires before a node is collapsed, return false to cancel.
16677         * @param {Node} node The node
16678         * @param {Boolean} deep
16679         * @param {Boolean} anim
16680         */
16681         "beforecollapse" : true,
16682         /**
16683         * @event expand
16684         * Fires when a node is expanded
16685         * @param {Node} node The node
16686         */
16687         "expand" : true,
16688         /**
16689         * @event disabledchange
16690         * Fires when the disabled status of a node changes
16691         * @param {Node} node The node
16692         * @param {Boolean} disabled
16693         */
16694         "disabledchange" : true,
16695         /**
16696         * @event collapse
16697         * Fires when a node is collapsed
16698         * @param {Node} node The node
16699         */
16700         "collapse" : true,
16701         /**
16702         * @event beforeclick
16703         * Fires before click processing on a node. Return false to cancel the default action.
16704         * @param {Node} node The node
16705         * @param {Roo.EventObject} e The event object
16706         */
16707         "beforeclick":true,
16708         /**
16709         * @event checkchange
16710         * Fires when a node with a checkbox's checked property changes
16711         * @param {Node} this This node
16712         * @param {Boolean} checked
16713         */
16714         "checkchange":true,
16715         /**
16716         * @event click
16717         * Fires when a node is clicked
16718         * @param {Node} node The node
16719         * @param {Roo.EventObject} e The event object
16720         */
16721         "click":true,
16722         /**
16723         * @event dblclick
16724         * Fires when a node is double clicked
16725         * @param {Node} node The node
16726         * @param {Roo.EventObject} e The event object
16727         */
16728         "dblclick":true,
16729         /**
16730         * @event contextmenu
16731         * Fires when a node is right clicked
16732         * @param {Node} node The node
16733         * @param {Roo.EventObject} e The event object
16734         */
16735         "contextmenu":true,
16736         /**
16737         * @event beforechildrenrendered
16738         * Fires right before the child nodes for a node are rendered
16739         * @param {Node} node The node
16740         */
16741         "beforechildrenrendered":true,
16742         /**
16743         * @event startdrag
16744         * Fires when a node starts being dragged
16745         * @param {Roo.tree.TreePanel} this
16746         * @param {Roo.tree.TreeNode} node
16747         * @param {event} e The raw browser event
16748         */ 
16749        "startdrag" : true,
16750        /**
16751         * @event enddrag
16752         * Fires when a drag operation is complete
16753         * @param {Roo.tree.TreePanel} this
16754         * @param {Roo.tree.TreeNode} node
16755         * @param {event} e The raw browser event
16756         */
16757        "enddrag" : true,
16758        /**
16759         * @event dragdrop
16760         * Fires when a dragged node is dropped on a valid DD target
16761         * @param {Roo.tree.TreePanel} this
16762         * @param {Roo.tree.TreeNode} node
16763         * @param {DD} dd The dd it was dropped on
16764         * @param {event} e The raw browser event
16765         */
16766        "dragdrop" : true,
16767        /**
16768         * @event beforenodedrop
16769         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16770         * passed to handlers has the following properties:<br />
16771         * <ul style="padding:5px;padding-left:16px;">
16772         * <li>tree - The TreePanel</li>
16773         * <li>target - The node being targeted for the drop</li>
16774         * <li>data - The drag data from the drag source</li>
16775         * <li>point - The point of the drop - append, above or below</li>
16776         * <li>source - The drag source</li>
16777         * <li>rawEvent - Raw mouse event</li>
16778         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16779         * to be inserted by setting them on this object.</li>
16780         * <li>cancel - Set this to true to cancel the drop.</li>
16781         * </ul>
16782         * @param {Object} dropEvent
16783         */
16784        "beforenodedrop" : true,
16785        /**
16786         * @event nodedrop
16787         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16788         * passed to handlers has the following properties:<br />
16789         * <ul style="padding:5px;padding-left:16px;">
16790         * <li>tree - The TreePanel</li>
16791         * <li>target - The node being targeted for the drop</li>
16792         * <li>data - The drag data from the drag source</li>
16793         * <li>point - The point of the drop - append, above or below</li>
16794         * <li>source - The drag source</li>
16795         * <li>rawEvent - Raw mouse event</li>
16796         * <li>dropNode - Dropped node(s).</li>
16797         * </ul>
16798         * @param {Object} dropEvent
16799         */
16800        "nodedrop" : true,
16801         /**
16802         * @event nodedragover
16803         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16804         * passed to handlers has the following properties:<br />
16805         * <ul style="padding:5px;padding-left:16px;">
16806         * <li>tree - The TreePanel</li>
16807         * <li>target - The node being targeted for the drop</li>
16808         * <li>data - The drag data from the drag source</li>
16809         * <li>point - The point of the drop - append, above or below</li>
16810         * <li>source - The drag source</li>
16811         * <li>rawEvent - Raw mouse event</li>
16812         * <li>dropNode - Drop node(s) provided by the source.</li>
16813         * <li>cancel - Set this to true to signal drop not allowed.</li>
16814         * </ul>
16815         * @param {Object} dragOverEvent
16816         */
16817        "nodedragover" : true
16818         
16819     });
16820     if(this.singleExpand){
16821        this.on("beforeexpand", this.restrictExpand, this);
16822     }
16823     if (this.editor) {
16824         this.editor.tree = this;
16825         this.editor = Roo.factory(this.editor, Roo.tree);
16826     }
16827     
16828     if (this.selModel) {
16829         this.selModel = Roo.factory(this.selModel, Roo.tree);
16830     }
16831    
16832 };
16833 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16834     rootVisible : true,
16835     animate: Roo.enableFx,
16836     lines : true,
16837     enableDD : false,
16838     hlDrop : Roo.enableFx,
16839   
16840     renderer: false,
16841     
16842     rendererTip: false,
16843     // private
16844     restrictExpand : function(node){
16845         var p = node.parentNode;
16846         if(p){
16847             if(p.expandedChild && p.expandedChild.parentNode == p){
16848                 p.expandedChild.collapse();
16849             }
16850             p.expandedChild = node;
16851         }
16852     },
16853
16854     // private override
16855     setRootNode : function(node){
16856         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16857         if(!this.rootVisible){
16858             node.ui = new Roo.tree.RootTreeNodeUI(node);
16859         }
16860         return node;
16861     },
16862
16863     /**
16864      * Returns the container element for this TreePanel
16865      */
16866     getEl : function(){
16867         return this.el;
16868     },
16869
16870     /**
16871      * Returns the default TreeLoader for this TreePanel
16872      */
16873     getLoader : function(){
16874         return this.loader;
16875     },
16876
16877     /**
16878      * Expand all nodes
16879      */
16880     expandAll : function(){
16881         this.root.expand(true);
16882     },
16883
16884     /**
16885      * Collapse all nodes
16886      */
16887     collapseAll : function(){
16888         this.root.collapse(true);
16889     },
16890
16891     /**
16892      * Returns the selection model used by this TreePanel
16893      */
16894     getSelectionModel : function(){
16895         if(!this.selModel){
16896             this.selModel = new Roo.tree.DefaultSelectionModel();
16897         }
16898         return this.selModel;
16899     },
16900
16901     /**
16902      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16903      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16904      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16905      * @return {Array}
16906      */
16907     getChecked : function(a, startNode){
16908         startNode = startNode || this.root;
16909         var r = [];
16910         var f = function(){
16911             if(this.attributes.checked){
16912                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16913             }
16914         }
16915         startNode.cascade(f);
16916         return r;
16917     },
16918
16919     /**
16920      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16921      * @param {String} path
16922      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16923      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16924      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16925      */
16926     expandPath : function(path, attr, callback){
16927         attr = attr || "id";
16928         var keys = path.split(this.pathSeparator);
16929         var curNode = this.root;
16930         if(curNode.attributes[attr] != keys[1]){ // invalid root
16931             if(callback){
16932                 callback(false, null);
16933             }
16934             return;
16935         }
16936         var index = 1;
16937         var f = function(){
16938             if(++index == keys.length){
16939                 if(callback){
16940                     callback(true, curNode);
16941                 }
16942                 return;
16943             }
16944             var c = curNode.findChild(attr, keys[index]);
16945             if(!c){
16946                 if(callback){
16947                     callback(false, curNode);
16948                 }
16949                 return;
16950             }
16951             curNode = c;
16952             c.expand(false, false, f);
16953         };
16954         curNode.expand(false, false, f);
16955     },
16956
16957     /**
16958      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16959      * @param {String} path
16960      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16961      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16962      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16963      */
16964     selectPath : function(path, attr, callback){
16965         attr = attr || "id";
16966         var keys = path.split(this.pathSeparator);
16967         var v = keys.pop();
16968         if(keys.length > 0){
16969             var f = function(success, node){
16970                 if(success && node){
16971                     var n = node.findChild(attr, v);
16972                     if(n){
16973                         n.select();
16974                         if(callback){
16975                             callback(true, n);
16976                         }
16977                     }else if(callback){
16978                         callback(false, n);
16979                     }
16980                 }else{
16981                     if(callback){
16982                         callback(false, n);
16983                     }
16984                 }
16985             };
16986             this.expandPath(keys.join(this.pathSeparator), attr, f);
16987         }else{
16988             this.root.select();
16989             if(callback){
16990                 callback(true, this.root);
16991             }
16992         }
16993     },
16994
16995     getTreeEl : function(){
16996         return this.el;
16997     },
16998
16999     /**
17000      * Trigger rendering of this TreePanel
17001      */
17002     render : function(){
17003         if (this.innerCt) {
17004             return this; // stop it rendering more than once!!
17005         }
17006         
17007         this.innerCt = this.el.createChild({tag:"ul",
17008                cls:"x-tree-root-ct " +
17009                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
17010
17011         if(this.containerScroll){
17012             Roo.dd.ScrollManager.register(this.el);
17013         }
17014         if((this.enableDD || this.enableDrop) && !this.dropZone){
17015            /**
17016             * The dropZone used by this tree if drop is enabled
17017             * @type Roo.tree.TreeDropZone
17018             */
17019              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
17020                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
17021            });
17022         }
17023         if((this.enableDD || this.enableDrag) && !this.dragZone){
17024            /**
17025             * The dragZone used by this tree if drag is enabled
17026             * @type Roo.tree.TreeDragZone
17027             */
17028             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
17029                ddGroup: this.ddGroup || "TreeDD",
17030                scroll: this.ddScroll
17031            });
17032         }
17033         this.getSelectionModel().init(this);
17034         if (!this.root) {
17035             Roo.log("ROOT not set in tree");
17036             return this;
17037         }
17038         this.root.render();
17039         if(!this.rootVisible){
17040             this.root.renderChildren();
17041         }
17042         return this;
17043     }
17044 });/*
17045  * Based on:
17046  * Ext JS Library 1.1.1
17047  * Copyright(c) 2006-2007, Ext JS, LLC.
17048  *
17049  * Originally Released Under LGPL - original licence link has changed is not relivant.
17050  *
17051  * Fork - LGPL
17052  * <script type="text/javascript">
17053  */
17054  
17055
17056 /**
17057  * @class Roo.tree.DefaultSelectionModel
17058  * @extends Roo.util.Observable
17059  * The default single selection for a TreePanel.
17060  * @param {Object} cfg Configuration
17061  */
17062 Roo.tree.DefaultSelectionModel = function(cfg){
17063    this.selNode = null;
17064    
17065    
17066    
17067    this.addEvents({
17068        /**
17069         * @event selectionchange
17070         * Fires when the selected node changes
17071         * @param {DefaultSelectionModel} this
17072         * @param {TreeNode} node the new selection
17073         */
17074        "selectionchange" : true,
17075
17076        /**
17077         * @event beforeselect
17078         * Fires before the selected node changes, return false to cancel the change
17079         * @param {DefaultSelectionModel} this
17080         * @param {TreeNode} node the new selection
17081         * @param {TreeNode} node the old selection
17082         */
17083        "beforeselect" : true
17084    });
17085    
17086     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
17087 };
17088
17089 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
17090     init : function(tree){
17091         this.tree = tree;
17092         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17093         tree.on("click", this.onNodeClick, this);
17094     },
17095     
17096     onNodeClick : function(node, e){
17097         if (e.ctrlKey && this.selNode == node)  {
17098             this.unselect(node);
17099             return;
17100         }
17101         this.select(node);
17102     },
17103     
17104     /**
17105      * Select a node.
17106      * @param {TreeNode} node The node to select
17107      * @return {TreeNode} The selected node
17108      */
17109     select : function(node){
17110         var last = this.selNode;
17111         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17112             if(last){
17113                 last.ui.onSelectedChange(false);
17114             }
17115             this.selNode = node;
17116             node.ui.onSelectedChange(true);
17117             this.fireEvent("selectionchange", this, node, last);
17118         }
17119         return node;
17120     },
17121     
17122     /**
17123      * Deselect a node.
17124      * @param {TreeNode} node The node to unselect
17125      */
17126     unselect : function(node){
17127         if(this.selNode == node){
17128             this.clearSelections();
17129         }    
17130     },
17131     
17132     /**
17133      * Clear all selections
17134      */
17135     clearSelections : function(){
17136         var n = this.selNode;
17137         if(n){
17138             n.ui.onSelectedChange(false);
17139             this.selNode = null;
17140             this.fireEvent("selectionchange", this, null);
17141         }
17142         return n;
17143     },
17144     
17145     /**
17146      * Get the selected node
17147      * @return {TreeNode} The selected node
17148      */
17149     getSelectedNode : function(){
17150         return this.selNode;    
17151     },
17152     
17153     /**
17154      * Returns true if the node is selected
17155      * @param {TreeNode} node The node to check
17156      * @return {Boolean}
17157      */
17158     isSelected : function(node){
17159         return this.selNode == node;  
17160     },
17161
17162     /**
17163      * Selects the node above the selected node in the tree, intelligently walking the nodes
17164      * @return TreeNode The new selection
17165      */
17166     selectPrevious : function(){
17167         var s = this.selNode || this.lastSelNode;
17168         if(!s){
17169             return null;
17170         }
17171         var ps = s.previousSibling;
17172         if(ps){
17173             if(!ps.isExpanded() || ps.childNodes.length < 1){
17174                 return this.select(ps);
17175             } else{
17176                 var lc = ps.lastChild;
17177                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17178                     lc = lc.lastChild;
17179                 }
17180                 return this.select(lc);
17181             }
17182         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17183             return this.select(s.parentNode);
17184         }
17185         return null;
17186     },
17187
17188     /**
17189      * Selects the node above the selected node in the tree, intelligently walking the nodes
17190      * @return TreeNode The new selection
17191      */
17192     selectNext : function(){
17193         var s = this.selNode || this.lastSelNode;
17194         if(!s){
17195             return null;
17196         }
17197         if(s.firstChild && s.isExpanded()){
17198              return this.select(s.firstChild);
17199          }else if(s.nextSibling){
17200              return this.select(s.nextSibling);
17201          }else if(s.parentNode){
17202             var newS = null;
17203             s.parentNode.bubble(function(){
17204                 if(this.nextSibling){
17205                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17206                     return false;
17207                 }
17208             });
17209             return newS;
17210          }
17211         return null;
17212     },
17213
17214     onKeyDown : function(e){
17215         var s = this.selNode || this.lastSelNode;
17216         // undesirable, but required
17217         var sm = this;
17218         if(!s){
17219             return;
17220         }
17221         var k = e.getKey();
17222         switch(k){
17223              case e.DOWN:
17224                  e.stopEvent();
17225                  this.selectNext();
17226              break;
17227              case e.UP:
17228                  e.stopEvent();
17229                  this.selectPrevious();
17230              break;
17231              case e.RIGHT:
17232                  e.preventDefault();
17233                  if(s.hasChildNodes()){
17234                      if(!s.isExpanded()){
17235                          s.expand();
17236                      }else if(s.firstChild){
17237                          this.select(s.firstChild, e);
17238                      }
17239                  }
17240              break;
17241              case e.LEFT:
17242                  e.preventDefault();
17243                  if(s.hasChildNodes() && s.isExpanded()){
17244                      s.collapse();
17245                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17246                      this.select(s.parentNode, e);
17247                  }
17248              break;
17249         };
17250     }
17251 });
17252
17253 /**
17254  * @class Roo.tree.MultiSelectionModel
17255  * @extends Roo.util.Observable
17256  * Multi selection for a TreePanel.
17257  * @param {Object} cfg Configuration
17258  */
17259 Roo.tree.MultiSelectionModel = function(){
17260    this.selNodes = [];
17261    this.selMap = {};
17262    this.addEvents({
17263        /**
17264         * @event selectionchange
17265         * Fires when the selected nodes change
17266         * @param {MultiSelectionModel} this
17267         * @param {Array} nodes Array of the selected nodes
17268         */
17269        "selectionchange" : true
17270    });
17271    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17272    
17273 };
17274
17275 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17276     init : function(tree){
17277         this.tree = tree;
17278         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17279         tree.on("click", this.onNodeClick, this);
17280     },
17281     
17282     onNodeClick : function(node, e){
17283         this.select(node, e, e.ctrlKey);
17284     },
17285     
17286     /**
17287      * Select a node.
17288      * @param {TreeNode} node The node to select
17289      * @param {EventObject} e (optional) An event associated with the selection
17290      * @param {Boolean} keepExisting True to retain existing selections
17291      * @return {TreeNode} The selected node
17292      */
17293     select : function(node, e, keepExisting){
17294         if(keepExisting !== true){
17295             this.clearSelections(true);
17296         }
17297         if(this.isSelected(node)){
17298             this.lastSelNode = node;
17299             return node;
17300         }
17301         this.selNodes.push(node);
17302         this.selMap[node.id] = node;
17303         this.lastSelNode = node;
17304         node.ui.onSelectedChange(true);
17305         this.fireEvent("selectionchange", this, this.selNodes);
17306         return node;
17307     },
17308     
17309     /**
17310      * Deselect a node.
17311      * @param {TreeNode} node The node to unselect
17312      */
17313     unselect : function(node){
17314         if(this.selMap[node.id]){
17315             node.ui.onSelectedChange(false);
17316             var sn = this.selNodes;
17317             var index = -1;
17318             if(sn.indexOf){
17319                 index = sn.indexOf(node);
17320             }else{
17321                 for(var i = 0, len = sn.length; i < len; i++){
17322                     if(sn[i] == node){
17323                         index = i;
17324                         break;
17325                     }
17326                 }
17327             }
17328             if(index != -1){
17329                 this.selNodes.splice(index, 1);
17330             }
17331             delete this.selMap[node.id];
17332             this.fireEvent("selectionchange", this, this.selNodes);
17333         }
17334     },
17335     
17336     /**
17337      * Clear all selections
17338      */
17339     clearSelections : function(suppressEvent){
17340         var sn = this.selNodes;
17341         if(sn.length > 0){
17342             for(var i = 0, len = sn.length; i < len; i++){
17343                 sn[i].ui.onSelectedChange(false);
17344             }
17345             this.selNodes = [];
17346             this.selMap = {};
17347             if(suppressEvent !== true){
17348                 this.fireEvent("selectionchange", this, this.selNodes);
17349             }
17350         }
17351     },
17352     
17353     /**
17354      * Returns true if the node is selected
17355      * @param {TreeNode} node The node to check
17356      * @return {Boolean}
17357      */
17358     isSelected : function(node){
17359         return this.selMap[node.id] ? true : false;  
17360     },
17361     
17362     /**
17363      * Returns an array of the selected nodes
17364      * @return {Array}
17365      */
17366     getSelectedNodes : function(){
17367         return this.selNodes;    
17368     },
17369
17370     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17371
17372     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17373
17374     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17375 });/*
17376  * Based on:
17377  * Ext JS Library 1.1.1
17378  * Copyright(c) 2006-2007, Ext JS, LLC.
17379  *
17380  * Originally Released Under LGPL - original licence link has changed is not relivant.
17381  *
17382  * Fork - LGPL
17383  * <script type="text/javascript">
17384  */
17385  
17386 /**
17387  * @class Roo.tree.TreeNode
17388  * @extends Roo.data.Node
17389  * @cfg {String} text The text for this node
17390  * @cfg {Boolean} expanded true to start the node expanded
17391  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17392  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17393  * @cfg {Boolean} disabled true to start the node disabled
17394  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17395  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17396  * @cfg {String} cls A css class to be added to the node
17397  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17398  * @cfg {String} href URL of the link used for the node (defaults to #)
17399  * @cfg {String} hrefTarget target frame for the link
17400  * @cfg {String} qtip An Ext QuickTip for the node
17401  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17402  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17403  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17404  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17405  * (defaults to undefined with no checkbox rendered)
17406  * @constructor
17407  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17408  */
17409 Roo.tree.TreeNode = function(attributes){
17410     attributes = attributes || {};
17411     if(typeof attributes == "string"){
17412         attributes = {text: attributes};
17413     }
17414     this.childrenRendered = false;
17415     this.rendered = false;
17416     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17417     this.expanded = attributes.expanded === true;
17418     this.isTarget = attributes.isTarget !== false;
17419     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17420     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17421
17422     /**
17423      * Read-only. The text for this node. To change it use setText().
17424      * @type String
17425      */
17426     this.text = attributes.text;
17427     /**
17428      * True if this node is disabled.
17429      * @type Boolean
17430      */
17431     this.disabled = attributes.disabled === true;
17432
17433     this.addEvents({
17434         /**
17435         * @event textchange
17436         * Fires when the text for this node is changed
17437         * @param {Node} this This node
17438         * @param {String} text The new text
17439         * @param {String} oldText The old text
17440         */
17441         "textchange" : true,
17442         /**
17443         * @event beforeexpand
17444         * Fires before this node is expanded, return false to cancel.
17445         * @param {Node} this This node
17446         * @param {Boolean} deep
17447         * @param {Boolean} anim
17448         */
17449         "beforeexpand" : true,
17450         /**
17451         * @event beforecollapse
17452         * Fires before this node is collapsed, return false to cancel.
17453         * @param {Node} this This node
17454         * @param {Boolean} deep
17455         * @param {Boolean} anim
17456         */
17457         "beforecollapse" : true,
17458         /**
17459         * @event expand
17460         * Fires when this node is expanded
17461         * @param {Node} this This node
17462         */
17463         "expand" : true,
17464         /**
17465         * @event disabledchange
17466         * Fires when the disabled status of this node changes
17467         * @param {Node} this This node
17468         * @param {Boolean} disabled
17469         */
17470         "disabledchange" : true,
17471         /**
17472         * @event collapse
17473         * Fires when this node is collapsed
17474         * @param {Node} this This node
17475         */
17476         "collapse" : true,
17477         /**
17478         * @event beforeclick
17479         * Fires before click processing. Return false to cancel the default action.
17480         * @param {Node} this This node
17481         * @param {Roo.EventObject} e The event object
17482         */
17483         "beforeclick":true,
17484         /**
17485         * @event checkchange
17486         * Fires when a node with a checkbox's checked property changes
17487         * @param {Node} this This node
17488         * @param {Boolean} checked
17489         */
17490         "checkchange":true,
17491         /**
17492         * @event click
17493         * Fires when this node is clicked
17494         * @param {Node} this This node
17495         * @param {Roo.EventObject} e The event object
17496         */
17497         "click":true,
17498         /**
17499         * @event dblclick
17500         * Fires when this node is double clicked
17501         * @param {Node} this This node
17502         * @param {Roo.EventObject} e The event object
17503         */
17504         "dblclick":true,
17505         /**
17506         * @event contextmenu
17507         * Fires when this node is right clicked
17508         * @param {Node} this This node
17509         * @param {Roo.EventObject} e The event object
17510         */
17511         "contextmenu":true,
17512         /**
17513         * @event beforechildrenrendered
17514         * Fires right before the child nodes for this node are rendered
17515         * @param {Node} this This node
17516         */
17517         "beforechildrenrendered":true
17518     });
17519
17520     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17521
17522     /**
17523      * Read-only. The UI for this node
17524      * @type TreeNodeUI
17525      */
17526     this.ui = new uiClass(this);
17527     
17528     // finally support items[]
17529     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17530         return;
17531     }
17532     
17533     
17534     Roo.each(this.attributes.items, function(c) {
17535         this.appendChild(Roo.factory(c,Roo.Tree));
17536     }, this);
17537     delete this.attributes.items;
17538     
17539     
17540     
17541 };
17542 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17543     preventHScroll: true,
17544     /**
17545      * Returns true if this node is expanded
17546      * @return {Boolean}
17547      */
17548     isExpanded : function(){
17549         return this.expanded;
17550     },
17551
17552     /**
17553      * Returns the UI object for this node
17554      * @return {TreeNodeUI}
17555      */
17556     getUI : function(){
17557         return this.ui;
17558     },
17559
17560     // private override
17561     setFirstChild : function(node){
17562         var of = this.firstChild;
17563         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17564         if(this.childrenRendered && of && node != of){
17565             of.renderIndent(true, true);
17566         }
17567         if(this.rendered){
17568             this.renderIndent(true, true);
17569         }
17570     },
17571
17572     // private override
17573     setLastChild : function(node){
17574         var ol = this.lastChild;
17575         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17576         if(this.childrenRendered && ol && node != ol){
17577             ol.renderIndent(true, true);
17578         }
17579         if(this.rendered){
17580             this.renderIndent(true, true);
17581         }
17582     },
17583
17584     // these methods are overridden to provide lazy rendering support
17585     // private override
17586     appendChild : function()
17587     {
17588         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17589         if(node && this.childrenRendered){
17590             node.render();
17591         }
17592         this.ui.updateExpandIcon();
17593         return node;
17594     },
17595
17596     // private override
17597     removeChild : function(node){
17598         this.ownerTree.getSelectionModel().unselect(node);
17599         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17600         // if it's been rendered remove dom node
17601         if(this.childrenRendered){
17602             node.ui.remove();
17603         }
17604         if(this.childNodes.length < 1){
17605             this.collapse(false, false);
17606         }else{
17607             this.ui.updateExpandIcon();
17608         }
17609         if(!this.firstChild) {
17610             this.childrenRendered = false;
17611         }
17612         return node;
17613     },
17614
17615     // private override
17616     insertBefore : function(node, refNode){
17617         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17618         if(newNode && refNode && this.childrenRendered){
17619             node.render();
17620         }
17621         this.ui.updateExpandIcon();
17622         return newNode;
17623     },
17624
17625     /**
17626      * Sets the text for this node
17627      * @param {String} text
17628      */
17629     setText : function(text){
17630         var oldText = this.text;
17631         this.text = text;
17632         this.attributes.text = text;
17633         if(this.rendered){ // event without subscribing
17634             this.ui.onTextChange(this, text, oldText);
17635         }
17636         this.fireEvent("textchange", this, text, oldText);
17637     },
17638
17639     /**
17640      * Triggers selection of this node
17641      */
17642     select : function(){
17643         this.getOwnerTree().getSelectionModel().select(this);
17644     },
17645
17646     /**
17647      * Triggers deselection of this node
17648      */
17649     unselect : function(){
17650         this.getOwnerTree().getSelectionModel().unselect(this);
17651     },
17652
17653     /**
17654      * Returns true if this node is selected
17655      * @return {Boolean}
17656      */
17657     isSelected : function(){
17658         return this.getOwnerTree().getSelectionModel().isSelected(this);
17659     },
17660
17661     /**
17662      * Expand this node.
17663      * @param {Boolean} deep (optional) True to expand all children as well
17664      * @param {Boolean} anim (optional) false to cancel the default animation
17665      * @param {Function} callback (optional) A callback to be called when
17666      * expanding this node completes (does not wait for deep expand to complete).
17667      * Called with 1 parameter, this node.
17668      */
17669     expand : function(deep, anim, callback){
17670         if(!this.expanded){
17671             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17672                 return;
17673             }
17674             if(!this.childrenRendered){
17675                 this.renderChildren();
17676             }
17677             this.expanded = true;
17678             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17679                 this.ui.animExpand(function(){
17680                     this.fireEvent("expand", this);
17681                     if(typeof callback == "function"){
17682                         callback(this);
17683                     }
17684                     if(deep === true){
17685                         this.expandChildNodes(true);
17686                     }
17687                 }.createDelegate(this));
17688                 return;
17689             }else{
17690                 this.ui.expand();
17691                 this.fireEvent("expand", this);
17692                 if(typeof callback == "function"){
17693                     callback(this);
17694                 }
17695             }
17696         }else{
17697            if(typeof callback == "function"){
17698                callback(this);
17699            }
17700         }
17701         if(deep === true){
17702             this.expandChildNodes(true);
17703         }
17704     },
17705
17706     isHiddenRoot : function(){
17707         return this.isRoot && !this.getOwnerTree().rootVisible;
17708     },
17709
17710     /**
17711      * Collapse this node.
17712      * @param {Boolean} deep (optional) True to collapse all children as well
17713      * @param {Boolean} anim (optional) false to cancel the default animation
17714      */
17715     collapse : function(deep, anim){
17716         if(this.expanded && !this.isHiddenRoot()){
17717             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17718                 return;
17719             }
17720             this.expanded = false;
17721             if((this.getOwnerTree().animate && anim !== false) || anim){
17722                 this.ui.animCollapse(function(){
17723                     this.fireEvent("collapse", this);
17724                     if(deep === true){
17725                         this.collapseChildNodes(true);
17726                     }
17727                 }.createDelegate(this));
17728                 return;
17729             }else{
17730                 this.ui.collapse();
17731                 this.fireEvent("collapse", this);
17732             }
17733         }
17734         if(deep === true){
17735             var cs = this.childNodes;
17736             for(var i = 0, len = cs.length; i < len; i++) {
17737                 cs[i].collapse(true, false);
17738             }
17739         }
17740     },
17741
17742     // private
17743     delayedExpand : function(delay){
17744         if(!this.expandProcId){
17745             this.expandProcId = this.expand.defer(delay, this);
17746         }
17747     },
17748
17749     // private
17750     cancelExpand : function(){
17751         if(this.expandProcId){
17752             clearTimeout(this.expandProcId);
17753         }
17754         this.expandProcId = false;
17755     },
17756
17757     /**
17758      * Toggles expanded/collapsed state of the node
17759      */
17760     toggle : function(){
17761         if(this.expanded){
17762             this.collapse();
17763         }else{
17764             this.expand();
17765         }
17766     },
17767
17768     /**
17769      * Ensures all parent nodes are expanded
17770      */
17771     ensureVisible : function(callback){
17772         var tree = this.getOwnerTree();
17773         tree.expandPath(this.parentNode.getPath(), false, function(){
17774             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17775             Roo.callback(callback);
17776         }.createDelegate(this));
17777     },
17778
17779     /**
17780      * Expand all child nodes
17781      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17782      */
17783     expandChildNodes : function(deep){
17784         var cs = this.childNodes;
17785         for(var i = 0, len = cs.length; i < len; i++) {
17786                 cs[i].expand(deep);
17787         }
17788     },
17789
17790     /**
17791      * Collapse all child nodes
17792      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17793      */
17794     collapseChildNodes : function(deep){
17795         var cs = this.childNodes;
17796         for(var i = 0, len = cs.length; i < len; i++) {
17797                 cs[i].collapse(deep);
17798         }
17799     },
17800
17801     /**
17802      * Disables this node
17803      */
17804     disable : function(){
17805         this.disabled = true;
17806         this.unselect();
17807         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17808             this.ui.onDisableChange(this, true);
17809         }
17810         this.fireEvent("disabledchange", this, true);
17811     },
17812
17813     /**
17814      * Enables this node
17815      */
17816     enable : function(){
17817         this.disabled = false;
17818         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17819             this.ui.onDisableChange(this, false);
17820         }
17821         this.fireEvent("disabledchange", this, false);
17822     },
17823
17824     // private
17825     renderChildren : function(suppressEvent){
17826         if(suppressEvent !== false){
17827             this.fireEvent("beforechildrenrendered", this);
17828         }
17829         var cs = this.childNodes;
17830         for(var i = 0, len = cs.length; i < len; i++){
17831             cs[i].render(true);
17832         }
17833         this.childrenRendered = true;
17834     },
17835
17836     // private
17837     sort : function(fn, scope){
17838         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17839         if(this.childrenRendered){
17840             var cs = this.childNodes;
17841             for(var i = 0, len = cs.length; i < len; i++){
17842                 cs[i].render(true);
17843             }
17844         }
17845     },
17846
17847     // private
17848     render : function(bulkRender){
17849         this.ui.render(bulkRender);
17850         if(!this.rendered){
17851             this.rendered = true;
17852             if(this.expanded){
17853                 this.expanded = false;
17854                 this.expand(false, false);
17855             }
17856         }
17857     },
17858
17859     // private
17860     renderIndent : function(deep, refresh){
17861         if(refresh){
17862             this.ui.childIndent = null;
17863         }
17864         this.ui.renderIndent();
17865         if(deep === true && this.childrenRendered){
17866             var cs = this.childNodes;
17867             for(var i = 0, len = cs.length; i < len; i++){
17868                 cs[i].renderIndent(true, refresh);
17869             }
17870         }
17871     }
17872 });/*
17873  * Based on:
17874  * Ext JS Library 1.1.1
17875  * Copyright(c) 2006-2007, Ext JS, LLC.
17876  *
17877  * Originally Released Under LGPL - original licence link has changed is not relivant.
17878  *
17879  * Fork - LGPL
17880  * <script type="text/javascript">
17881  */
17882  
17883 /**
17884  * @class Roo.tree.AsyncTreeNode
17885  * @extends Roo.tree.TreeNode
17886  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17887  * @constructor
17888  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17889  */
17890  Roo.tree.AsyncTreeNode = function(config){
17891     this.loaded = false;
17892     this.loading = false;
17893     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17894     /**
17895     * @event beforeload
17896     * Fires before this node is loaded, return false to cancel
17897     * @param {Node} this This node
17898     */
17899     this.addEvents({'beforeload':true, 'load': true});
17900     /**
17901     * @event load
17902     * Fires when this node is loaded
17903     * @param {Node} this This node
17904     */
17905     /**
17906      * The loader used by this node (defaults to using the tree's defined loader)
17907      * @type TreeLoader
17908      * @property loader
17909      */
17910 };
17911 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17912     expand : function(deep, anim, callback){
17913         if(this.loading){ // if an async load is already running, waiting til it's done
17914             var timer;
17915             var f = function(){
17916                 if(!this.loading){ // done loading
17917                     clearInterval(timer);
17918                     this.expand(deep, anim, callback);
17919                 }
17920             }.createDelegate(this);
17921             timer = setInterval(f, 200);
17922             return;
17923         }
17924         if(!this.loaded){
17925             if(this.fireEvent("beforeload", this) === false){
17926                 return;
17927             }
17928             this.loading = true;
17929             this.ui.beforeLoad(this);
17930             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17931             if(loader){
17932                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17933                 return;
17934             }
17935         }
17936         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17937     },
17938     
17939     /**
17940      * Returns true if this node is currently loading
17941      * @return {Boolean}
17942      */
17943     isLoading : function(){
17944         return this.loading;  
17945     },
17946     
17947     loadComplete : function(deep, anim, callback){
17948         this.loading = false;
17949         this.loaded = true;
17950         this.ui.afterLoad(this);
17951         this.fireEvent("load", this);
17952         this.expand(deep, anim, callback);
17953     },
17954     
17955     /**
17956      * Returns true if this node has been loaded
17957      * @return {Boolean}
17958      */
17959     isLoaded : function(){
17960         return this.loaded;
17961     },
17962     
17963     hasChildNodes : function(){
17964         if(!this.isLeaf() && !this.loaded){
17965             return true;
17966         }else{
17967             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17968         }
17969     },
17970
17971     /**
17972      * Trigger a reload for this node
17973      * @param {Function} callback
17974      */
17975     reload : function(callback){
17976         this.collapse(false, false);
17977         while(this.firstChild){
17978             this.removeChild(this.firstChild);
17979         }
17980         this.childrenRendered = false;
17981         this.loaded = false;
17982         if(this.isHiddenRoot()){
17983             this.expanded = false;
17984         }
17985         this.expand(false, false, callback);
17986     }
17987 });/*
17988  * Based on:
17989  * Ext JS Library 1.1.1
17990  * Copyright(c) 2006-2007, Ext JS, LLC.
17991  *
17992  * Originally Released Under LGPL - original licence link has changed is not relivant.
17993  *
17994  * Fork - LGPL
17995  * <script type="text/javascript">
17996  */
17997  
17998 /**
17999  * @class Roo.tree.TreeNodeUI
18000  * @constructor
18001  * @param {Object} node The node to render
18002  * The TreeNode UI implementation is separate from the
18003  * tree implementation. Unless you are customizing the tree UI,
18004  * you should never have to use this directly.
18005  */
18006 Roo.tree.TreeNodeUI = function(node){
18007     this.node = node;
18008     this.rendered = false;
18009     this.animating = false;
18010     this.emptyIcon = Roo.BLANK_IMAGE_URL;
18011 };
18012
18013 Roo.tree.TreeNodeUI.prototype = {
18014     removeChild : function(node){
18015         if(this.rendered){
18016             this.ctNode.removeChild(node.ui.getEl());
18017         }
18018     },
18019
18020     beforeLoad : function(){
18021          this.addClass("x-tree-node-loading");
18022     },
18023
18024     afterLoad : function(){
18025          this.removeClass("x-tree-node-loading");
18026     },
18027
18028     onTextChange : function(node, text, oldText){
18029         if(this.rendered){
18030             this.textNode.innerHTML = text;
18031         }
18032     },
18033
18034     onDisableChange : function(node, state){
18035         this.disabled = state;
18036         if(state){
18037             this.addClass("x-tree-node-disabled");
18038         }else{
18039             this.removeClass("x-tree-node-disabled");
18040         }
18041     },
18042
18043     onSelectedChange : function(state){
18044         if(state){
18045             this.focus();
18046             this.addClass("x-tree-selected");
18047         }else{
18048             //this.blur();
18049             this.removeClass("x-tree-selected");
18050         }
18051     },
18052
18053     onMove : function(tree, node, oldParent, newParent, index, refNode){
18054         this.childIndent = null;
18055         if(this.rendered){
18056             var targetNode = newParent.ui.getContainer();
18057             if(!targetNode){//target not rendered
18058                 this.holder = document.createElement("div");
18059                 this.holder.appendChild(this.wrap);
18060                 return;
18061             }
18062             var insertBefore = refNode ? refNode.ui.getEl() : null;
18063             if(insertBefore){
18064                 targetNode.insertBefore(this.wrap, insertBefore);
18065             }else{
18066                 targetNode.appendChild(this.wrap);
18067             }
18068             this.node.renderIndent(true);
18069         }
18070     },
18071
18072     addClass : function(cls){
18073         if(this.elNode){
18074             Roo.fly(this.elNode).addClass(cls);
18075         }
18076     },
18077
18078     removeClass : function(cls){
18079         if(this.elNode){
18080             Roo.fly(this.elNode).removeClass(cls);
18081         }
18082     },
18083
18084     remove : function(){
18085         if(this.rendered){
18086             this.holder = document.createElement("div");
18087             this.holder.appendChild(this.wrap);
18088         }
18089     },
18090
18091     fireEvent : function(){
18092         return this.node.fireEvent.apply(this.node, arguments);
18093     },
18094
18095     initEvents : function(){
18096         this.node.on("move", this.onMove, this);
18097         var E = Roo.EventManager;
18098         var a = this.anchor;
18099
18100         var el = Roo.fly(a, '_treeui');
18101
18102         if(Roo.isOpera){ // opera render bug ignores the CSS
18103             el.setStyle("text-decoration", "none");
18104         }
18105
18106         el.on("click", this.onClick, this);
18107         el.on("dblclick", this.onDblClick, this);
18108
18109         if(this.checkbox){
18110             Roo.EventManager.on(this.checkbox,
18111                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18112         }
18113
18114         el.on("contextmenu", this.onContextMenu, this);
18115
18116         var icon = Roo.fly(this.iconNode);
18117         icon.on("click", this.onClick, this);
18118         icon.on("dblclick", this.onDblClick, this);
18119         icon.on("contextmenu", this.onContextMenu, this);
18120         E.on(this.ecNode, "click", this.ecClick, this, true);
18121
18122         if(this.node.disabled){
18123             this.addClass("x-tree-node-disabled");
18124         }
18125         if(this.node.hidden){
18126             this.addClass("x-tree-node-disabled");
18127         }
18128         var ot = this.node.getOwnerTree();
18129         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18130         if(dd && (!this.node.isRoot || ot.rootVisible)){
18131             Roo.dd.Registry.register(this.elNode, {
18132                 node: this.node,
18133                 handles: this.getDDHandles(),
18134                 isHandle: false
18135             });
18136         }
18137     },
18138
18139     getDDHandles : function(){
18140         return [this.iconNode, this.textNode];
18141     },
18142
18143     hide : function(){
18144         if(this.rendered){
18145             this.wrap.style.display = "none";
18146         }
18147     },
18148
18149     show : function(){
18150         if(this.rendered){
18151             this.wrap.style.display = "";
18152         }
18153     },
18154
18155     onContextMenu : function(e){
18156         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18157             e.preventDefault();
18158             this.focus();
18159             this.fireEvent("contextmenu", this.node, e);
18160         }
18161     },
18162
18163     onClick : function(e){
18164         if(this.dropping){
18165             e.stopEvent();
18166             return;
18167         }
18168         if(this.fireEvent("beforeclick", this.node, e) !== false){
18169             if(!this.disabled && this.node.attributes.href){
18170                 this.fireEvent("click", this.node, e);
18171                 return;
18172             }
18173             e.preventDefault();
18174             if(this.disabled){
18175                 return;
18176             }
18177
18178             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18179                 this.node.toggle();
18180             }
18181
18182             this.fireEvent("click", this.node, e);
18183         }else{
18184             e.stopEvent();
18185         }
18186     },
18187
18188     onDblClick : function(e){
18189         e.preventDefault();
18190         if(this.disabled){
18191             return;
18192         }
18193         if(this.checkbox){
18194             this.toggleCheck();
18195         }
18196         if(!this.animating && this.node.hasChildNodes()){
18197             this.node.toggle();
18198         }
18199         this.fireEvent("dblclick", this.node, e);
18200     },
18201
18202     onCheckChange : function(){
18203         var checked = this.checkbox.checked;
18204         this.node.attributes.checked = checked;
18205         this.fireEvent('checkchange', this.node, checked);
18206     },
18207
18208     ecClick : function(e){
18209         if(!this.animating && this.node.hasChildNodes()){
18210             this.node.toggle();
18211         }
18212     },
18213
18214     startDrop : function(){
18215         this.dropping = true;
18216     },
18217
18218     // delayed drop so the click event doesn't get fired on a drop
18219     endDrop : function(){
18220        setTimeout(function(){
18221            this.dropping = false;
18222        }.createDelegate(this), 50);
18223     },
18224
18225     expand : function(){
18226         this.updateExpandIcon();
18227         this.ctNode.style.display = "";
18228     },
18229
18230     focus : function(){
18231         if(!this.node.preventHScroll){
18232             try{this.anchor.focus();
18233             }catch(e){}
18234         }else if(!Roo.isIE){
18235             try{
18236                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18237                 var l = noscroll.scrollLeft;
18238                 this.anchor.focus();
18239                 noscroll.scrollLeft = l;
18240             }catch(e){}
18241         }
18242     },
18243
18244     toggleCheck : function(value){
18245         var cb = this.checkbox;
18246         if(cb){
18247             cb.checked = (value === undefined ? !cb.checked : value);
18248         }
18249     },
18250
18251     blur : function(){
18252         try{
18253             this.anchor.blur();
18254         }catch(e){}
18255     },
18256
18257     animExpand : function(callback){
18258         var ct = Roo.get(this.ctNode);
18259         ct.stopFx();
18260         if(!this.node.hasChildNodes()){
18261             this.updateExpandIcon();
18262             this.ctNode.style.display = "";
18263             Roo.callback(callback);
18264             return;
18265         }
18266         this.animating = true;
18267         this.updateExpandIcon();
18268
18269         ct.slideIn('t', {
18270            callback : function(){
18271                this.animating = false;
18272                Roo.callback(callback);
18273             },
18274             scope: this,
18275             duration: this.node.ownerTree.duration || .25
18276         });
18277     },
18278
18279     highlight : function(){
18280         var tree = this.node.getOwnerTree();
18281         Roo.fly(this.wrap).highlight(
18282             tree.hlColor || "C3DAF9",
18283             {endColor: tree.hlBaseColor}
18284         );
18285     },
18286
18287     collapse : function(){
18288         this.updateExpandIcon();
18289         this.ctNode.style.display = "none";
18290     },
18291
18292     animCollapse : function(callback){
18293         var ct = Roo.get(this.ctNode);
18294         ct.enableDisplayMode('block');
18295         ct.stopFx();
18296
18297         this.animating = true;
18298         this.updateExpandIcon();
18299
18300         ct.slideOut('t', {
18301             callback : function(){
18302                this.animating = false;
18303                Roo.callback(callback);
18304             },
18305             scope: this,
18306             duration: this.node.ownerTree.duration || .25
18307         });
18308     },
18309
18310     getContainer : function(){
18311         return this.ctNode;
18312     },
18313
18314     getEl : function(){
18315         return this.wrap;
18316     },
18317
18318     appendDDGhost : function(ghostNode){
18319         ghostNode.appendChild(this.elNode.cloneNode(true));
18320     },
18321
18322     getDDRepairXY : function(){
18323         return Roo.lib.Dom.getXY(this.iconNode);
18324     },
18325
18326     onRender : function(){
18327         this.render();
18328     },
18329
18330     render : function(bulkRender){
18331         var n = this.node, a = n.attributes;
18332         var targetNode = n.parentNode ?
18333               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18334
18335         if(!this.rendered){
18336             this.rendered = true;
18337
18338             this.renderElements(n, a, targetNode, bulkRender);
18339
18340             if(a.qtip){
18341                if(this.textNode.setAttributeNS){
18342                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18343                    if(a.qtipTitle){
18344                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18345                    }
18346                }else{
18347                    this.textNode.setAttribute("ext:qtip", a.qtip);
18348                    if(a.qtipTitle){
18349                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18350                    }
18351                }
18352             }else if(a.qtipCfg){
18353                 a.qtipCfg.target = Roo.id(this.textNode);
18354                 Roo.QuickTips.register(a.qtipCfg);
18355             }
18356             this.initEvents();
18357             if(!this.node.expanded){
18358                 this.updateExpandIcon();
18359             }
18360         }else{
18361             if(bulkRender === true) {
18362                 targetNode.appendChild(this.wrap);
18363             }
18364         }
18365     },
18366
18367     renderElements : function(n, a, targetNode, bulkRender)
18368     {
18369         // add some indent caching, this helps performance when rendering a large tree
18370         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18371         var t = n.getOwnerTree();
18372         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18373         if (typeof(n.attributes.html) != 'undefined') {
18374             txt = n.attributes.html;
18375         }
18376         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18377         var cb = typeof a.checked == 'boolean';
18378         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18379         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18380             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18381             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18382             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18383             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18384             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18385              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18386                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18387             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18388             "</li>"];
18389
18390         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18391             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18392                                 n.nextSibling.ui.getEl(), buf.join(""));
18393         }else{
18394             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18395         }
18396
18397         this.elNode = this.wrap.childNodes[0];
18398         this.ctNode = this.wrap.childNodes[1];
18399         var cs = this.elNode.childNodes;
18400         this.indentNode = cs[0];
18401         this.ecNode = cs[1];
18402         this.iconNode = cs[2];
18403         var index = 3;
18404         if(cb){
18405             this.checkbox = cs[3];
18406             index++;
18407         }
18408         this.anchor = cs[index];
18409         this.textNode = cs[index].firstChild;
18410     },
18411
18412     getAnchor : function(){
18413         return this.anchor;
18414     },
18415
18416     getTextEl : function(){
18417         return this.textNode;
18418     },
18419
18420     getIconEl : function(){
18421         return this.iconNode;
18422     },
18423
18424     isChecked : function(){
18425         return this.checkbox ? this.checkbox.checked : false;
18426     },
18427
18428     updateExpandIcon : function(){
18429         if(this.rendered){
18430             var n = this.node, c1, c2;
18431             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18432             var hasChild = n.hasChildNodes();
18433             if(hasChild){
18434                 if(n.expanded){
18435                     cls += "-minus";
18436                     c1 = "x-tree-node-collapsed";
18437                     c2 = "x-tree-node-expanded";
18438                 }else{
18439                     cls += "-plus";
18440                     c1 = "x-tree-node-expanded";
18441                     c2 = "x-tree-node-collapsed";
18442                 }
18443                 if(this.wasLeaf){
18444                     this.removeClass("x-tree-node-leaf");
18445                     this.wasLeaf = false;
18446                 }
18447                 if(this.c1 != c1 || this.c2 != c2){
18448                     Roo.fly(this.elNode).replaceClass(c1, c2);
18449                     this.c1 = c1; this.c2 = c2;
18450                 }
18451             }else{
18452                 // this changes non-leafs into leafs if they have no children.
18453                 // it's not very rational behaviour..
18454                 
18455                 if(!this.wasLeaf && this.node.leaf){
18456                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18457                     delete this.c1;
18458                     delete this.c2;
18459                     this.wasLeaf = true;
18460                 }
18461             }
18462             var ecc = "x-tree-ec-icon "+cls;
18463             if(this.ecc != ecc){
18464                 this.ecNode.className = ecc;
18465                 this.ecc = ecc;
18466             }
18467         }
18468     },
18469
18470     getChildIndent : function(){
18471         if(!this.childIndent){
18472             var buf = [];
18473             var p = this.node;
18474             while(p){
18475                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18476                     if(!p.isLast()) {
18477                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18478                     } else {
18479                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18480                     }
18481                 }
18482                 p = p.parentNode;
18483             }
18484             this.childIndent = buf.join("");
18485         }
18486         return this.childIndent;
18487     },
18488
18489     renderIndent : function(){
18490         if(this.rendered){
18491             var indent = "";
18492             var p = this.node.parentNode;
18493             if(p){
18494                 indent = p.ui.getChildIndent();
18495             }
18496             if(this.indentMarkup != indent){ // don't rerender if not required
18497                 this.indentNode.innerHTML = indent;
18498                 this.indentMarkup = indent;
18499             }
18500             this.updateExpandIcon();
18501         }
18502     }
18503 };
18504
18505 Roo.tree.RootTreeNodeUI = function(){
18506     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18507 };
18508 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18509     render : function(){
18510         if(!this.rendered){
18511             var targetNode = this.node.ownerTree.innerCt.dom;
18512             this.node.expanded = true;
18513             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18514             this.wrap = this.ctNode = targetNode.firstChild;
18515         }
18516     },
18517     collapse : function(){
18518     },
18519     expand : function(){
18520     }
18521 });/*
18522  * Based on:
18523  * Ext JS Library 1.1.1
18524  * Copyright(c) 2006-2007, Ext JS, LLC.
18525  *
18526  * Originally Released Under LGPL - original licence link has changed is not relivant.
18527  *
18528  * Fork - LGPL
18529  * <script type="text/javascript">
18530  */
18531 /**
18532  * @class Roo.tree.TreeLoader
18533  * @extends Roo.util.Observable
18534  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18535  * nodes from a specified URL. The response must be a javascript Array definition
18536  * who's elements are node definition objects. eg:
18537  * <pre><code>
18538 {  success : true,
18539    data :      [
18540    
18541     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18542     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18543     ]
18544 }
18545
18546
18547 </code></pre>
18548  * <br><br>
18549  * The old style respose with just an array is still supported, but not recommended.
18550  * <br><br>
18551  *
18552  * A server request is sent, and child nodes are loaded only when a node is expanded.
18553  * The loading node's id is passed to the server under the parameter name "node" to
18554  * enable the server to produce the correct child nodes.
18555  * <br><br>
18556  * To pass extra parameters, an event handler may be attached to the "beforeload"
18557  * event, and the parameters specified in the TreeLoader's baseParams property:
18558  * <pre><code>
18559     myTreeLoader.on("beforeload", function(treeLoader, node) {
18560         this.baseParams.category = node.attributes.category;
18561     }, this);
18562 </code></pre><
18563  * This would pass an HTTP parameter called "category" to the server containing
18564  * the value of the Node's "category" attribute.
18565  * @constructor
18566  * Creates a new Treeloader.
18567  * @param {Object} config A config object containing config properties.
18568  */
18569 Roo.tree.TreeLoader = function(config){
18570     this.baseParams = {};
18571     this.requestMethod = "POST";
18572     Roo.apply(this, config);
18573
18574     this.addEvents({
18575     
18576         /**
18577          * @event beforeload
18578          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18579          * @param {Object} This TreeLoader object.
18580          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18581          * @param {Object} callback The callback function specified in the {@link #load} call.
18582          */
18583         beforeload : true,
18584         /**
18585          * @event load
18586          * Fires when the node has been successfuly loaded.
18587          * @param {Object} This TreeLoader object.
18588          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18589          * @param {Object} response The response object containing the data from the server.
18590          */
18591         load : true,
18592         /**
18593          * @event loadexception
18594          * Fires if the network request failed.
18595          * @param {Object} This TreeLoader object.
18596          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18597          * @param {Object} response The response object containing the data from the server.
18598          */
18599         loadexception : true,
18600         /**
18601          * @event create
18602          * Fires before a node is created, enabling you to return custom Node types 
18603          * @param {Object} This TreeLoader object.
18604          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18605          */
18606         create : true
18607     });
18608
18609     Roo.tree.TreeLoader.superclass.constructor.call(this);
18610 };
18611
18612 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18613     /**
18614     * @cfg {String} dataUrl The URL from which to request a Json string which
18615     * specifies an array of node definition object representing the child nodes
18616     * to be loaded.
18617     */
18618     /**
18619     * @cfg {String} requestMethod either GET or POST
18620     * defaults to POST (due to BC)
18621     * to be loaded.
18622     */
18623     /**
18624     * @cfg {Object} baseParams (optional) An object containing properties which
18625     * specify HTTP parameters to be passed to each request for child nodes.
18626     */
18627     /**
18628     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18629     * created by this loader. If the attributes sent by the server have an attribute in this object,
18630     * they take priority.
18631     */
18632     /**
18633     * @cfg {Object} uiProviders (optional) An object containing properties which
18634     * 
18635     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18636     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18637     * <i>uiProvider</i> attribute of a returned child node is a string rather
18638     * than a reference to a TreeNodeUI implementation, this that string value
18639     * is used as a property name in the uiProviders object. You can define the provider named
18640     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18641     */
18642     uiProviders : {},
18643
18644     /**
18645     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18646     * child nodes before loading.
18647     */
18648     clearOnLoad : true,
18649
18650     /**
18651     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18652     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18653     * Grid query { data : [ .....] }
18654     */
18655     
18656     root : false,
18657      /**
18658     * @cfg {String} queryParam (optional) 
18659     * Name of the query as it will be passed on the querystring (defaults to 'node')
18660     * eg. the request will be ?node=[id]
18661     */
18662     
18663     
18664     queryParam: false,
18665     
18666     /**
18667      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18668      * This is called automatically when a node is expanded, but may be used to reload
18669      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18670      * @param {Roo.tree.TreeNode} node
18671      * @param {Function} callback
18672      */
18673     load : function(node, callback){
18674         if(this.clearOnLoad){
18675             while(node.firstChild){
18676                 node.removeChild(node.firstChild);
18677             }
18678         }
18679         if(node.attributes.children){ // preloaded json children
18680             var cs = node.attributes.children;
18681             for(var i = 0, len = cs.length; i < len; i++){
18682                 node.appendChild(this.createNode(cs[i]));
18683             }
18684             if(typeof callback == "function"){
18685                 callback();
18686             }
18687         }else if(this.dataUrl){
18688             this.requestData(node, callback);
18689         }
18690     },
18691
18692     getParams: function(node){
18693         var buf = [], bp = this.baseParams;
18694         for(var key in bp){
18695             if(typeof bp[key] != "function"){
18696                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18697             }
18698         }
18699         var n = this.queryParam === false ? 'node' : this.queryParam;
18700         buf.push(n + "=", encodeURIComponent(node.id));
18701         return buf.join("");
18702     },
18703
18704     requestData : function(node, callback){
18705         if(this.fireEvent("beforeload", this, node, callback) !== false){
18706             this.transId = Roo.Ajax.request({
18707                 method:this.requestMethod,
18708                 url: this.dataUrl||this.url,
18709                 success: this.handleResponse,
18710                 failure: this.handleFailure,
18711                 scope: this,
18712                 argument: {callback: callback, node: node},
18713                 params: this.getParams(node)
18714             });
18715         }else{
18716             // if the load is cancelled, make sure we notify
18717             // the node that we are done
18718             if(typeof callback == "function"){
18719                 callback();
18720             }
18721         }
18722     },
18723
18724     isLoading : function(){
18725         return this.transId ? true : false;
18726     },
18727
18728     abort : function(){
18729         if(this.isLoading()){
18730             Roo.Ajax.abort(this.transId);
18731         }
18732     },
18733
18734     // private
18735     createNode : function(attr)
18736     {
18737         // apply baseAttrs, nice idea Corey!
18738         if(this.baseAttrs){
18739             Roo.applyIf(attr, this.baseAttrs);
18740         }
18741         if(this.applyLoader !== false){
18742             attr.loader = this;
18743         }
18744         // uiProvider = depreciated..
18745         
18746         if(typeof(attr.uiProvider) == 'string'){
18747            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18748                 /**  eval:var:attr */ eval(attr.uiProvider);
18749         }
18750         if(typeof(this.uiProviders['default']) != 'undefined') {
18751             attr.uiProvider = this.uiProviders['default'];
18752         }
18753         
18754         this.fireEvent('create', this, attr);
18755         
18756         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18757         return(attr.leaf ?
18758                         new Roo.tree.TreeNode(attr) :
18759                         new Roo.tree.AsyncTreeNode(attr));
18760     },
18761
18762     processResponse : function(response, node, callback)
18763     {
18764         var json = response.responseText;
18765         try {
18766             
18767             var o = Roo.decode(json);
18768             
18769             if (this.root === false && typeof(o.success) != undefined) {
18770                 this.root = 'data'; // the default behaviour for list like data..
18771                 }
18772                 
18773             if (this.root !== false &&  !o.success) {
18774                 // it's a failure condition.
18775                 var a = response.argument;
18776                 this.fireEvent("loadexception", this, a.node, response);
18777                 Roo.log("Load failed - should have a handler really");
18778                 return;
18779             }
18780             
18781             
18782             
18783             if (this.root !== false) {
18784                  o = o[this.root];
18785             }
18786             
18787             for(var i = 0, len = o.length; i < len; i++){
18788                 var n = this.createNode(o[i]);
18789                 if(n){
18790                     node.appendChild(n);
18791                 }
18792             }
18793             if(typeof callback == "function"){
18794                 callback(this, node);
18795             }
18796         }catch(e){
18797             this.handleFailure(response);
18798         }
18799     },
18800
18801     handleResponse : function(response){
18802         this.transId = false;
18803         var a = response.argument;
18804         this.processResponse(response, a.node, a.callback);
18805         this.fireEvent("load", this, a.node, response);
18806     },
18807
18808     handleFailure : function(response)
18809     {
18810         // should handle failure better..
18811         this.transId = false;
18812         var a = response.argument;
18813         this.fireEvent("loadexception", this, a.node, response);
18814         if(typeof a.callback == "function"){
18815             a.callback(this, a.node);
18816         }
18817     }
18818 });/*
18819  * Based on:
18820  * Ext JS Library 1.1.1
18821  * Copyright(c) 2006-2007, Ext JS, LLC.
18822  *
18823  * Originally Released Under LGPL - original licence link has changed is not relivant.
18824  *
18825  * Fork - LGPL
18826  * <script type="text/javascript">
18827  */
18828
18829 /**
18830 * @class Roo.tree.TreeFilter
18831 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18832 * @param {TreePanel} tree
18833 * @param {Object} config (optional)
18834  */
18835 Roo.tree.TreeFilter = function(tree, config){
18836     this.tree = tree;
18837     this.filtered = {};
18838     Roo.apply(this, config);
18839 };
18840
18841 Roo.tree.TreeFilter.prototype = {
18842     clearBlank:false,
18843     reverse:false,
18844     autoClear:false,
18845     remove:false,
18846
18847      /**
18848      * Filter the data by a specific attribute.
18849      * @param {String/RegExp} value Either string that the attribute value
18850      * should start with or a RegExp to test against the attribute
18851      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18852      * @param {TreeNode} startNode (optional) The node to start the filter at.
18853      */
18854     filter : function(value, attr, startNode){
18855         attr = attr || "text";
18856         var f;
18857         if(typeof value == "string"){
18858             var vlen = value.length;
18859             // auto clear empty filter
18860             if(vlen == 0 && this.clearBlank){
18861                 this.clear();
18862                 return;
18863             }
18864             value = value.toLowerCase();
18865             f = function(n){
18866                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18867             };
18868         }else if(value.exec){ // regex?
18869             f = function(n){
18870                 return value.test(n.attributes[attr]);
18871             };
18872         }else{
18873             throw 'Illegal filter type, must be string or regex';
18874         }
18875         this.filterBy(f, null, startNode);
18876         },
18877
18878     /**
18879      * Filter by a function. The passed function will be called with each
18880      * node in the tree (or from the startNode). If the function returns true, the node is kept
18881      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18882      * @param {Function} fn The filter function
18883      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18884      */
18885     filterBy : function(fn, scope, startNode){
18886         startNode = startNode || this.tree.root;
18887         if(this.autoClear){
18888             this.clear();
18889         }
18890         var af = this.filtered, rv = this.reverse;
18891         var f = function(n){
18892             if(n == startNode){
18893                 return true;
18894             }
18895             if(af[n.id]){
18896                 return false;
18897             }
18898             var m = fn.call(scope || n, n);
18899             if(!m || rv){
18900                 af[n.id] = n;
18901                 n.ui.hide();
18902                 return false;
18903             }
18904             return true;
18905         };
18906         startNode.cascade(f);
18907         if(this.remove){
18908            for(var id in af){
18909                if(typeof id != "function"){
18910                    var n = af[id];
18911                    if(n && n.parentNode){
18912                        n.parentNode.removeChild(n);
18913                    }
18914                }
18915            }
18916         }
18917     },
18918
18919     /**
18920      * Clears the current filter. Note: with the "remove" option
18921      * set a filter cannot be cleared.
18922      */
18923     clear : function(){
18924         var t = this.tree;
18925         var af = this.filtered;
18926         for(var id in af){
18927             if(typeof id != "function"){
18928                 var n = af[id];
18929                 if(n){
18930                     n.ui.show();
18931                 }
18932             }
18933         }
18934         this.filtered = {};
18935     }
18936 };
18937 /*
18938  * Based on:
18939  * Ext JS Library 1.1.1
18940  * Copyright(c) 2006-2007, Ext JS, LLC.
18941  *
18942  * Originally Released Under LGPL - original licence link has changed is not relivant.
18943  *
18944  * Fork - LGPL
18945  * <script type="text/javascript">
18946  */
18947  
18948
18949 /**
18950  * @class Roo.tree.TreeSorter
18951  * Provides sorting of nodes in a TreePanel
18952  * 
18953  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18954  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18955  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18956  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18957  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18958  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18959  * @constructor
18960  * @param {TreePanel} tree
18961  * @param {Object} config
18962  */
18963 Roo.tree.TreeSorter = function(tree, config){
18964     Roo.apply(this, config);
18965     tree.on("beforechildrenrendered", this.doSort, this);
18966     tree.on("append", this.updateSort, this);
18967     tree.on("insert", this.updateSort, this);
18968     
18969     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18970     var p = this.property || "text";
18971     var sortType = this.sortType;
18972     var fs = this.folderSort;
18973     var cs = this.caseSensitive === true;
18974     var leafAttr = this.leafAttr || 'leaf';
18975
18976     this.sortFn = function(n1, n2){
18977         if(fs){
18978             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18979                 return 1;
18980             }
18981             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18982                 return -1;
18983             }
18984         }
18985         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18986         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18987         if(v1 < v2){
18988                         return dsc ? +1 : -1;
18989                 }else if(v1 > v2){
18990                         return dsc ? -1 : +1;
18991         }else{
18992                 return 0;
18993         }
18994     };
18995 };
18996
18997 Roo.tree.TreeSorter.prototype = {
18998     doSort : function(node){
18999         node.sort(this.sortFn);
19000     },
19001     
19002     compareNodes : function(n1, n2){
19003         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
19004     },
19005     
19006     updateSort : function(tree, node){
19007         if(node.childrenRendered){
19008             this.doSort.defer(1, this, [node]);
19009         }
19010     }
19011 };/*
19012  * Based on:
19013  * Ext JS Library 1.1.1
19014  * Copyright(c) 2006-2007, Ext JS, LLC.
19015  *
19016  * Originally Released Under LGPL - original licence link has changed is not relivant.
19017  *
19018  * Fork - LGPL
19019  * <script type="text/javascript">
19020  */
19021
19022 if(Roo.dd.DropZone){
19023     
19024 Roo.tree.TreeDropZone = function(tree, config){
19025     this.allowParentInsert = false;
19026     this.allowContainerDrop = false;
19027     this.appendOnly = false;
19028     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19029     this.tree = tree;
19030     this.lastInsertClass = "x-tree-no-status";
19031     this.dragOverData = {};
19032 };
19033
19034 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
19035     ddGroup : "TreeDD",
19036     scroll:  true,
19037     
19038     expandDelay : 1000,
19039     
19040     expandNode : function(node){
19041         if(node.hasChildNodes() && !node.isExpanded()){
19042             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
19043         }
19044     },
19045     
19046     queueExpand : function(node){
19047         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
19048     },
19049     
19050     cancelExpand : function(){
19051         if(this.expandProcId){
19052             clearTimeout(this.expandProcId);
19053             this.expandProcId = false;
19054         }
19055     },
19056     
19057     isValidDropPoint : function(n, pt, dd, e, data){
19058         if(!n || !data){ return false; }
19059         var targetNode = n.node;
19060         var dropNode = data.node;
19061         // default drop rules
19062         if(!(targetNode && targetNode.isTarget && pt)){
19063             return false;
19064         }
19065         if(pt == "append" && targetNode.allowChildren === false){
19066             return false;
19067         }
19068         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
19069             return false;
19070         }
19071         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
19072             return false;
19073         }
19074         // reuse the object
19075         var overEvent = this.dragOverData;
19076         overEvent.tree = this.tree;
19077         overEvent.target = targetNode;
19078         overEvent.data = data;
19079         overEvent.point = pt;
19080         overEvent.source = dd;
19081         overEvent.rawEvent = e;
19082         overEvent.dropNode = dropNode;
19083         overEvent.cancel = false;  
19084         var result = this.tree.fireEvent("nodedragover", overEvent);
19085         return overEvent.cancel === false && result !== false;
19086     },
19087     
19088     getDropPoint : function(e, n, dd)
19089     {
19090         var tn = n.node;
19091         if(tn.isRoot){
19092             return tn.allowChildren !== false ? "append" : false; // always append for root
19093         }
19094         var dragEl = n.ddel;
19095         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19096         var y = Roo.lib.Event.getPageY(e);
19097         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19098         
19099         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19100         var noAppend = tn.allowChildren === false;
19101         if(this.appendOnly || tn.parentNode.allowChildren === false){
19102             return noAppend ? false : "append";
19103         }
19104         var noBelow = false;
19105         if(!this.allowParentInsert){
19106             noBelow = tn.hasChildNodes() && tn.isExpanded();
19107         }
19108         var q = (b - t) / (noAppend ? 2 : 3);
19109         if(y >= t && y < (t + q)){
19110             return "above";
19111         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19112             return "below";
19113         }else{
19114             return "append";
19115         }
19116     },
19117     
19118     onNodeEnter : function(n, dd, e, data)
19119     {
19120         this.cancelExpand();
19121     },
19122     
19123     onNodeOver : function(n, dd, e, data)
19124     {
19125        
19126         var pt = this.getDropPoint(e, n, dd);
19127         var node = n.node;
19128         
19129         // auto node expand check
19130         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19131             this.queueExpand(node);
19132         }else if(pt != "append"){
19133             this.cancelExpand();
19134         }
19135         
19136         // set the insert point style on the target node
19137         var returnCls = this.dropNotAllowed;
19138         if(this.isValidDropPoint(n, pt, dd, e, data)){
19139            if(pt){
19140                var el = n.ddel;
19141                var cls;
19142                if(pt == "above"){
19143                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19144                    cls = "x-tree-drag-insert-above";
19145                }else if(pt == "below"){
19146                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19147                    cls = "x-tree-drag-insert-below";
19148                }else{
19149                    returnCls = "x-tree-drop-ok-append";
19150                    cls = "x-tree-drag-append";
19151                }
19152                if(this.lastInsertClass != cls){
19153                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19154                    this.lastInsertClass = cls;
19155                }
19156            }
19157        }
19158        return returnCls;
19159     },
19160     
19161     onNodeOut : function(n, dd, e, data){
19162         
19163         this.cancelExpand();
19164         this.removeDropIndicators(n);
19165     },
19166     
19167     onNodeDrop : function(n, dd, e, data){
19168         var point = this.getDropPoint(e, n, dd);
19169         var targetNode = n.node;
19170         targetNode.ui.startDrop();
19171         if(!this.isValidDropPoint(n, point, dd, e, data)){
19172             targetNode.ui.endDrop();
19173             return false;
19174         }
19175         // first try to find the drop node
19176         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19177         var dropEvent = {
19178             tree : this.tree,
19179             target: targetNode,
19180             data: data,
19181             point: point,
19182             source: dd,
19183             rawEvent: e,
19184             dropNode: dropNode,
19185             cancel: !dropNode   
19186         };
19187         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19188         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19189             targetNode.ui.endDrop();
19190             return false;
19191         }
19192         // allow target changing
19193         targetNode = dropEvent.target;
19194         if(point == "append" && !targetNode.isExpanded()){
19195             targetNode.expand(false, null, function(){
19196                 this.completeDrop(dropEvent);
19197             }.createDelegate(this));
19198         }else{
19199             this.completeDrop(dropEvent);
19200         }
19201         return true;
19202     },
19203     
19204     completeDrop : function(de){
19205         var ns = de.dropNode, p = de.point, t = de.target;
19206         if(!(ns instanceof Array)){
19207             ns = [ns];
19208         }
19209         var n;
19210         for(var i = 0, len = ns.length; i < len; i++){
19211             n = ns[i];
19212             if(p == "above"){
19213                 t.parentNode.insertBefore(n, t);
19214             }else if(p == "below"){
19215                 t.parentNode.insertBefore(n, t.nextSibling);
19216             }else{
19217                 t.appendChild(n);
19218             }
19219         }
19220         n.ui.focus();
19221         if(this.tree.hlDrop){
19222             n.ui.highlight();
19223         }
19224         t.ui.endDrop();
19225         this.tree.fireEvent("nodedrop", de);
19226     },
19227     
19228     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19229         if(this.tree.hlDrop){
19230             dropNode.ui.focus();
19231             dropNode.ui.highlight();
19232         }
19233         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19234     },
19235     
19236     getTree : function(){
19237         return this.tree;
19238     },
19239     
19240     removeDropIndicators : function(n){
19241         if(n && n.ddel){
19242             var el = n.ddel;
19243             Roo.fly(el).removeClass([
19244                     "x-tree-drag-insert-above",
19245                     "x-tree-drag-insert-below",
19246                     "x-tree-drag-append"]);
19247             this.lastInsertClass = "_noclass";
19248         }
19249     },
19250     
19251     beforeDragDrop : function(target, e, id){
19252         this.cancelExpand();
19253         return true;
19254     },
19255     
19256     afterRepair : function(data){
19257         if(data && Roo.enableFx){
19258             data.node.ui.highlight();
19259         }
19260         this.hideProxy();
19261     } 
19262     
19263 });
19264
19265 }
19266 /*
19267  * Based on:
19268  * Ext JS Library 1.1.1
19269  * Copyright(c) 2006-2007, Ext JS, LLC.
19270  *
19271  * Originally Released Under LGPL - original licence link has changed is not relivant.
19272  *
19273  * Fork - LGPL
19274  * <script type="text/javascript">
19275  */
19276  
19277
19278 if(Roo.dd.DragZone){
19279 Roo.tree.TreeDragZone = function(tree, config){
19280     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19281     this.tree = tree;
19282 };
19283
19284 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19285     ddGroup : "TreeDD",
19286    
19287     onBeforeDrag : function(data, e){
19288         var n = data.node;
19289         return n && n.draggable && !n.disabled;
19290     },
19291      
19292     
19293     onInitDrag : function(e){
19294         var data = this.dragData;
19295         this.tree.getSelectionModel().select(data.node);
19296         this.proxy.update("");
19297         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19298         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19299     },
19300     
19301     getRepairXY : function(e, data){
19302         return data.node.ui.getDDRepairXY();
19303     },
19304     
19305     onEndDrag : function(data, e){
19306         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19307         
19308         
19309     },
19310     
19311     onValidDrop : function(dd, e, id){
19312         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19313         this.hideProxy();
19314     },
19315     
19316     beforeInvalidDrop : function(e, id){
19317         // this scrolls the original position back into view
19318         var sm = this.tree.getSelectionModel();
19319         sm.clearSelections();
19320         sm.select(this.dragData.node);
19321     }
19322 });
19323 }/*
19324  * Based on:
19325  * Ext JS Library 1.1.1
19326  * Copyright(c) 2006-2007, Ext JS, LLC.
19327  *
19328  * Originally Released Under LGPL - original licence link has changed is not relivant.
19329  *
19330  * Fork - LGPL
19331  * <script type="text/javascript">
19332  */
19333 /**
19334  * @class Roo.tree.TreeEditor
19335  * @extends Roo.Editor
19336  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19337  * as the editor field.
19338  * @constructor
19339  * @param {Object} config (used to be the tree panel.)
19340  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19341  * 
19342  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19343  * @cfg {Roo.form.TextField|Object} field The field configuration
19344  *
19345  * 
19346  */
19347 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19348     var tree = config;
19349     var field;
19350     if (oldconfig) { // old style..
19351         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19352     } else {
19353         // new style..
19354         tree = config.tree;
19355         config.field = config.field  || {};
19356         config.field.xtype = 'TextField';
19357         field = Roo.factory(config.field, Roo.form);
19358     }
19359     config = config || {};
19360     
19361     
19362     this.addEvents({
19363         /**
19364          * @event beforenodeedit
19365          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19366          * false from the handler of this event.
19367          * @param {Editor} this
19368          * @param {Roo.tree.Node} node 
19369          */
19370         "beforenodeedit" : true
19371     });
19372     
19373     //Roo.log(config);
19374     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19375
19376     this.tree = tree;
19377
19378     tree.on('beforeclick', this.beforeNodeClick, this);
19379     tree.getTreeEl().on('mousedown', this.hide, this);
19380     this.on('complete', this.updateNode, this);
19381     this.on('beforestartedit', this.fitToTree, this);
19382     this.on('startedit', this.bindScroll, this, {delay:10});
19383     this.on('specialkey', this.onSpecialKey, this);
19384 };
19385
19386 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19387     /**
19388      * @cfg {String} alignment
19389      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19390      */
19391     alignment: "l-l",
19392     // inherit
19393     autoSize: false,
19394     /**
19395      * @cfg {Boolean} hideEl
19396      * True to hide the bound element while the editor is displayed (defaults to false)
19397      */
19398     hideEl : false,
19399     /**
19400      * @cfg {String} cls
19401      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19402      */
19403     cls: "x-small-editor x-tree-editor",
19404     /**
19405      * @cfg {Boolean} shim
19406      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19407      */
19408     shim:false,
19409     // inherit
19410     shadow:"frame",
19411     /**
19412      * @cfg {Number} maxWidth
19413      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19414      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19415      * scroll and client offsets into account prior to each edit.
19416      */
19417     maxWidth: 250,
19418
19419     editDelay : 350,
19420
19421     // private
19422     fitToTree : function(ed, el){
19423         var td = this.tree.getTreeEl().dom, nd = el.dom;
19424         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19425             td.scrollLeft = nd.offsetLeft;
19426         }
19427         var w = Math.min(
19428                 this.maxWidth,
19429                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19430         this.setSize(w, '');
19431         
19432         return this.fireEvent('beforenodeedit', this, this.editNode);
19433         
19434     },
19435
19436     // private
19437     triggerEdit : function(node){
19438         this.completeEdit();
19439         this.editNode = node;
19440         this.startEdit(node.ui.textNode, node.text);
19441     },
19442
19443     // private
19444     bindScroll : function(){
19445         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19446     },
19447
19448     // private
19449     beforeNodeClick : function(node, e){
19450         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19451         this.lastClick = new Date();
19452         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19453             e.stopEvent();
19454             this.triggerEdit(node);
19455             return false;
19456         }
19457         return true;
19458     },
19459
19460     // private
19461     updateNode : function(ed, value){
19462         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19463         this.editNode.setText(value);
19464     },
19465
19466     // private
19467     onHide : function(){
19468         Roo.tree.TreeEditor.superclass.onHide.call(this);
19469         if(this.editNode){
19470             this.editNode.ui.focus();
19471         }
19472     },
19473
19474     // private
19475     onSpecialKey : function(field, e){
19476         var k = e.getKey();
19477         if(k == e.ESC){
19478             e.stopEvent();
19479             this.cancelEdit();
19480         }else if(k == e.ENTER && !e.hasModifier()){
19481             e.stopEvent();
19482             this.completeEdit();
19483         }
19484     }
19485 });//<Script type="text/javascript">
19486 /*
19487  * Based on:
19488  * Ext JS Library 1.1.1
19489  * Copyright(c) 2006-2007, Ext JS, LLC.
19490  *
19491  * Originally Released Under LGPL - original licence link has changed is not relivant.
19492  *
19493  * Fork - LGPL
19494  * <script type="text/javascript">
19495  */
19496  
19497 /**
19498  * Not documented??? - probably should be...
19499  */
19500
19501 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19502     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19503     
19504     renderElements : function(n, a, targetNode, bulkRender){
19505         //consel.log("renderElements?");
19506         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19507
19508         var t = n.getOwnerTree();
19509         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19510         
19511         var cols = t.columns;
19512         var bw = t.borderWidth;
19513         var c = cols[0];
19514         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19515          var cb = typeof a.checked == "boolean";
19516         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19517         var colcls = 'x-t-' + tid + '-c0';
19518         var buf = [
19519             '<li class="x-tree-node">',
19520             
19521                 
19522                 '<div class="x-tree-node-el ', a.cls,'">',
19523                     // extran...
19524                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19525                 
19526                 
19527                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19528                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19529                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19530                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19531                            (a.iconCls ? ' '+a.iconCls : ''),
19532                            '" unselectable="on" />',
19533                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19534                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19535                              
19536                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19537                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19538                             '<span unselectable="on" qtip="' + tx + '">',
19539                              tx,
19540                              '</span></a>' ,
19541                     '</div>',
19542                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19543                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19544                  ];
19545         for(var i = 1, len = cols.length; i < len; i++){
19546             c = cols[i];
19547             colcls = 'x-t-' + tid + '-c' +i;
19548             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19549             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19550                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19551                       "</div>");
19552          }
19553          
19554          buf.push(
19555             '</a>',
19556             '<div class="x-clear"></div></div>',
19557             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19558             "</li>");
19559         
19560         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19561             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19562                                 n.nextSibling.ui.getEl(), buf.join(""));
19563         }else{
19564             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19565         }
19566         var el = this.wrap.firstChild;
19567         this.elRow = el;
19568         this.elNode = el.firstChild;
19569         this.ranchor = el.childNodes[1];
19570         this.ctNode = this.wrap.childNodes[1];
19571         var cs = el.firstChild.childNodes;
19572         this.indentNode = cs[0];
19573         this.ecNode = cs[1];
19574         this.iconNode = cs[2];
19575         var index = 3;
19576         if(cb){
19577             this.checkbox = cs[3];
19578             index++;
19579         }
19580         this.anchor = cs[index];
19581         
19582         this.textNode = cs[index].firstChild;
19583         
19584         //el.on("click", this.onClick, this);
19585         //el.on("dblclick", this.onDblClick, this);
19586         
19587         
19588        // console.log(this);
19589     },
19590     initEvents : function(){
19591         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19592         
19593             
19594         var a = this.ranchor;
19595
19596         var el = Roo.get(a);
19597
19598         if(Roo.isOpera){ // opera render bug ignores the CSS
19599             el.setStyle("text-decoration", "none");
19600         }
19601
19602         el.on("click", this.onClick, this);
19603         el.on("dblclick", this.onDblClick, this);
19604         el.on("contextmenu", this.onContextMenu, this);
19605         
19606     },
19607     
19608     /*onSelectedChange : function(state){
19609         if(state){
19610             this.focus();
19611             this.addClass("x-tree-selected");
19612         }else{
19613             //this.blur();
19614             this.removeClass("x-tree-selected");
19615         }
19616     },*/
19617     addClass : function(cls){
19618         if(this.elRow){
19619             Roo.fly(this.elRow).addClass(cls);
19620         }
19621         
19622     },
19623     
19624     
19625     removeClass : function(cls){
19626         if(this.elRow){
19627             Roo.fly(this.elRow).removeClass(cls);
19628         }
19629     }
19630
19631     
19632     
19633 });//<Script type="text/javascript">
19634
19635 /*
19636  * Based on:
19637  * Ext JS Library 1.1.1
19638  * Copyright(c) 2006-2007, Ext JS, LLC.
19639  *
19640  * Originally Released Under LGPL - original licence link has changed is not relivant.
19641  *
19642  * Fork - LGPL
19643  * <script type="text/javascript">
19644  */
19645  
19646
19647 /**
19648  * @class Roo.tree.ColumnTree
19649  * @extends Roo.data.TreePanel
19650  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19651  * @cfg {int} borderWidth  compined right/left border allowance
19652  * @constructor
19653  * @param {String/HTMLElement/Element} el The container element
19654  * @param {Object} config
19655  */
19656 Roo.tree.ColumnTree =  function(el, config)
19657 {
19658    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19659    this.addEvents({
19660         /**
19661         * @event resize
19662         * Fire this event on a container when it resizes
19663         * @param {int} w Width
19664         * @param {int} h Height
19665         */
19666        "resize" : true
19667     });
19668     this.on('resize', this.onResize, this);
19669 };
19670
19671 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19672     //lines:false,
19673     
19674     
19675     borderWidth: Roo.isBorderBox ? 0 : 2, 
19676     headEls : false,
19677     
19678     render : function(){
19679         // add the header.....
19680        
19681         Roo.tree.ColumnTree.superclass.render.apply(this);
19682         
19683         this.el.addClass('x-column-tree');
19684         
19685         this.headers = this.el.createChild(
19686             {cls:'x-tree-headers'},this.innerCt.dom);
19687    
19688         var cols = this.columns, c;
19689         var totalWidth = 0;
19690         this.headEls = [];
19691         var  len = cols.length;
19692         for(var i = 0; i < len; i++){
19693              c = cols[i];
19694              totalWidth += c.width;
19695             this.headEls.push(this.headers.createChild({
19696                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19697                  cn: {
19698                      cls:'x-tree-hd-text',
19699                      html: c.header
19700                  },
19701                  style:'width:'+(c.width-this.borderWidth)+'px;'
19702              }));
19703         }
19704         this.headers.createChild({cls:'x-clear'});
19705         // prevent floats from wrapping when clipped
19706         this.headers.setWidth(totalWidth);
19707         //this.innerCt.setWidth(totalWidth);
19708         this.innerCt.setStyle({ overflow: 'auto' });
19709         this.onResize(this.width, this.height);
19710              
19711         
19712     },
19713     onResize : function(w,h)
19714     {
19715         this.height = h;
19716         this.width = w;
19717         // resize cols..
19718         this.innerCt.setWidth(this.width);
19719         this.innerCt.setHeight(this.height-20);
19720         
19721         // headers...
19722         var cols = this.columns, c;
19723         var totalWidth = 0;
19724         var expEl = false;
19725         var len = cols.length;
19726         for(var i = 0; i < len; i++){
19727             c = cols[i];
19728             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19729                 // it's the expander..
19730                 expEl  = this.headEls[i];
19731                 continue;
19732             }
19733             totalWidth += c.width;
19734             
19735         }
19736         if (expEl) {
19737             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19738         }
19739         this.headers.setWidth(w-20);
19740
19741         
19742         
19743         
19744     }
19745 });
19746 /*
19747  * Based on:
19748  * Ext JS Library 1.1.1
19749  * Copyright(c) 2006-2007, Ext JS, LLC.
19750  *
19751  * Originally Released Under LGPL - original licence link has changed is not relivant.
19752  *
19753  * Fork - LGPL
19754  * <script type="text/javascript">
19755  */
19756  
19757 /**
19758  * @class Roo.menu.Menu
19759  * @extends Roo.util.Observable
19760  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19761  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19762  * @constructor
19763  * Creates a new Menu
19764  * @param {Object} config Configuration options
19765  */
19766 Roo.menu.Menu = function(config){
19767     Roo.apply(this, config);
19768     this.id = this.id || Roo.id();
19769     this.addEvents({
19770         /**
19771          * @event beforeshow
19772          * Fires before this menu is displayed
19773          * @param {Roo.menu.Menu} this
19774          */
19775         beforeshow : true,
19776         /**
19777          * @event beforehide
19778          * Fires before this menu is hidden
19779          * @param {Roo.menu.Menu} this
19780          */
19781         beforehide : true,
19782         /**
19783          * @event show
19784          * Fires after this menu is displayed
19785          * @param {Roo.menu.Menu} this
19786          */
19787         show : true,
19788         /**
19789          * @event hide
19790          * Fires after this menu is hidden
19791          * @param {Roo.menu.Menu} this
19792          */
19793         hide : true,
19794         /**
19795          * @event click
19796          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19797          * @param {Roo.menu.Menu} this
19798          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19799          * @param {Roo.EventObject} e
19800          */
19801         click : true,
19802         /**
19803          * @event mouseover
19804          * Fires when the mouse is hovering over this menu
19805          * @param {Roo.menu.Menu} this
19806          * @param {Roo.EventObject} e
19807          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19808          */
19809         mouseover : true,
19810         /**
19811          * @event mouseout
19812          * Fires when the mouse exits this menu
19813          * @param {Roo.menu.Menu} this
19814          * @param {Roo.EventObject} e
19815          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19816          */
19817         mouseout : true,
19818         /**
19819          * @event itemclick
19820          * Fires when a menu item contained in this menu is clicked
19821          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19822          * @param {Roo.EventObject} e
19823          */
19824         itemclick: true
19825     });
19826     if (this.registerMenu) {
19827         Roo.menu.MenuMgr.register(this);
19828     }
19829     
19830     var mis = this.items;
19831     this.items = new Roo.util.MixedCollection();
19832     if(mis){
19833         this.add.apply(this, mis);
19834     }
19835 };
19836
19837 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19838     /**
19839      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19840      */
19841     minWidth : 120,
19842     /**
19843      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19844      * for bottom-right shadow (defaults to "sides")
19845      */
19846     shadow : "sides",
19847     /**
19848      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19849      * this menu (defaults to "tl-tr?")
19850      */
19851     subMenuAlign : "tl-tr?",
19852     /**
19853      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19854      * relative to its element of origin (defaults to "tl-bl?")
19855      */
19856     defaultAlign : "tl-bl?",
19857     /**
19858      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19859      */
19860     allowOtherMenus : false,
19861     /**
19862      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19863      */
19864     registerMenu : true,
19865
19866     hidden:true,
19867
19868     // private
19869     render : function(){
19870         if(this.el){
19871             return;
19872         }
19873         var el = this.el = new Roo.Layer({
19874             cls: "x-menu",
19875             shadow:this.shadow,
19876             constrain: false,
19877             parentEl: this.parentEl || document.body,
19878             zindex:15000
19879         });
19880
19881         this.keyNav = new Roo.menu.MenuNav(this);
19882
19883         if(this.plain){
19884             el.addClass("x-menu-plain");
19885         }
19886         if(this.cls){
19887             el.addClass(this.cls);
19888         }
19889         // generic focus element
19890         this.focusEl = el.createChild({
19891             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19892         });
19893         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19894         ul.on("click", this.onClick, this);
19895         ul.on("mouseover", this.onMouseOver, this);
19896         ul.on("mouseout", this.onMouseOut, this);
19897         this.items.each(function(item){
19898             if (item.hidden) {
19899                 return;
19900             }
19901             
19902             var li = document.createElement("li");
19903             li.className = "x-menu-list-item";
19904             ul.dom.appendChild(li);
19905             item.render(li, this);
19906         }, this);
19907         this.ul = ul;
19908         this.autoWidth();
19909     },
19910
19911     // private
19912     autoWidth : function(){
19913         var el = this.el, ul = this.ul;
19914         if(!el){
19915             return;
19916         }
19917         var w = this.width;
19918         if(w){
19919             el.setWidth(w);
19920         }else if(Roo.isIE){
19921             el.setWidth(this.minWidth);
19922             var t = el.dom.offsetWidth; // force recalc
19923             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19924         }
19925     },
19926
19927     // private
19928     delayAutoWidth : function(){
19929         if(this.rendered){
19930             if(!this.awTask){
19931                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19932             }
19933             this.awTask.delay(20);
19934         }
19935     },
19936
19937     // private
19938     findTargetItem : function(e){
19939         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19940         if(t && t.menuItemId){
19941             return this.items.get(t.menuItemId);
19942         }
19943     },
19944
19945     // private
19946     onClick : function(e){
19947         var t;
19948         if(t = this.findTargetItem(e)){
19949             t.onClick(e);
19950             this.fireEvent("click", this, t, e);
19951         }
19952     },
19953
19954     // private
19955     setActiveItem : function(item, autoExpand){
19956         if(item != this.activeItem){
19957             if(this.activeItem){
19958                 this.activeItem.deactivate();
19959             }
19960             this.activeItem = item;
19961             item.activate(autoExpand);
19962         }else if(autoExpand){
19963             item.expandMenu();
19964         }
19965     },
19966
19967     // private
19968     tryActivate : function(start, step){
19969         var items = this.items;
19970         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19971             var item = items.get(i);
19972             if(!item.disabled && item.canActivate){
19973                 this.setActiveItem(item, false);
19974                 return item;
19975             }
19976         }
19977         return false;
19978     },
19979
19980     // private
19981     onMouseOver : function(e){
19982         var t;
19983         if(t = this.findTargetItem(e)){
19984             if(t.canActivate && !t.disabled){
19985                 this.setActiveItem(t, true);
19986             }
19987         }
19988         this.fireEvent("mouseover", this, e, t);
19989     },
19990
19991     // private
19992     onMouseOut : function(e){
19993         var t;
19994         if(t = this.findTargetItem(e)){
19995             if(t == this.activeItem && t.shouldDeactivate(e)){
19996                 this.activeItem.deactivate();
19997                 delete this.activeItem;
19998             }
19999         }
20000         this.fireEvent("mouseout", this, e, t);
20001     },
20002
20003     /**
20004      * Read-only.  Returns true if the menu is currently displayed, else false.
20005      * @type Boolean
20006      */
20007     isVisible : function(){
20008         return this.el && !this.hidden;
20009     },
20010
20011     /**
20012      * Displays this menu relative to another element
20013      * @param {String/HTMLElement/Roo.Element} element The element to align to
20014      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
20015      * the element (defaults to this.defaultAlign)
20016      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20017      */
20018     show : function(el, pos, parentMenu){
20019         this.parentMenu = parentMenu;
20020         if(!this.el){
20021             this.render();
20022         }
20023         this.fireEvent("beforeshow", this);
20024         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
20025     },
20026
20027     /**
20028      * Displays this menu at a specific xy position
20029      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
20030      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
20031      */
20032     showAt : function(xy, parentMenu, /* private: */_e){
20033         this.parentMenu = parentMenu;
20034         if(!this.el){
20035             this.render();
20036         }
20037         if(_e !== false){
20038             this.fireEvent("beforeshow", this);
20039             xy = this.el.adjustForConstraints(xy);
20040         }
20041         this.el.setXY(xy);
20042         this.el.show();
20043         this.hidden = false;
20044         this.focus();
20045         this.fireEvent("show", this);
20046     },
20047
20048     focus : function(){
20049         if(!this.hidden){
20050             this.doFocus.defer(50, this);
20051         }
20052     },
20053
20054     doFocus : function(){
20055         if(!this.hidden){
20056             this.focusEl.focus();
20057         }
20058     },
20059
20060     /**
20061      * Hides this menu and optionally all parent menus
20062      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
20063      */
20064     hide : function(deep){
20065         if(this.el && this.isVisible()){
20066             this.fireEvent("beforehide", this);
20067             if(this.activeItem){
20068                 this.activeItem.deactivate();
20069                 this.activeItem = null;
20070             }
20071             this.el.hide();
20072             this.hidden = true;
20073             this.fireEvent("hide", this);
20074         }
20075         if(deep === true && this.parentMenu){
20076             this.parentMenu.hide(true);
20077         }
20078     },
20079
20080     /**
20081      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
20082      * Any of the following are valid:
20083      * <ul>
20084      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
20085      * <li>An HTMLElement object which will be converted to a menu item</li>
20086      * <li>A menu item config object that will be created as a new menu item</li>
20087      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
20088      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
20089      * </ul>
20090      * Usage:
20091      * <pre><code>
20092 // Create the menu
20093 var menu = new Roo.menu.Menu();
20094
20095 // Create a menu item to add by reference
20096 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20097
20098 // Add a bunch of items at once using different methods.
20099 // Only the last item added will be returned.
20100 var item = menu.add(
20101     menuItem,                // add existing item by ref
20102     'Dynamic Item',          // new TextItem
20103     '-',                     // new separator
20104     { text: 'Config Item' }  // new item by config
20105 );
20106 </code></pre>
20107      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20108      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20109      */
20110     add : function(){
20111         var a = arguments, l = a.length, item;
20112         for(var i = 0; i < l; i++){
20113             var el = a[i];
20114             if ((typeof(el) == "object") && el.xtype && el.xns) {
20115                 el = Roo.factory(el, Roo.menu);
20116             }
20117             
20118             if(el.render){ // some kind of Item
20119                 item = this.addItem(el);
20120             }else if(typeof el == "string"){ // string
20121                 if(el == "separator" || el == "-"){
20122                     item = this.addSeparator();
20123                 }else{
20124                     item = this.addText(el);
20125                 }
20126             }else if(el.tagName || el.el){ // element
20127                 item = this.addElement(el);
20128             }else if(typeof el == "object"){ // must be menu item config?
20129                 item = this.addMenuItem(el);
20130             }
20131         }
20132         return item;
20133     },
20134
20135     /**
20136      * Returns this menu's underlying {@link Roo.Element} object
20137      * @return {Roo.Element} The element
20138      */
20139     getEl : function(){
20140         if(!this.el){
20141             this.render();
20142         }
20143         return this.el;
20144     },
20145
20146     /**
20147      * Adds a separator bar to the menu
20148      * @return {Roo.menu.Item} The menu item that was added
20149      */
20150     addSeparator : function(){
20151         return this.addItem(new Roo.menu.Separator());
20152     },
20153
20154     /**
20155      * Adds an {@link Roo.Element} object to the menu
20156      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20157      * @return {Roo.menu.Item} The menu item that was added
20158      */
20159     addElement : function(el){
20160         return this.addItem(new Roo.menu.BaseItem(el));
20161     },
20162
20163     /**
20164      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20165      * @param {Roo.menu.Item} item The menu item to add
20166      * @return {Roo.menu.Item} The menu item that was added
20167      */
20168     addItem : function(item){
20169         this.items.add(item);
20170         if(this.ul){
20171             var li = document.createElement("li");
20172             li.className = "x-menu-list-item";
20173             this.ul.dom.appendChild(li);
20174             item.render(li, this);
20175             this.delayAutoWidth();
20176         }
20177         return item;
20178     },
20179
20180     /**
20181      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20182      * @param {Object} config A MenuItem config object
20183      * @return {Roo.menu.Item} The menu item that was added
20184      */
20185     addMenuItem : function(config){
20186         if(!(config instanceof Roo.menu.Item)){
20187             if(typeof config.checked == "boolean"){ // must be check menu item config?
20188                 config = new Roo.menu.CheckItem(config);
20189             }else{
20190                 config = new Roo.menu.Item(config);
20191             }
20192         }
20193         return this.addItem(config);
20194     },
20195
20196     /**
20197      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20198      * @param {String} text The text to display in the menu item
20199      * @return {Roo.menu.Item} The menu item that was added
20200      */
20201     addText : function(text){
20202         return this.addItem(new Roo.menu.TextItem({ text : text }));
20203     },
20204
20205     /**
20206      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20207      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20208      * @param {Roo.menu.Item} item The menu item to add
20209      * @return {Roo.menu.Item} The menu item that was added
20210      */
20211     insert : function(index, item){
20212         this.items.insert(index, item);
20213         if(this.ul){
20214             var li = document.createElement("li");
20215             li.className = "x-menu-list-item";
20216             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20217             item.render(li, this);
20218             this.delayAutoWidth();
20219         }
20220         return item;
20221     },
20222
20223     /**
20224      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20225      * @param {Roo.menu.Item} item The menu item to remove
20226      */
20227     remove : function(item){
20228         this.items.removeKey(item.id);
20229         item.destroy();
20230     },
20231
20232     /**
20233      * Removes and destroys all items in the menu
20234      */
20235     removeAll : function(){
20236         var f;
20237         while(f = this.items.first()){
20238             this.remove(f);
20239         }
20240     }
20241 });
20242
20243 // MenuNav is a private utility class used internally by the Menu
20244 Roo.menu.MenuNav = function(menu){
20245     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20246     this.scope = this.menu = menu;
20247 };
20248
20249 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20250     doRelay : function(e, h){
20251         var k = e.getKey();
20252         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20253             this.menu.tryActivate(0, 1);
20254             return false;
20255         }
20256         return h.call(this.scope || this, e, this.menu);
20257     },
20258
20259     up : function(e, m){
20260         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20261             m.tryActivate(m.items.length-1, -1);
20262         }
20263     },
20264
20265     down : function(e, m){
20266         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20267             m.tryActivate(0, 1);
20268         }
20269     },
20270
20271     right : function(e, m){
20272         if(m.activeItem){
20273             m.activeItem.expandMenu(true);
20274         }
20275     },
20276
20277     left : function(e, m){
20278         m.hide();
20279         if(m.parentMenu && m.parentMenu.activeItem){
20280             m.parentMenu.activeItem.activate();
20281         }
20282     },
20283
20284     enter : function(e, m){
20285         if(m.activeItem){
20286             e.stopPropagation();
20287             m.activeItem.onClick(e);
20288             m.fireEvent("click", this, m.activeItem);
20289             return true;
20290         }
20291     }
20292 });/*
20293  * Based on:
20294  * Ext JS Library 1.1.1
20295  * Copyright(c) 2006-2007, Ext JS, LLC.
20296  *
20297  * Originally Released Under LGPL - original licence link has changed is not relivant.
20298  *
20299  * Fork - LGPL
20300  * <script type="text/javascript">
20301  */
20302  
20303 /**
20304  * @class Roo.menu.MenuMgr
20305  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20306  * @singleton
20307  */
20308 Roo.menu.MenuMgr = function(){
20309    var menus, active, groups = {}, attached = false, lastShow = new Date();
20310
20311    // private - called when first menu is created
20312    function init(){
20313        menus = {};
20314        active = new Roo.util.MixedCollection();
20315        Roo.get(document).addKeyListener(27, function(){
20316            if(active.length > 0){
20317                hideAll();
20318            }
20319        });
20320    }
20321
20322    // private
20323    function hideAll(){
20324        if(active && active.length > 0){
20325            var c = active.clone();
20326            c.each(function(m){
20327                m.hide();
20328            });
20329        }
20330    }
20331
20332    // private
20333    function onHide(m){
20334        active.remove(m);
20335        if(active.length < 1){
20336            Roo.get(document).un("mousedown", onMouseDown);
20337            attached = false;
20338        }
20339    }
20340
20341    // private
20342    function onShow(m){
20343        var last = active.last();
20344        lastShow = new Date();
20345        active.add(m);
20346        if(!attached){
20347            Roo.get(document).on("mousedown", onMouseDown);
20348            attached = true;
20349        }
20350        if(m.parentMenu){
20351           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20352           m.parentMenu.activeChild = m;
20353        }else if(last && last.isVisible()){
20354           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20355        }
20356    }
20357
20358    // private
20359    function onBeforeHide(m){
20360        if(m.activeChild){
20361            m.activeChild.hide();
20362        }
20363        if(m.autoHideTimer){
20364            clearTimeout(m.autoHideTimer);
20365            delete m.autoHideTimer;
20366        }
20367    }
20368
20369    // private
20370    function onBeforeShow(m){
20371        var pm = m.parentMenu;
20372        if(!pm && !m.allowOtherMenus){
20373            hideAll();
20374        }else if(pm && pm.activeChild && active != m){
20375            pm.activeChild.hide();
20376        }
20377    }
20378
20379    // private
20380    function onMouseDown(e){
20381        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20382            hideAll();
20383        }
20384    }
20385
20386    // private
20387    function onBeforeCheck(mi, state){
20388        if(state){
20389            var g = groups[mi.group];
20390            for(var i = 0, l = g.length; i < l; i++){
20391                if(g[i] != mi){
20392                    g[i].setChecked(false);
20393                }
20394            }
20395        }
20396    }
20397
20398    return {
20399
20400        /**
20401         * Hides all menus that are currently visible
20402         */
20403        hideAll : function(){
20404             hideAll();  
20405        },
20406
20407        // private
20408        register : function(menu){
20409            if(!menus){
20410                init();
20411            }
20412            menus[menu.id] = menu;
20413            menu.on("beforehide", onBeforeHide);
20414            menu.on("hide", onHide);
20415            menu.on("beforeshow", onBeforeShow);
20416            menu.on("show", onShow);
20417            var g = menu.group;
20418            if(g && menu.events["checkchange"]){
20419                if(!groups[g]){
20420                    groups[g] = [];
20421                }
20422                groups[g].push(menu);
20423                menu.on("checkchange", onCheck);
20424            }
20425        },
20426
20427         /**
20428          * Returns a {@link Roo.menu.Menu} object
20429          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20430          * be used to generate and return a new Menu instance.
20431          */
20432        get : function(menu){
20433            if(typeof menu == "string"){ // menu id
20434                return menus[menu];
20435            }else if(menu.events){  // menu instance
20436                return menu;
20437            }else if(typeof menu.length == 'number'){ // array of menu items?
20438                return new Roo.menu.Menu({items:menu});
20439            }else{ // otherwise, must be a config
20440                return new Roo.menu.Menu(menu);
20441            }
20442        },
20443
20444        // private
20445        unregister : function(menu){
20446            delete menus[menu.id];
20447            menu.un("beforehide", onBeforeHide);
20448            menu.un("hide", onHide);
20449            menu.un("beforeshow", onBeforeShow);
20450            menu.un("show", onShow);
20451            var g = menu.group;
20452            if(g && menu.events["checkchange"]){
20453                groups[g].remove(menu);
20454                menu.un("checkchange", onCheck);
20455            }
20456        },
20457
20458        // private
20459        registerCheckable : function(menuItem){
20460            var g = menuItem.group;
20461            if(g){
20462                if(!groups[g]){
20463                    groups[g] = [];
20464                }
20465                groups[g].push(menuItem);
20466                menuItem.on("beforecheckchange", onBeforeCheck);
20467            }
20468        },
20469
20470        // private
20471        unregisterCheckable : function(menuItem){
20472            var g = menuItem.group;
20473            if(g){
20474                groups[g].remove(menuItem);
20475                menuItem.un("beforecheckchange", onBeforeCheck);
20476            }
20477        }
20478    };
20479 }();/*
20480  * Based on:
20481  * Ext JS Library 1.1.1
20482  * Copyright(c) 2006-2007, Ext JS, LLC.
20483  *
20484  * Originally Released Under LGPL - original licence link has changed is not relivant.
20485  *
20486  * Fork - LGPL
20487  * <script type="text/javascript">
20488  */
20489  
20490
20491 /**
20492  * @class Roo.menu.BaseItem
20493  * @extends Roo.Component
20494  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20495  * management and base configuration options shared by all menu components.
20496  * @constructor
20497  * Creates a new BaseItem
20498  * @param {Object} config Configuration options
20499  */
20500 Roo.menu.BaseItem = function(config){
20501     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20502
20503     this.addEvents({
20504         /**
20505          * @event click
20506          * Fires when this item is clicked
20507          * @param {Roo.menu.BaseItem} this
20508          * @param {Roo.EventObject} e
20509          */
20510         click: true,
20511         /**
20512          * @event activate
20513          * Fires when this item is activated
20514          * @param {Roo.menu.BaseItem} this
20515          */
20516         activate : true,
20517         /**
20518          * @event deactivate
20519          * Fires when this item is deactivated
20520          * @param {Roo.menu.BaseItem} this
20521          */
20522         deactivate : true
20523     });
20524
20525     if(this.handler){
20526         this.on("click", this.handler, this.scope, true);
20527     }
20528 };
20529
20530 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20531     /**
20532      * @cfg {Function} handler
20533      * A function that will handle the click event of this menu item (defaults to undefined)
20534      */
20535     /**
20536      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20537      */
20538     canActivate : false,
20539     
20540      /**
20541      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
20542      */
20543     hidden: false,
20544     
20545     /**
20546      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20547      */
20548     activeClass : "x-menu-item-active",
20549     /**
20550      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20551      */
20552     hideOnClick : true,
20553     /**
20554      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20555      */
20556     hideDelay : 100,
20557
20558     // private
20559     ctype: "Roo.menu.BaseItem",
20560
20561     // private
20562     actionMode : "container",
20563
20564     // private
20565     render : function(container, parentMenu){
20566         this.parentMenu = parentMenu;
20567         Roo.menu.BaseItem.superclass.render.call(this, container);
20568         this.container.menuItemId = this.id;
20569     },
20570
20571     // private
20572     onRender : function(container, position){
20573         this.el = Roo.get(this.el);
20574         container.dom.appendChild(this.el.dom);
20575     },
20576
20577     // private
20578     onClick : function(e){
20579         if(!this.disabled && this.fireEvent("click", this, e) !== false
20580                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20581             this.handleClick(e);
20582         }else{
20583             e.stopEvent();
20584         }
20585     },
20586
20587     // private
20588     activate : function(){
20589         if(this.disabled){
20590             return false;
20591         }
20592         var li = this.container;
20593         li.addClass(this.activeClass);
20594         this.region = li.getRegion().adjust(2, 2, -2, -2);
20595         this.fireEvent("activate", this);
20596         return true;
20597     },
20598
20599     // private
20600     deactivate : function(){
20601         this.container.removeClass(this.activeClass);
20602         this.fireEvent("deactivate", this);
20603     },
20604
20605     // private
20606     shouldDeactivate : function(e){
20607         return !this.region || !this.region.contains(e.getPoint());
20608     },
20609
20610     // private
20611     handleClick : function(e){
20612         if(this.hideOnClick){
20613             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20614         }
20615     },
20616
20617     // private
20618     expandMenu : function(autoActivate){
20619         // do nothing
20620     },
20621
20622     // private
20623     hideMenu : function(){
20624         // do nothing
20625     }
20626 });/*
20627  * Based on:
20628  * Ext JS Library 1.1.1
20629  * Copyright(c) 2006-2007, Ext JS, LLC.
20630  *
20631  * Originally Released Under LGPL - original licence link has changed is not relivant.
20632  *
20633  * Fork - LGPL
20634  * <script type="text/javascript">
20635  */
20636  
20637 /**
20638  * @class Roo.menu.Adapter
20639  * @extends Roo.menu.BaseItem
20640  * 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.
20641  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20642  * @constructor
20643  * Creates a new Adapter
20644  * @param {Object} config Configuration options
20645  */
20646 Roo.menu.Adapter = function(component, config){
20647     Roo.menu.Adapter.superclass.constructor.call(this, config);
20648     this.component = component;
20649 };
20650 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20651     // private
20652     canActivate : true,
20653
20654     // private
20655     onRender : function(container, position){
20656         this.component.render(container);
20657         this.el = this.component.getEl();
20658     },
20659
20660     // private
20661     activate : function(){
20662         if(this.disabled){
20663             return false;
20664         }
20665         this.component.focus();
20666         this.fireEvent("activate", this);
20667         return true;
20668     },
20669
20670     // private
20671     deactivate : function(){
20672         this.fireEvent("deactivate", this);
20673     },
20674
20675     // private
20676     disable : function(){
20677         this.component.disable();
20678         Roo.menu.Adapter.superclass.disable.call(this);
20679     },
20680
20681     // private
20682     enable : function(){
20683         this.component.enable();
20684         Roo.menu.Adapter.superclass.enable.call(this);
20685     }
20686 });/*
20687  * Based on:
20688  * Ext JS Library 1.1.1
20689  * Copyright(c) 2006-2007, Ext JS, LLC.
20690  *
20691  * Originally Released Under LGPL - original licence link has changed is not relivant.
20692  *
20693  * Fork - LGPL
20694  * <script type="text/javascript">
20695  */
20696
20697 /**
20698  * @class Roo.menu.TextItem
20699  * @extends Roo.menu.BaseItem
20700  * Adds a static text string to a menu, usually used as either a heading or group separator.
20701  * Note: old style constructor with text is still supported.
20702  * 
20703  * @constructor
20704  * Creates a new TextItem
20705  * @param {Object} cfg Configuration
20706  */
20707 Roo.menu.TextItem = function(cfg){
20708     if (typeof(cfg) == 'string') {
20709         this.text = cfg;
20710     } else {
20711         Roo.apply(this,cfg);
20712     }
20713     
20714     Roo.menu.TextItem.superclass.constructor.call(this);
20715 };
20716
20717 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20718     /**
20719      * @cfg {Boolean} text Text to show on item.
20720      */
20721     text : '',
20722     
20723     /**
20724      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20725      */
20726     hideOnClick : false,
20727     /**
20728      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20729      */
20730     itemCls : "x-menu-text",
20731
20732     // private
20733     onRender : function(){
20734         var s = document.createElement("span");
20735         s.className = this.itemCls;
20736         s.innerHTML = this.text;
20737         this.el = s;
20738         Roo.menu.TextItem.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 /**
20752  * @class Roo.menu.Separator
20753  * @extends Roo.menu.BaseItem
20754  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20755  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20756  * @constructor
20757  * @param {Object} config Configuration options
20758  */
20759 Roo.menu.Separator = function(config){
20760     Roo.menu.Separator.superclass.constructor.call(this, config);
20761 };
20762
20763 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20764     /**
20765      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20766      */
20767     itemCls : "x-menu-sep",
20768     /**
20769      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20770      */
20771     hideOnClick : false,
20772
20773     // private
20774     onRender : function(li){
20775         var s = document.createElement("span");
20776         s.className = this.itemCls;
20777         s.innerHTML = "&#160;";
20778         this.el = s;
20779         li.addClass("x-menu-sep-li");
20780         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20781     }
20782 });/*
20783  * Based on:
20784  * Ext JS Library 1.1.1
20785  * Copyright(c) 2006-2007, Ext JS, LLC.
20786  *
20787  * Originally Released Under LGPL - original licence link has changed is not relivant.
20788  *
20789  * Fork - LGPL
20790  * <script type="text/javascript">
20791  */
20792 /**
20793  * @class Roo.menu.Item
20794  * @extends Roo.menu.BaseItem
20795  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20796  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20797  * activation and click handling.
20798  * @constructor
20799  * Creates a new Item
20800  * @param {Object} config Configuration options
20801  */
20802 Roo.menu.Item = function(config){
20803     Roo.menu.Item.superclass.constructor.call(this, config);
20804     if(this.menu){
20805         this.menu = Roo.menu.MenuMgr.get(this.menu);
20806     }
20807 };
20808 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20809     
20810     /**
20811      * @cfg {String} text
20812      * The text to show on the menu item.
20813      */
20814     text: '',
20815      /**
20816      * @cfg {String} HTML to render in menu
20817      * The text to show on the menu item (HTML version).
20818      */
20819     html: '',
20820     /**
20821      * @cfg {String} icon
20822      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20823      */
20824     icon: undefined,
20825     /**
20826      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20827      */
20828     itemCls : "x-menu-item",
20829     /**
20830      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20831      */
20832     canActivate : true,
20833     /**
20834      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20835      */
20836     showDelay: 200,
20837     // doc'd in BaseItem
20838     hideDelay: 200,
20839
20840     // private
20841     ctype: "Roo.menu.Item",
20842     
20843     // private
20844     onRender : function(container, position){
20845         var el = document.createElement("a");
20846         el.hideFocus = true;
20847         el.unselectable = "on";
20848         el.href = this.href || "#";
20849         if(this.hrefTarget){
20850             el.target = this.hrefTarget;
20851         }
20852         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20853         
20854         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20855         
20856         el.innerHTML = String.format(
20857                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20858                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20859         this.el = el;
20860         Roo.menu.Item.superclass.onRender.call(this, container, position);
20861     },
20862
20863     /**
20864      * Sets the text to display in this menu item
20865      * @param {String} text The text to display
20866      * @param {Boolean} isHTML true to indicate text is pure html.
20867      */
20868     setText : function(text, isHTML){
20869         if (isHTML) {
20870             this.html = text;
20871         } else {
20872             this.text = text;
20873             this.html = '';
20874         }
20875         if(this.rendered){
20876             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20877      
20878             this.el.update(String.format(
20879                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20880                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20881             this.parentMenu.autoWidth();
20882         }
20883     },
20884
20885     // private
20886     handleClick : function(e){
20887         if(!this.href){ // if no link defined, stop the event automatically
20888             e.stopEvent();
20889         }
20890         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20891     },
20892
20893     // private
20894     activate : function(autoExpand){
20895         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20896             this.focus();
20897             if(autoExpand){
20898                 this.expandMenu();
20899             }
20900         }
20901         return true;
20902     },
20903
20904     // private
20905     shouldDeactivate : function(e){
20906         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20907             if(this.menu && this.menu.isVisible()){
20908                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20909             }
20910             return true;
20911         }
20912         return false;
20913     },
20914
20915     // private
20916     deactivate : function(){
20917         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20918         this.hideMenu();
20919     },
20920
20921     // private
20922     expandMenu : function(autoActivate){
20923         if(!this.disabled && this.menu){
20924             clearTimeout(this.hideTimer);
20925             delete this.hideTimer;
20926             if(!this.menu.isVisible() && !this.showTimer){
20927                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20928             }else if (this.menu.isVisible() && autoActivate){
20929                 this.menu.tryActivate(0, 1);
20930             }
20931         }
20932     },
20933
20934     // private
20935     deferExpand : function(autoActivate){
20936         delete this.showTimer;
20937         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20938         if(autoActivate){
20939             this.menu.tryActivate(0, 1);
20940         }
20941     },
20942
20943     // private
20944     hideMenu : function(){
20945         clearTimeout(this.showTimer);
20946         delete this.showTimer;
20947         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20948             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20949         }
20950     },
20951
20952     // private
20953     deferHide : function(){
20954         delete this.hideTimer;
20955         this.menu.hide();
20956     }
20957 });/*
20958  * Based on:
20959  * Ext JS Library 1.1.1
20960  * Copyright(c) 2006-2007, Ext JS, LLC.
20961  *
20962  * Originally Released Under LGPL - original licence link has changed is not relivant.
20963  *
20964  * Fork - LGPL
20965  * <script type="text/javascript">
20966  */
20967  
20968 /**
20969  * @class Roo.menu.CheckItem
20970  * @extends Roo.menu.Item
20971  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20972  * @constructor
20973  * Creates a new CheckItem
20974  * @param {Object} config Configuration options
20975  */
20976 Roo.menu.CheckItem = function(config){
20977     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20978     this.addEvents({
20979         /**
20980          * @event beforecheckchange
20981          * Fires before the checked value is set, providing an opportunity to cancel if needed
20982          * @param {Roo.menu.CheckItem} this
20983          * @param {Boolean} checked The new checked value that will be set
20984          */
20985         "beforecheckchange" : true,
20986         /**
20987          * @event checkchange
20988          * Fires after the checked value has been set
20989          * @param {Roo.menu.CheckItem} this
20990          * @param {Boolean} checked The checked value that was set
20991          */
20992         "checkchange" : true
20993     });
20994     if(this.checkHandler){
20995         this.on('checkchange', this.checkHandler, this.scope);
20996     }
20997 };
20998 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20999     /**
21000      * @cfg {String} group
21001      * All check items with the same group name will automatically be grouped into a single-select
21002      * radio button group (defaults to '')
21003      */
21004     /**
21005      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
21006      */
21007     itemCls : "x-menu-item x-menu-check-item",
21008     /**
21009      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
21010      */
21011     groupClass : "x-menu-group-item",
21012
21013     /**
21014      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
21015      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
21016      * initialized with checked = true will be rendered as checked.
21017      */
21018     checked: false,
21019
21020     // private
21021     ctype: "Roo.menu.CheckItem",
21022
21023     // private
21024     onRender : function(c){
21025         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
21026         if(this.group){
21027             this.el.addClass(this.groupClass);
21028         }
21029         Roo.menu.MenuMgr.registerCheckable(this);
21030         if(this.checked){
21031             this.checked = false;
21032             this.setChecked(true, true);
21033         }
21034     },
21035
21036     // private
21037     destroy : function(){
21038         if(this.rendered){
21039             Roo.menu.MenuMgr.unregisterCheckable(this);
21040         }
21041         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
21042     },
21043
21044     /**
21045      * Set the checked state of this item
21046      * @param {Boolean} checked The new checked value
21047      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
21048      */
21049     setChecked : function(state, suppressEvent){
21050         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
21051             if(this.container){
21052                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
21053             }
21054             this.checked = state;
21055             if(suppressEvent !== true){
21056                 this.fireEvent("checkchange", this, state);
21057             }
21058         }
21059     },
21060
21061     // private
21062     handleClick : function(e){
21063        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
21064            this.setChecked(!this.checked);
21065        }
21066        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
21067     }
21068 });/*
21069  * Based on:
21070  * Ext JS Library 1.1.1
21071  * Copyright(c) 2006-2007, Ext JS, LLC.
21072  *
21073  * Originally Released Under LGPL - original licence link has changed is not relivant.
21074  *
21075  * Fork - LGPL
21076  * <script type="text/javascript">
21077  */
21078  
21079 /**
21080  * @class Roo.menu.DateItem
21081  * @extends Roo.menu.Adapter
21082  * A menu item that wraps the {@link Roo.DatPicker} component.
21083  * @constructor
21084  * Creates a new DateItem
21085  * @param {Object} config Configuration options
21086  */
21087 Roo.menu.DateItem = function(config){
21088     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
21089     /** The Roo.DatePicker object @type Roo.DatePicker */
21090     this.picker = this.component;
21091     this.addEvents({select: true});
21092     
21093     this.picker.on("render", function(picker){
21094         picker.getEl().swallowEvent("click");
21095         picker.container.addClass("x-menu-date-item");
21096     });
21097
21098     this.picker.on("select", this.onSelect, this);
21099 };
21100
21101 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21102     // private
21103     onSelect : function(picker, date){
21104         this.fireEvent("select", this, date, picker);
21105         Roo.menu.DateItem.superclass.handleClick.call(this);
21106     }
21107 });/*
21108  * Based on:
21109  * Ext JS Library 1.1.1
21110  * Copyright(c) 2006-2007, Ext JS, LLC.
21111  *
21112  * Originally Released Under LGPL - original licence link has changed is not relivant.
21113  *
21114  * Fork - LGPL
21115  * <script type="text/javascript">
21116  */
21117  
21118 /**
21119  * @class Roo.menu.ColorItem
21120  * @extends Roo.menu.Adapter
21121  * A menu item that wraps the {@link Roo.ColorPalette} component.
21122  * @constructor
21123  * Creates a new ColorItem
21124  * @param {Object} config Configuration options
21125  */
21126 Roo.menu.ColorItem = function(config){
21127     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21128     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21129     this.palette = this.component;
21130     this.relayEvents(this.palette, ["select"]);
21131     if(this.selectHandler){
21132         this.on('select', this.selectHandler, this.scope);
21133     }
21134 };
21135 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21136  * Based on:
21137  * Ext JS Library 1.1.1
21138  * Copyright(c) 2006-2007, Ext JS, LLC.
21139  *
21140  * Originally Released Under LGPL - original licence link has changed is not relivant.
21141  *
21142  * Fork - LGPL
21143  * <script type="text/javascript">
21144  */
21145  
21146
21147 /**
21148  * @class Roo.menu.DateMenu
21149  * @extends Roo.menu.Menu
21150  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21151  * @constructor
21152  * Creates a new DateMenu
21153  * @param {Object} config Configuration options
21154  */
21155 Roo.menu.DateMenu = function(config){
21156     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21157     this.plain = true;
21158     var di = new Roo.menu.DateItem(config);
21159     this.add(di);
21160     /**
21161      * The {@link Roo.DatePicker} instance for this DateMenu
21162      * @type DatePicker
21163      */
21164     this.picker = di.picker;
21165     /**
21166      * @event select
21167      * @param {DatePicker} picker
21168      * @param {Date} date
21169      */
21170     this.relayEvents(di, ["select"]);
21171     this.on('beforeshow', function(){
21172         if(this.picker){
21173             this.picker.hideMonthPicker(false);
21174         }
21175     }, this);
21176 };
21177 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21178     cls:'x-date-menu'
21179 });/*
21180  * Based on:
21181  * Ext JS Library 1.1.1
21182  * Copyright(c) 2006-2007, Ext JS, LLC.
21183  *
21184  * Originally Released Under LGPL - original licence link has changed is not relivant.
21185  *
21186  * Fork - LGPL
21187  * <script type="text/javascript">
21188  */
21189  
21190
21191 /**
21192  * @class Roo.menu.ColorMenu
21193  * @extends Roo.menu.Menu
21194  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21195  * @constructor
21196  * Creates a new ColorMenu
21197  * @param {Object} config Configuration options
21198  */
21199 Roo.menu.ColorMenu = function(config){
21200     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21201     this.plain = true;
21202     var ci = new Roo.menu.ColorItem(config);
21203     this.add(ci);
21204     /**
21205      * The {@link Roo.ColorPalette} instance for this ColorMenu
21206      * @type ColorPalette
21207      */
21208     this.palette = ci.palette;
21209     /**
21210      * @event select
21211      * @param {ColorPalette} palette
21212      * @param {String} color
21213      */
21214     this.relayEvents(ci, ["select"]);
21215 };
21216 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21217  * Based on:
21218  * Ext JS Library 1.1.1
21219  * Copyright(c) 2006-2007, Ext JS, LLC.
21220  *
21221  * Originally Released Under LGPL - original licence link has changed is not relivant.
21222  *
21223  * Fork - LGPL
21224  * <script type="text/javascript">
21225  */
21226  
21227 /**
21228  * @class Roo.form.Field
21229  * @extends Roo.BoxComponent
21230  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21231  * @constructor
21232  * Creates a new Field
21233  * @param {Object} config Configuration options
21234  */
21235 Roo.form.Field = function(config){
21236     Roo.form.Field.superclass.constructor.call(this, config);
21237 };
21238
21239 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21240     /**
21241      * @cfg {String} fieldLabel Label to use when rendering a form.
21242      */
21243        /**
21244      * @cfg {String} qtip Mouse over tip
21245      */
21246      
21247     /**
21248      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21249      */
21250     invalidClass : "x-form-invalid",
21251     /**
21252      * @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")
21253      */
21254     invalidText : "The value in this field is invalid",
21255     /**
21256      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21257      */
21258     focusClass : "x-form-focus",
21259     /**
21260      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21261       automatic validation (defaults to "keyup").
21262      */
21263     validationEvent : "keyup",
21264     /**
21265      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21266      */
21267     validateOnBlur : true,
21268     /**
21269      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21270      */
21271     validationDelay : 250,
21272     /**
21273      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21274      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21275      */
21276     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21277     /**
21278      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21279      */
21280     fieldClass : "x-form-field",
21281     /**
21282      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21283      *<pre>
21284 Value         Description
21285 -----------   ----------------------------------------------------------------------
21286 qtip          Display a quick tip when the user hovers over the field
21287 title         Display a default browser title attribute popup
21288 under         Add a block div beneath the field containing the error text
21289 side          Add an error icon to the right of the field with a popup on hover
21290 [element id]  Add the error text directly to the innerHTML of the specified element
21291 </pre>
21292      */
21293     msgTarget : 'qtip',
21294     /**
21295      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21296      */
21297     msgFx : 'normal',
21298
21299     /**
21300      * @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.
21301      */
21302     readOnly : false,
21303
21304     /**
21305      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21306      */
21307     disabled : false,
21308
21309     /**
21310      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21311      */
21312     inputType : undefined,
21313     
21314     /**
21315      * @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).
21316          */
21317         tabIndex : undefined,
21318         
21319     // private
21320     isFormField : true,
21321
21322     // private
21323     hasFocus : false,
21324     /**
21325      * @property {Roo.Element} fieldEl
21326      * Element Containing the rendered Field (with label etc.)
21327      */
21328     /**
21329      * @cfg {Mixed} value A value to initialize this field with.
21330      */
21331     value : undefined,
21332
21333     /**
21334      * @cfg {String} name The field's HTML name attribute.
21335      */
21336     /**
21337      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21338      */
21339
21340         // private ??
21341         initComponent : function(){
21342         Roo.form.Field.superclass.initComponent.call(this);
21343         this.addEvents({
21344             /**
21345              * @event focus
21346              * Fires when this field receives input focus.
21347              * @param {Roo.form.Field} this
21348              */
21349             focus : true,
21350             /**
21351              * @event blur
21352              * Fires when this field loses input focus.
21353              * @param {Roo.form.Field} this
21354              */
21355             blur : true,
21356             /**
21357              * @event specialkey
21358              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21359              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21360              * @param {Roo.form.Field} this
21361              * @param {Roo.EventObject} e The event object
21362              */
21363             specialkey : true,
21364             /**
21365              * @event change
21366              * Fires just before the field blurs if the field value has changed.
21367              * @param {Roo.form.Field} this
21368              * @param {Mixed} newValue The new value
21369              * @param {Mixed} oldValue The original value
21370              */
21371             change : true,
21372             /**
21373              * @event invalid
21374              * Fires after the field has been marked as invalid.
21375              * @param {Roo.form.Field} this
21376              * @param {String} msg The validation message
21377              */
21378             invalid : true,
21379             /**
21380              * @event valid
21381              * Fires after the field has been validated with no errors.
21382              * @param {Roo.form.Field} this
21383              */
21384             valid : true,
21385              /**
21386              * @event keyup
21387              * Fires after the key up
21388              * @param {Roo.form.Field} this
21389              * @param {Roo.EventObject}  e The event Object
21390              */
21391             keyup : true
21392         });
21393     },
21394
21395     /**
21396      * Returns the name attribute of the field if available
21397      * @return {String} name The field name
21398      */
21399     getName: function(){
21400          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21401     },
21402
21403     // private
21404     onRender : function(ct, position){
21405         Roo.form.Field.superclass.onRender.call(this, ct, position);
21406         if(!this.el){
21407             var cfg = this.getAutoCreate();
21408             if(!cfg.name){
21409                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
21410             }
21411             if (!cfg.name.length) {
21412                 delete cfg.name;
21413             }
21414             if(this.inputType){
21415                 cfg.type = this.inputType;
21416             }
21417             this.el = ct.createChild(cfg, position);
21418         }
21419         var type = this.el.dom.type;
21420         if(type){
21421             if(type == 'password'){
21422                 type = 'text';
21423             }
21424             this.el.addClass('x-form-'+type);
21425         }
21426         if(this.readOnly){
21427             this.el.dom.readOnly = true;
21428         }
21429         if(this.tabIndex !== undefined){
21430             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21431         }
21432
21433         this.el.addClass([this.fieldClass, this.cls]);
21434         this.initValue();
21435     },
21436
21437     /**
21438      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21439      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21440      * @return {Roo.form.Field} this
21441      */
21442     applyTo : function(target){
21443         this.allowDomMove = false;
21444         this.el = Roo.get(target);
21445         this.render(this.el.dom.parentNode);
21446         return this;
21447     },
21448
21449     // private
21450     initValue : function(){
21451         if(this.value !== undefined){
21452             this.setValue(this.value);
21453         }else if(this.el.dom.value.length > 0){
21454             this.setValue(this.el.dom.value);
21455         }
21456     },
21457
21458     /**
21459      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21460      */
21461     isDirty : function() {
21462         if(this.disabled) {
21463             return false;
21464         }
21465         return String(this.getValue()) !== String(this.originalValue);
21466     },
21467
21468     // private
21469     afterRender : function(){
21470         Roo.form.Field.superclass.afterRender.call(this);
21471         this.initEvents();
21472     },
21473
21474     // private
21475     fireKey : function(e){
21476         //Roo.log('field ' + e.getKey());
21477         if(e.isNavKeyPress()){
21478             this.fireEvent("specialkey", this, e);
21479         }
21480     },
21481
21482     /**
21483      * Resets the current field value to the originally loaded value and clears any validation messages
21484      */
21485     reset : function(){
21486         this.setValue(this.originalValue);
21487         this.clearInvalid();
21488     },
21489
21490     // private
21491     initEvents : function(){
21492         // safari killled keypress - so keydown is now used..
21493         this.el.on("keydown" , this.fireKey,  this);
21494         this.el.on("focus", this.onFocus,  this);
21495         this.el.on("blur", this.onBlur,  this);
21496         this.el.relayEvent('keyup', this);
21497
21498         // reference to original value for reset
21499         this.originalValue = this.getValue();
21500     },
21501
21502     // private
21503     onFocus : function(){
21504         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21505             this.el.addClass(this.focusClass);
21506         }
21507         if(!this.hasFocus){
21508             this.hasFocus = true;
21509             this.startValue = this.getValue();
21510             this.fireEvent("focus", this);
21511         }
21512     },
21513
21514     beforeBlur : Roo.emptyFn,
21515
21516     // private
21517     onBlur : function(){
21518         this.beforeBlur();
21519         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21520             this.el.removeClass(this.focusClass);
21521         }
21522         this.hasFocus = false;
21523         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21524             this.validate();
21525         }
21526         var v = this.getValue();
21527         if(String(v) !== String(this.startValue)){
21528             this.fireEvent('change', this, v, this.startValue);
21529         }
21530         this.fireEvent("blur", this);
21531     },
21532
21533     /**
21534      * Returns whether or not the field value is currently valid
21535      * @param {Boolean} preventMark True to disable marking the field invalid
21536      * @return {Boolean} True if the value is valid, else false
21537      */
21538     isValid : function(preventMark){
21539         if(this.disabled){
21540             return true;
21541         }
21542         var restore = this.preventMark;
21543         this.preventMark = preventMark === true;
21544         var v = this.validateValue(this.processValue(this.getRawValue()));
21545         this.preventMark = restore;
21546         return v;
21547     },
21548
21549     /**
21550      * Validates the field value
21551      * @return {Boolean} True if the value is valid, else false
21552      */
21553     validate : function(){
21554         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21555             this.clearInvalid();
21556             return true;
21557         }
21558         return false;
21559     },
21560
21561     processValue : function(value){
21562         return value;
21563     },
21564
21565     // private
21566     // Subclasses should provide the validation implementation by overriding this
21567     validateValue : function(value){
21568         return true;
21569     },
21570
21571     /**
21572      * Mark this field as invalid
21573      * @param {String} msg The validation message
21574      */
21575     markInvalid : function(msg){
21576         if(!this.rendered || this.preventMark){ // not rendered
21577             return;
21578         }
21579         this.el.addClass(this.invalidClass);
21580         msg = msg || this.invalidText;
21581         switch(this.msgTarget){
21582             case 'qtip':
21583                 this.el.dom.qtip = msg;
21584                 this.el.dom.qclass = 'x-form-invalid-tip';
21585                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21586                     Roo.QuickTips.enable();
21587                 }
21588                 break;
21589             case 'title':
21590                 this.el.dom.title = msg;
21591                 break;
21592             case 'under':
21593                 if(!this.errorEl){
21594                     var elp = this.el.findParent('.x-form-element', 5, true);
21595                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21596                     this.errorEl.setWidth(elp.getWidth(true)-20);
21597                 }
21598                 this.errorEl.update(msg);
21599                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21600                 break;
21601             case 'side':
21602                 if(!this.errorIcon){
21603                     var elp = this.el.findParent('.x-form-element', 5, true);
21604                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21605                 }
21606                 this.alignErrorIcon();
21607                 this.errorIcon.dom.qtip = msg;
21608                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21609                 this.errorIcon.show();
21610                 this.on('resize', this.alignErrorIcon, this);
21611                 break;
21612             default:
21613                 var t = Roo.getDom(this.msgTarget);
21614                 t.innerHTML = msg;
21615                 t.style.display = this.msgDisplay;
21616                 break;
21617         }
21618         this.fireEvent('invalid', this, msg);
21619     },
21620
21621     // private
21622     alignErrorIcon : function(){
21623         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21624     },
21625
21626     /**
21627      * Clear any invalid styles/messages for this field
21628      */
21629     clearInvalid : function(){
21630         if(!this.rendered || this.preventMark){ // not rendered
21631             return;
21632         }
21633         this.el.removeClass(this.invalidClass);
21634         switch(this.msgTarget){
21635             case 'qtip':
21636                 this.el.dom.qtip = '';
21637                 break;
21638             case 'title':
21639                 this.el.dom.title = '';
21640                 break;
21641             case 'under':
21642                 if(this.errorEl){
21643                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21644                 }
21645                 break;
21646             case 'side':
21647                 if(this.errorIcon){
21648                     this.errorIcon.dom.qtip = '';
21649                     this.errorIcon.hide();
21650                     this.un('resize', this.alignErrorIcon, this);
21651                 }
21652                 break;
21653             default:
21654                 var t = Roo.getDom(this.msgTarget);
21655                 t.innerHTML = '';
21656                 t.style.display = 'none';
21657                 break;
21658         }
21659         this.fireEvent('valid', this);
21660     },
21661
21662     /**
21663      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21664      * @return {Mixed} value The field value
21665      */
21666     getRawValue : function(){
21667         var v = this.el.getValue();
21668         
21669         return v;
21670     },
21671
21672     /**
21673      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21674      * @return {Mixed} value The field value
21675      */
21676     getValue : function(){
21677         var v = this.el.getValue();
21678          
21679         return v;
21680     },
21681
21682     /**
21683      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21684      * @param {Mixed} value The value to set
21685      */
21686     setRawValue : function(v){
21687         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21688     },
21689
21690     /**
21691      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21692      * @param {Mixed} value The value to set
21693      */
21694     setValue : function(v){
21695         this.value = v;
21696         if(this.rendered){
21697             this.el.dom.value = (v === null || v === undefined ? '' : v);
21698              this.validate();
21699         }
21700     },
21701
21702     adjustSize : function(w, h){
21703         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21704         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21705         return s;
21706     },
21707
21708     adjustWidth : function(tag, w){
21709         tag = tag.toLowerCase();
21710         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21711             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21712                 if(tag == 'input'){
21713                     return w + 2;
21714                 }
21715                 if(tag == 'textarea'){
21716                     return w-2;
21717                 }
21718             }else if(Roo.isOpera){
21719                 if(tag == 'input'){
21720                     return w + 2;
21721                 }
21722                 if(tag == 'textarea'){
21723                     return w-2;
21724                 }
21725             }
21726         }
21727         return w;
21728     }
21729 });
21730
21731
21732 // anything other than normal should be considered experimental
21733 Roo.form.Field.msgFx = {
21734     normal : {
21735         show: function(msgEl, f){
21736             msgEl.setDisplayed('block');
21737         },
21738
21739         hide : function(msgEl, f){
21740             msgEl.setDisplayed(false).update('');
21741         }
21742     },
21743
21744     slide : {
21745         show: function(msgEl, f){
21746             msgEl.slideIn('t', {stopFx:true});
21747         },
21748
21749         hide : function(msgEl, f){
21750             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21751         }
21752     },
21753
21754     slideRight : {
21755         show: function(msgEl, f){
21756             msgEl.fixDisplay();
21757             msgEl.alignTo(f.el, 'tl-tr');
21758             msgEl.slideIn('l', {stopFx:true});
21759         },
21760
21761         hide : function(msgEl, f){
21762             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21763         }
21764     }
21765 };/*
21766  * Based on:
21767  * Ext JS Library 1.1.1
21768  * Copyright(c) 2006-2007, Ext JS, LLC.
21769  *
21770  * Originally Released Under LGPL - original licence link has changed is not relivant.
21771  *
21772  * Fork - LGPL
21773  * <script type="text/javascript">
21774  */
21775  
21776
21777 /**
21778  * @class Roo.form.TextField
21779  * @extends Roo.form.Field
21780  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21781  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21782  * @constructor
21783  * Creates a new TextField
21784  * @param {Object} config Configuration options
21785  */
21786 Roo.form.TextField = function(config){
21787     Roo.form.TextField.superclass.constructor.call(this, config);
21788     this.addEvents({
21789         /**
21790          * @event autosize
21791          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21792          * according to the default logic, but this event provides a hook for the developer to apply additional
21793          * logic at runtime to resize the field if needed.
21794              * @param {Roo.form.Field} this This text field
21795              * @param {Number} width The new field width
21796              */
21797         autosize : true
21798     });
21799 };
21800
21801 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21802     /**
21803      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21804      */
21805     grow : false,
21806     /**
21807      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21808      */
21809     growMin : 30,
21810     /**
21811      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21812      */
21813     growMax : 800,
21814     /**
21815      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21816      */
21817     vtype : null,
21818     /**
21819      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21820      */
21821     maskRe : null,
21822     /**
21823      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21824      */
21825     disableKeyFilter : false,
21826     /**
21827      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21828      */
21829     allowBlank : true,
21830     /**
21831      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21832      */
21833     minLength : 0,
21834     /**
21835      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21836      */
21837     maxLength : Number.MAX_VALUE,
21838     /**
21839      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21840      */
21841     minLengthText : "The minimum length for this field is {0}",
21842     /**
21843      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21844      */
21845     maxLengthText : "The maximum length for this field is {0}",
21846     /**
21847      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21848      */
21849     selectOnFocus : false,
21850     /**
21851      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21852      */
21853     blankText : "This field is required",
21854     /**
21855      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21856      * If available, this function will be called only after the basic validators all return true, and will be passed the
21857      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21858      */
21859     validator : null,
21860     /**
21861      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21862      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21863      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21864      */
21865     regex : null,
21866     /**
21867      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21868      */
21869     regexText : "",
21870     /**
21871      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
21872      */
21873     emptyText : null,
21874    
21875
21876     // private
21877     initEvents : function()
21878     {
21879         if (this.emptyText) {
21880             this.el.attr('placeholder', this.emptyText);
21881         }
21882         
21883         Roo.form.TextField.superclass.initEvents.call(this);
21884         if(this.validationEvent == 'keyup'){
21885             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21886             this.el.on('keyup', this.filterValidation, this);
21887         }
21888         else if(this.validationEvent !== false){
21889             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21890         }
21891         
21892         if(this.selectOnFocus){
21893             this.on("focus", this.preFocus, this);
21894             
21895         }
21896         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21897             this.el.on("keypress", this.filterKeys, this);
21898         }
21899         if(this.grow){
21900             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21901             this.el.on("click", this.autoSize,  this);
21902         }
21903         if(this.el.is('input[type=password]') && Roo.isSafari){
21904             this.el.on('keydown', this.SafariOnKeyDown, this);
21905         }
21906     },
21907
21908     processValue : function(value){
21909         if(this.stripCharsRe){
21910             var newValue = value.replace(this.stripCharsRe, '');
21911             if(newValue !== value){
21912                 this.setRawValue(newValue);
21913                 return newValue;
21914             }
21915         }
21916         return value;
21917     },
21918
21919     filterValidation : function(e){
21920         if(!e.isNavKeyPress()){
21921             this.validationTask.delay(this.validationDelay);
21922         }
21923     },
21924
21925     // private
21926     onKeyUp : function(e){
21927         if(!e.isNavKeyPress()){
21928             this.autoSize();
21929         }
21930     },
21931
21932     /**
21933      * Resets the current field value to the originally-loaded value and clears any validation messages.
21934      *  
21935      */
21936     reset : function(){
21937         Roo.form.TextField.superclass.reset.call(this);
21938        
21939     },
21940
21941     
21942     // private
21943     preFocus : function(){
21944         
21945         if(this.selectOnFocus){
21946             this.el.dom.select();
21947         }
21948     },
21949
21950     
21951     // private
21952     filterKeys : function(e){
21953         var k = e.getKey();
21954         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21955             return;
21956         }
21957         var c = e.getCharCode(), cc = String.fromCharCode(c);
21958         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21959             return;
21960         }
21961         if(!this.maskRe.test(cc)){
21962             e.stopEvent();
21963         }
21964     },
21965
21966     setValue : function(v){
21967         
21968         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21969         
21970         this.autoSize();
21971     },
21972
21973     /**
21974      * Validates a value according to the field's validation rules and marks the field as invalid
21975      * if the validation fails
21976      * @param {Mixed} value The value to validate
21977      * @return {Boolean} True if the value is valid, else false
21978      */
21979     validateValue : function(value){
21980         if(value.length < 1)  { // if it's blank
21981              if(this.allowBlank){
21982                 this.clearInvalid();
21983                 return true;
21984              }else{
21985                 this.markInvalid(this.blankText);
21986                 return false;
21987              }
21988         }
21989         if(value.length < this.minLength){
21990             this.markInvalid(String.format(this.minLengthText, this.minLength));
21991             return false;
21992         }
21993         if(value.length > this.maxLength){
21994             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21995             return false;
21996         }
21997         if(this.vtype){
21998             var vt = Roo.form.VTypes;
21999             if(!vt[this.vtype](value, this)){
22000                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
22001                 return false;
22002             }
22003         }
22004         if(typeof this.validator == "function"){
22005             var msg = this.validator(value);
22006             if(msg !== true){
22007                 this.markInvalid(msg);
22008                 return false;
22009             }
22010         }
22011         if(this.regex && !this.regex.test(value)){
22012             this.markInvalid(this.regexText);
22013             return false;
22014         }
22015         return true;
22016     },
22017
22018     /**
22019      * Selects text in this field
22020      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
22021      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
22022      */
22023     selectText : function(start, end){
22024         var v = this.getRawValue();
22025         if(v.length > 0){
22026             start = start === undefined ? 0 : start;
22027             end = end === undefined ? v.length : end;
22028             var d = this.el.dom;
22029             if(d.setSelectionRange){
22030                 d.setSelectionRange(start, end);
22031             }else if(d.createTextRange){
22032                 var range = d.createTextRange();
22033                 range.moveStart("character", start);
22034                 range.moveEnd("character", v.length-end);
22035                 range.select();
22036             }
22037         }
22038     },
22039
22040     /**
22041      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
22042      * This only takes effect if grow = true, and fires the autosize event.
22043      */
22044     autoSize : function(){
22045         if(!this.grow || !this.rendered){
22046             return;
22047         }
22048         if(!this.metrics){
22049             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
22050         }
22051         var el = this.el;
22052         var v = el.dom.value;
22053         var d = document.createElement('div');
22054         d.appendChild(document.createTextNode(v));
22055         v = d.innerHTML;
22056         d = null;
22057         v += "&#160;";
22058         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
22059         this.el.setWidth(w);
22060         this.fireEvent("autosize", this, w);
22061     },
22062     
22063     // private
22064     SafariOnKeyDown : function(event)
22065     {
22066         // this is a workaround for a password hang bug on chrome/ webkit.
22067         
22068         var isSelectAll = false;
22069         
22070         if(this.el.dom.selectionEnd > 0){
22071             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
22072         }
22073         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
22074             event.preventDefault();
22075             this.setValue('');
22076             return;
22077         }
22078         
22079         if(isSelectAll){ // backspace and delete key
22080             
22081             event.preventDefault();
22082             // this is very hacky as keydown always get's upper case.
22083             //
22084             var cc = String.fromCharCode(event.getCharCode());
22085             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
22086             
22087         }
22088         
22089         
22090     }
22091 });/*
22092  * Based on:
22093  * Ext JS Library 1.1.1
22094  * Copyright(c) 2006-2007, Ext JS, LLC.
22095  *
22096  * Originally Released Under LGPL - original licence link has changed is not relivant.
22097  *
22098  * Fork - LGPL
22099  * <script type="text/javascript">
22100  */
22101  
22102 /**
22103  * @class Roo.form.Hidden
22104  * @extends Roo.form.TextField
22105  * Simple Hidden element used on forms 
22106  * 
22107  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
22108  * 
22109  * @constructor
22110  * Creates a new Hidden form element.
22111  * @param {Object} config Configuration options
22112  */
22113
22114
22115
22116 // easy hidden field...
22117 Roo.form.Hidden = function(config){
22118     Roo.form.Hidden.superclass.constructor.call(this, config);
22119 };
22120   
22121 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22122     fieldLabel:      '',
22123     inputType:      'hidden',
22124     width:          50,
22125     allowBlank:     true,
22126     labelSeparator: '',
22127     hidden:         true,
22128     itemCls :       'x-form-item-display-none'
22129
22130
22131 });
22132
22133
22134 /*
22135  * Based on:
22136  * Ext JS Library 1.1.1
22137  * Copyright(c) 2006-2007, Ext JS, LLC.
22138  *
22139  * Originally Released Under LGPL - original licence link has changed is not relivant.
22140  *
22141  * Fork - LGPL
22142  * <script type="text/javascript">
22143  */
22144  
22145 /**
22146  * @class Roo.form.TriggerField
22147  * @extends Roo.form.TextField
22148  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22149  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22150  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22151  * for which you can provide a custom implementation.  For example:
22152  * <pre><code>
22153 var trigger = new Roo.form.TriggerField();
22154 trigger.onTriggerClick = myTriggerFn;
22155 trigger.applyTo('my-field');
22156 </code></pre>
22157  *
22158  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22159  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22160  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22161  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22162  * @constructor
22163  * Create a new TriggerField.
22164  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22165  * to the base TextField)
22166  */
22167 Roo.form.TriggerField = function(config){
22168     this.mimicing = false;
22169     Roo.form.TriggerField.superclass.constructor.call(this, config);
22170 };
22171
22172 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22173     /**
22174      * @cfg {String} triggerClass A CSS class to apply to the trigger
22175      */
22176     /**
22177      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22178      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22179      */
22180     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22181     /**
22182      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22183      */
22184     hideTrigger:false,
22185
22186     /** @cfg {Boolean} grow @hide */
22187     /** @cfg {Number} growMin @hide */
22188     /** @cfg {Number} growMax @hide */
22189
22190     /**
22191      * @hide 
22192      * @method
22193      */
22194     autoSize: Roo.emptyFn,
22195     // private
22196     monitorTab : true,
22197     // private
22198     deferHeight : true,
22199
22200     
22201     actionMode : 'wrap',
22202     // private
22203     onResize : function(w, h){
22204         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22205         if(typeof w == 'number'){
22206             var x = w - this.trigger.getWidth();
22207             this.el.setWidth(this.adjustWidth('input', x));
22208             this.trigger.setStyle('left', x+'px');
22209         }
22210     },
22211
22212     // private
22213     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22214
22215     // private
22216     getResizeEl : function(){
22217         return this.wrap;
22218     },
22219
22220     // private
22221     getPositionEl : function(){
22222         return this.wrap;
22223     },
22224
22225     // private
22226     alignErrorIcon : function(){
22227         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22228     },
22229
22230     // private
22231     onRender : function(ct, position){
22232         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22233         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22234         this.trigger = this.wrap.createChild(this.triggerConfig ||
22235                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22236         if(this.hideTrigger){
22237             this.trigger.setDisplayed(false);
22238         }
22239         this.initTrigger();
22240         if(!this.width){
22241             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22242         }
22243     },
22244
22245     // private
22246     initTrigger : function(){
22247         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22248         this.trigger.addClassOnOver('x-form-trigger-over');
22249         this.trigger.addClassOnClick('x-form-trigger-click');
22250     },
22251
22252     // private
22253     onDestroy : function(){
22254         if(this.trigger){
22255             this.trigger.removeAllListeners();
22256             this.trigger.remove();
22257         }
22258         if(this.wrap){
22259             this.wrap.remove();
22260         }
22261         Roo.form.TriggerField.superclass.onDestroy.call(this);
22262     },
22263
22264     // private
22265     onFocus : function(){
22266         Roo.form.TriggerField.superclass.onFocus.call(this);
22267         if(!this.mimicing){
22268             this.wrap.addClass('x-trigger-wrap-focus');
22269             this.mimicing = true;
22270             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22271             if(this.monitorTab){
22272                 this.el.on("keydown", this.checkTab, this);
22273             }
22274         }
22275     },
22276
22277     // private
22278     checkTab : function(e){
22279         if(e.getKey() == e.TAB){
22280             this.triggerBlur();
22281         }
22282     },
22283
22284     // private
22285     onBlur : function(){
22286         // do nothing
22287     },
22288
22289     // private
22290     mimicBlur : function(e, t){
22291         if(!this.wrap.contains(t) && this.validateBlur()){
22292             this.triggerBlur();
22293         }
22294     },
22295
22296     // private
22297     triggerBlur : function(){
22298         this.mimicing = false;
22299         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22300         if(this.monitorTab){
22301             this.el.un("keydown", this.checkTab, this);
22302         }
22303         this.wrap.removeClass('x-trigger-wrap-focus');
22304         Roo.form.TriggerField.superclass.onBlur.call(this);
22305     },
22306
22307     // private
22308     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22309     validateBlur : function(e, t){
22310         return true;
22311     },
22312
22313     // private
22314     onDisable : function(){
22315         Roo.form.TriggerField.superclass.onDisable.call(this);
22316         if(this.wrap){
22317             this.wrap.addClass('x-item-disabled');
22318         }
22319     },
22320
22321     // private
22322     onEnable : function(){
22323         Roo.form.TriggerField.superclass.onEnable.call(this);
22324         if(this.wrap){
22325             this.wrap.removeClass('x-item-disabled');
22326         }
22327     },
22328
22329     // private
22330     onShow : function(){
22331         var ae = this.getActionEl();
22332         
22333         if(ae){
22334             ae.dom.style.display = '';
22335             ae.dom.style.visibility = 'visible';
22336         }
22337     },
22338
22339     // private
22340     
22341     onHide : function(){
22342         var ae = this.getActionEl();
22343         ae.dom.style.display = 'none';
22344     },
22345
22346     /**
22347      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22348      * by an implementing function.
22349      * @method
22350      * @param {EventObject} e
22351      */
22352     onTriggerClick : Roo.emptyFn
22353 });
22354
22355 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22356 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22357 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22358 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22359     initComponent : function(){
22360         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22361
22362         this.triggerConfig = {
22363             tag:'span', cls:'x-form-twin-triggers', cn:[
22364             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22365             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22366         ]};
22367     },
22368
22369     getTrigger : function(index){
22370         return this.triggers[index];
22371     },
22372
22373     initTrigger : function(){
22374         var ts = this.trigger.select('.x-form-trigger', true);
22375         this.wrap.setStyle('overflow', 'hidden');
22376         var triggerField = this;
22377         ts.each(function(t, all, index){
22378             t.hide = function(){
22379                 var w = triggerField.wrap.getWidth();
22380                 this.dom.style.display = 'none';
22381                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22382             };
22383             t.show = function(){
22384                 var w = triggerField.wrap.getWidth();
22385                 this.dom.style.display = '';
22386                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22387             };
22388             var triggerIndex = 'Trigger'+(index+1);
22389
22390             if(this['hide'+triggerIndex]){
22391                 t.dom.style.display = 'none';
22392             }
22393             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22394             t.addClassOnOver('x-form-trigger-over');
22395             t.addClassOnClick('x-form-trigger-click');
22396         }, this);
22397         this.triggers = ts.elements;
22398     },
22399
22400     onTrigger1Click : Roo.emptyFn,
22401     onTrigger2Click : Roo.emptyFn
22402 });/*
22403  * Based on:
22404  * Ext JS Library 1.1.1
22405  * Copyright(c) 2006-2007, Ext JS, LLC.
22406  *
22407  * Originally Released Under LGPL - original licence link has changed is not relivant.
22408  *
22409  * Fork - LGPL
22410  * <script type="text/javascript">
22411  */
22412  
22413 /**
22414  * @class Roo.form.TextArea
22415  * @extends Roo.form.TextField
22416  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22417  * support for auto-sizing.
22418  * @constructor
22419  * Creates a new TextArea
22420  * @param {Object} config Configuration options
22421  */
22422 Roo.form.TextArea = function(config){
22423     Roo.form.TextArea.superclass.constructor.call(this, config);
22424     // these are provided exchanges for backwards compat
22425     // minHeight/maxHeight were replaced by growMin/growMax to be
22426     // compatible with TextField growing config values
22427     if(this.minHeight !== undefined){
22428         this.growMin = this.minHeight;
22429     }
22430     if(this.maxHeight !== undefined){
22431         this.growMax = this.maxHeight;
22432     }
22433 };
22434
22435 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22436     /**
22437      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22438      */
22439     growMin : 60,
22440     /**
22441      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22442      */
22443     growMax: 1000,
22444     /**
22445      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22446      * in the field (equivalent to setting overflow: hidden, defaults to false)
22447      */
22448     preventScrollbars: false,
22449     /**
22450      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22451      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22452      */
22453
22454     // private
22455     onRender : function(ct, position){
22456         if(!this.el){
22457             this.defaultAutoCreate = {
22458                 tag: "textarea",
22459                 style:"width:300px;height:60px;",
22460                 autocomplete: "off"
22461             };
22462         }
22463         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22464         if(this.grow){
22465             this.textSizeEl = Roo.DomHelper.append(document.body, {
22466                 tag: "pre", cls: "x-form-grow-sizer"
22467             });
22468             if(this.preventScrollbars){
22469                 this.el.setStyle("overflow", "hidden");
22470             }
22471             this.el.setHeight(this.growMin);
22472         }
22473     },
22474
22475     onDestroy : function(){
22476         if(this.textSizeEl){
22477             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22478         }
22479         Roo.form.TextArea.superclass.onDestroy.call(this);
22480     },
22481
22482     // private
22483     onKeyUp : function(e){
22484         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22485             this.autoSize();
22486         }
22487     },
22488
22489     /**
22490      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22491      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22492      */
22493     autoSize : function(){
22494         if(!this.grow || !this.textSizeEl){
22495             return;
22496         }
22497         var el = this.el;
22498         var v = el.dom.value;
22499         var ts = this.textSizeEl;
22500
22501         ts.innerHTML = '';
22502         ts.appendChild(document.createTextNode(v));
22503         v = ts.innerHTML;
22504
22505         Roo.fly(ts).setWidth(this.el.getWidth());
22506         if(v.length < 1){
22507             v = "&#160;&#160;";
22508         }else{
22509             if(Roo.isIE){
22510                 v = v.replace(/\n/g, '<p>&#160;</p>');
22511             }
22512             v += "&#160;\n&#160;";
22513         }
22514         ts.innerHTML = v;
22515         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22516         if(h != this.lastHeight){
22517             this.lastHeight = h;
22518             this.el.setHeight(h);
22519             this.fireEvent("autosize", this, h);
22520         }
22521     }
22522 });/*
22523  * Based on:
22524  * Ext JS Library 1.1.1
22525  * Copyright(c) 2006-2007, Ext JS, LLC.
22526  *
22527  * Originally Released Under LGPL - original licence link has changed is not relivant.
22528  *
22529  * Fork - LGPL
22530  * <script type="text/javascript">
22531  */
22532  
22533
22534 /**
22535  * @class Roo.form.NumberField
22536  * @extends Roo.form.TextField
22537  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22538  * @constructor
22539  * Creates a new NumberField
22540  * @param {Object} config Configuration options
22541  */
22542 Roo.form.NumberField = function(config){
22543     Roo.form.NumberField.superclass.constructor.call(this, config);
22544 };
22545
22546 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22547     /**
22548      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22549      */
22550     fieldClass: "x-form-field x-form-num-field",
22551     /**
22552      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22553      */
22554     allowDecimals : true,
22555     /**
22556      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22557      */
22558     decimalSeparator : ".",
22559     /**
22560      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22561      */
22562     decimalPrecision : 2,
22563     /**
22564      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22565      */
22566     allowNegative : true,
22567     /**
22568      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22569      */
22570     minValue : Number.NEGATIVE_INFINITY,
22571     /**
22572      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22573      */
22574     maxValue : Number.MAX_VALUE,
22575     /**
22576      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22577      */
22578     minText : "The minimum value for this field is {0}",
22579     /**
22580      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22581      */
22582     maxText : "The maximum value for this field is {0}",
22583     /**
22584      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22585      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22586      */
22587     nanText : "{0} is not a valid number",
22588
22589     // private
22590     initEvents : function(){
22591         Roo.form.NumberField.superclass.initEvents.call(this);
22592         var allowed = "0123456789";
22593         if(this.allowDecimals){
22594             allowed += this.decimalSeparator;
22595         }
22596         if(this.allowNegative){
22597             allowed += "-";
22598         }
22599         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22600         var keyPress = function(e){
22601             var k = e.getKey();
22602             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22603                 return;
22604             }
22605             var c = e.getCharCode();
22606             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22607                 e.stopEvent();
22608             }
22609         };
22610         this.el.on("keypress", keyPress, this);
22611     },
22612
22613     // private
22614     validateValue : function(value){
22615         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22616             return false;
22617         }
22618         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22619              return true;
22620         }
22621         var num = this.parseValue(value);
22622         if(isNaN(num)){
22623             this.markInvalid(String.format(this.nanText, value));
22624             return false;
22625         }
22626         if(num < this.minValue){
22627             this.markInvalid(String.format(this.minText, this.minValue));
22628             return false;
22629         }
22630         if(num > this.maxValue){
22631             this.markInvalid(String.format(this.maxText, this.maxValue));
22632             return false;
22633         }
22634         return true;
22635     },
22636
22637     getValue : function(){
22638         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22639     },
22640
22641     // private
22642     parseValue : function(value){
22643         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22644         return isNaN(value) ? '' : value;
22645     },
22646
22647     // private
22648     fixPrecision : function(value){
22649         var nan = isNaN(value);
22650         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22651             return nan ? '' : value;
22652         }
22653         return parseFloat(value).toFixed(this.decimalPrecision);
22654     },
22655
22656     setValue : function(v){
22657         v = this.fixPrecision(v);
22658         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22659     },
22660
22661     // private
22662     decimalPrecisionFcn : function(v){
22663         return Math.floor(v);
22664     },
22665
22666     beforeBlur : function(){
22667         var v = this.parseValue(this.getRawValue());
22668         if(v){
22669             this.setValue(v);
22670         }
22671     }
22672 });/*
22673  * Based on:
22674  * Ext JS Library 1.1.1
22675  * Copyright(c) 2006-2007, Ext JS, LLC.
22676  *
22677  * Originally Released Under LGPL - original licence link has changed is not relivant.
22678  *
22679  * Fork - LGPL
22680  * <script type="text/javascript">
22681  */
22682  
22683 /**
22684  * @class Roo.form.DateField
22685  * @extends Roo.form.TriggerField
22686  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22687 * @constructor
22688 * Create a new DateField
22689 * @param {Object} config
22690  */
22691 Roo.form.DateField = function(config){
22692     Roo.form.DateField.superclass.constructor.call(this, config);
22693     
22694       this.addEvents({
22695          
22696         /**
22697          * @event select
22698          * Fires when a date is selected
22699              * @param {Roo.form.DateField} combo This combo box
22700              * @param {Date} date The date selected
22701              */
22702         'select' : true
22703          
22704     });
22705     
22706     
22707     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22708     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22709     this.ddMatch = null;
22710     if(this.disabledDates){
22711         var dd = this.disabledDates;
22712         var re = "(?:";
22713         for(var i = 0; i < dd.length; i++){
22714             re += dd[i];
22715             if(i != dd.length-1) re += "|";
22716         }
22717         this.ddMatch = new RegExp(re + ")");
22718     }
22719 };
22720
22721 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22722     /**
22723      * @cfg {String} format
22724      * The default date format string which can be overriden for localization support.  The format must be
22725      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22726      */
22727     format : "m/d/y",
22728     /**
22729      * @cfg {String} altFormats
22730      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22731      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22732      */
22733     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22734     /**
22735      * @cfg {Array} disabledDays
22736      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22737      */
22738     disabledDays : null,
22739     /**
22740      * @cfg {String} disabledDaysText
22741      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22742      */
22743     disabledDaysText : "Disabled",
22744     /**
22745      * @cfg {Array} disabledDates
22746      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22747      * expression so they are very powerful. Some examples:
22748      * <ul>
22749      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22750      * <li>["03/08", "09/16"] would disable those days for every year</li>
22751      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22752      * <li>["03/../2006"] would disable every day in March 2006</li>
22753      * <li>["^03"] would disable every day in every March</li>
22754      * </ul>
22755      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22756      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22757      */
22758     disabledDates : null,
22759     /**
22760      * @cfg {String} disabledDatesText
22761      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22762      */
22763     disabledDatesText : "Disabled",
22764     /**
22765      * @cfg {Date/String} minValue
22766      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22767      * valid format (defaults to null).
22768      */
22769     minValue : null,
22770     /**
22771      * @cfg {Date/String} maxValue
22772      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22773      * valid format (defaults to null).
22774      */
22775     maxValue : null,
22776     /**
22777      * @cfg {String} minText
22778      * The error text to display when the date in the cell is before minValue (defaults to
22779      * 'The date in this field must be after {minValue}').
22780      */
22781     minText : "The date in this field must be equal to or after {0}",
22782     /**
22783      * @cfg {String} maxText
22784      * The error text to display when the date in the cell is after maxValue (defaults to
22785      * 'The date in this field must be before {maxValue}').
22786      */
22787     maxText : "The date in this field must be equal to or before {0}",
22788     /**
22789      * @cfg {String} invalidText
22790      * The error text to display when the date in the field is invalid (defaults to
22791      * '{value} is not a valid date - it must be in the format {format}').
22792      */
22793     invalidText : "{0} is not a valid date - it must be in the format {1}",
22794     /**
22795      * @cfg {String} triggerClass
22796      * An additional CSS class used to style the trigger button.  The trigger will always get the
22797      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22798      * which displays a calendar icon).
22799      */
22800     triggerClass : 'x-form-date-trigger',
22801     
22802
22803     /**
22804      * @cfg {Boolean} useIso
22805      * if enabled, then the date field will use a hidden field to store the 
22806      * real value as iso formated date. default (false)
22807      */ 
22808     useIso : false,
22809     /**
22810      * @cfg {String/Object} autoCreate
22811      * A DomHelper element spec, or true for a default element spec (defaults to
22812      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22813      */ 
22814     // private
22815     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22816     
22817     // private
22818     hiddenField: false,
22819     
22820     onRender : function(ct, position)
22821     {
22822         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22823         if (this.useIso) {
22824             //this.el.dom.removeAttribute('name'); 
22825             Roo.log("Changing name?");
22826             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
22827             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22828                     'before', true);
22829             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22830             // prevent input submission
22831             this.hiddenName = this.name;
22832         }
22833             
22834             
22835     },
22836     
22837     // private
22838     validateValue : function(value)
22839     {
22840         value = this.formatDate(value);
22841         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22842             Roo.log('super failed');
22843             return false;
22844         }
22845         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22846              return true;
22847         }
22848         var svalue = value;
22849         value = this.parseDate(value);
22850         if(!value){
22851             Roo.log('parse date failed' + svalue);
22852             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22853             return false;
22854         }
22855         var time = value.getTime();
22856         if(this.minValue && time < this.minValue.getTime()){
22857             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22858             return false;
22859         }
22860         if(this.maxValue && time > this.maxValue.getTime()){
22861             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22862             return false;
22863         }
22864         if(this.disabledDays){
22865             var day = value.getDay();
22866             for(var i = 0; i < this.disabledDays.length; i++) {
22867                 if(day === this.disabledDays[i]){
22868                     this.markInvalid(this.disabledDaysText);
22869                     return false;
22870                 }
22871             }
22872         }
22873         var fvalue = this.formatDate(value);
22874         if(this.ddMatch && this.ddMatch.test(fvalue)){
22875             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22876             return false;
22877         }
22878         return true;
22879     },
22880
22881     // private
22882     // Provides logic to override the default TriggerField.validateBlur which just returns true
22883     validateBlur : function(){
22884         return !this.menu || !this.menu.isVisible();
22885     },
22886     
22887     getName: function()
22888     {
22889         // returns hidden if it's set..
22890         if (!this.rendered) {return ''};
22891         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
22892         
22893     },
22894
22895     /**
22896      * Returns the current date value of the date field.
22897      * @return {Date} The date value
22898      */
22899     getValue : function(){
22900         
22901         return  this.hiddenField ?
22902                 this.hiddenField.value :
22903                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22904     },
22905
22906     /**
22907      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22908      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22909      * (the default format used is "m/d/y").
22910      * <br />Usage:
22911      * <pre><code>
22912 //All of these calls set the same date value (May 4, 2006)
22913
22914 //Pass a date object:
22915 var dt = new Date('5/4/06');
22916 dateField.setValue(dt);
22917
22918 //Pass a date string (default format):
22919 dateField.setValue('5/4/06');
22920
22921 //Pass a date string (custom format):
22922 dateField.format = 'Y-m-d';
22923 dateField.setValue('2006-5-4');
22924 </code></pre>
22925      * @param {String/Date} date The date or valid date string
22926      */
22927     setValue : function(date){
22928         if (this.hiddenField) {
22929             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22930         }
22931         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22932         // make sure the value field is always stored as a date..
22933         this.value = this.parseDate(date);
22934         
22935         
22936     },
22937
22938     // private
22939     parseDate : function(value){
22940         if(!value || value instanceof Date){
22941             return value;
22942         }
22943         var v = Date.parseDate(value, this.format);
22944          if (!v && this.useIso) {
22945             v = Date.parseDate(value, 'Y-m-d');
22946         }
22947         if(!v && this.altFormats){
22948             if(!this.altFormatsArray){
22949                 this.altFormatsArray = this.altFormats.split("|");
22950             }
22951             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22952                 v = Date.parseDate(value, this.altFormatsArray[i]);
22953             }
22954         }
22955         return v;
22956     },
22957
22958     // private
22959     formatDate : function(date, fmt){
22960         return (!date || !(date instanceof Date)) ?
22961                date : date.dateFormat(fmt || this.format);
22962     },
22963
22964     // private
22965     menuListeners : {
22966         select: function(m, d){
22967             
22968             this.setValue(d);
22969             this.fireEvent('select', this, d);
22970         },
22971         show : function(){ // retain focus styling
22972             this.onFocus();
22973         },
22974         hide : function(){
22975             this.focus.defer(10, this);
22976             var ml = this.menuListeners;
22977             this.menu.un("select", ml.select,  this);
22978             this.menu.un("show", ml.show,  this);
22979             this.menu.un("hide", ml.hide,  this);
22980         }
22981     },
22982
22983     // private
22984     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22985     onTriggerClick : function(){
22986         if(this.disabled){
22987             return;
22988         }
22989         if(this.menu == null){
22990             this.menu = new Roo.menu.DateMenu();
22991         }
22992         Roo.apply(this.menu.picker,  {
22993             showClear: this.allowBlank,
22994             minDate : this.minValue,
22995             maxDate : this.maxValue,
22996             disabledDatesRE : this.ddMatch,
22997             disabledDatesText : this.disabledDatesText,
22998             disabledDays : this.disabledDays,
22999             disabledDaysText : this.disabledDaysText,
23000             format : this.useIso ? 'Y-m-d' : this.format,
23001             minText : String.format(this.minText, this.formatDate(this.minValue)),
23002             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23003         });
23004         this.menu.on(Roo.apply({}, this.menuListeners, {
23005             scope:this
23006         }));
23007         this.menu.picker.setValue(this.getValue() || new Date());
23008         this.menu.show(this.el, "tl-bl?");
23009     },
23010
23011     beforeBlur : function(){
23012         var v = this.parseDate(this.getRawValue());
23013         if(v){
23014             this.setValue(v);
23015         }
23016     }
23017
23018     /** @cfg {Boolean} grow @hide */
23019     /** @cfg {Number} growMin @hide */
23020     /** @cfg {Number} growMax @hide */
23021     /**
23022      * @hide
23023      * @method autoSize
23024      */
23025 });/*
23026  * Based on:
23027  * Ext JS Library 1.1.1
23028  * Copyright(c) 2006-2007, Ext JS, LLC.
23029  *
23030  * Originally Released Under LGPL - original licence link has changed is not relivant.
23031  *
23032  * Fork - LGPL
23033  * <script type="text/javascript">
23034  */
23035  
23036 /**
23037  * @class Roo.form.MonthField
23038  * @extends Roo.form.TriggerField
23039  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
23040 * @constructor
23041 * Create a new MonthField
23042 * @param {Object} config
23043  */
23044 Roo.form.MonthField = function(config){
23045     
23046     Roo.form.MonthField.superclass.constructor.call(this, config);
23047     
23048       this.addEvents({
23049          
23050         /**
23051          * @event select
23052          * Fires when a date is selected
23053              * @param {Roo.form.MonthFieeld} combo This combo box
23054              * @param {Date} date The date selected
23055              */
23056         'select' : true
23057          
23058     });
23059     
23060     
23061     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
23062     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
23063     this.ddMatch = null;
23064     if(this.disabledDates){
23065         var dd = this.disabledDates;
23066         var re = "(?:";
23067         for(var i = 0; i < dd.length; i++){
23068             re += dd[i];
23069             if(i != dd.length-1) re += "|";
23070         }
23071         this.ddMatch = new RegExp(re + ")");
23072     }
23073 };
23074
23075 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
23076     /**
23077      * @cfg {String} format
23078      * The default date format string which can be overriden for localization support.  The format must be
23079      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
23080      */
23081     format : "M Y",
23082     /**
23083      * @cfg {String} altFormats
23084      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
23085      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
23086      */
23087     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
23088     /**
23089      * @cfg {Array} disabledDays
23090      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
23091      */
23092     disabledDays : [0,1,2,3,4,5,6],
23093     /**
23094      * @cfg {String} disabledDaysText
23095      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
23096      */
23097     disabledDaysText : "Disabled",
23098     /**
23099      * @cfg {Array} disabledDates
23100      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
23101      * expression so they are very powerful. Some examples:
23102      * <ul>
23103      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
23104      * <li>["03/08", "09/16"] would disable those days for every year</li>
23105      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
23106      * <li>["03/../2006"] would disable every day in March 2006</li>
23107      * <li>["^03"] would disable every day in every March</li>
23108      * </ul>
23109      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
23110      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
23111      */
23112     disabledDates : null,
23113     /**
23114      * @cfg {String} disabledDatesText
23115      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
23116      */
23117     disabledDatesText : "Disabled",
23118     /**
23119      * @cfg {Date/String} minValue
23120      * The minimum allowed date. Can be either a Javascript date object or a string date in a
23121      * valid format (defaults to null).
23122      */
23123     minValue : null,
23124     /**
23125      * @cfg {Date/String} maxValue
23126      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23127      * valid format (defaults to null).
23128      */
23129     maxValue : null,
23130     /**
23131      * @cfg {String} minText
23132      * The error text to display when the date in the cell is before minValue (defaults to
23133      * 'The date in this field must be after {minValue}').
23134      */
23135     minText : "The date in this field must be equal to or after {0}",
23136     /**
23137      * @cfg {String} maxTextf
23138      * The error text to display when the date in the cell is after maxValue (defaults to
23139      * 'The date in this field must be before {maxValue}').
23140      */
23141     maxText : "The date in this field must be equal to or before {0}",
23142     /**
23143      * @cfg {String} invalidText
23144      * The error text to display when the date in the field is invalid (defaults to
23145      * '{value} is not a valid date - it must be in the format {format}').
23146      */
23147     invalidText : "{0} is not a valid date - it must be in the format {1}",
23148     /**
23149      * @cfg {String} triggerClass
23150      * An additional CSS class used to style the trigger button.  The trigger will always get the
23151      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23152      * which displays a calendar icon).
23153      */
23154     triggerClass : 'x-form-date-trigger',
23155     
23156
23157     /**
23158      * @cfg {Boolean} useIso
23159      * if enabled, then the date field will use a hidden field to store the 
23160      * real value as iso formated date. default (true)
23161      */ 
23162     useIso : true,
23163     /**
23164      * @cfg {String/Object} autoCreate
23165      * A DomHelper element spec, or true for a default element spec (defaults to
23166      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23167      */ 
23168     // private
23169     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23170     
23171     // private
23172     hiddenField: false,
23173     
23174     hideMonthPicker : false,
23175     
23176     onRender : function(ct, position)
23177     {
23178         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23179         if (this.useIso) {
23180             this.el.dom.removeAttribute('name'); 
23181             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23182                     'before', true);
23183             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23184             // prevent input submission
23185             this.hiddenName = this.name;
23186         }
23187             
23188             
23189     },
23190     
23191     // private
23192     validateValue : function(value)
23193     {
23194         value = this.formatDate(value);
23195         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23196             return false;
23197         }
23198         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23199              return true;
23200         }
23201         var svalue = value;
23202         value = this.parseDate(value);
23203         if(!value){
23204             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23205             return false;
23206         }
23207         var time = value.getTime();
23208         if(this.minValue && time < this.minValue.getTime()){
23209             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23210             return false;
23211         }
23212         if(this.maxValue && time > this.maxValue.getTime()){
23213             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23214             return false;
23215         }
23216         /*if(this.disabledDays){
23217             var day = value.getDay();
23218             for(var i = 0; i < this.disabledDays.length; i++) {
23219                 if(day === this.disabledDays[i]){
23220                     this.markInvalid(this.disabledDaysText);
23221                     return false;
23222                 }
23223             }
23224         }
23225         */
23226         var fvalue = this.formatDate(value);
23227         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23228             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23229             return false;
23230         }
23231         */
23232         return true;
23233     },
23234
23235     // private
23236     // Provides logic to override the default TriggerField.validateBlur which just returns true
23237     validateBlur : function(){
23238         return !this.menu || !this.menu.isVisible();
23239     },
23240
23241     /**
23242      * Returns the current date value of the date field.
23243      * @return {Date} The date value
23244      */
23245     getValue : function(){
23246         
23247         
23248         
23249         return  this.hiddenField ?
23250                 this.hiddenField.value :
23251                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23252     },
23253
23254     /**
23255      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23256      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23257      * (the default format used is "m/d/y").
23258      * <br />Usage:
23259      * <pre><code>
23260 //All of these calls set the same date value (May 4, 2006)
23261
23262 //Pass a date object:
23263 var dt = new Date('5/4/06');
23264 monthField.setValue(dt);
23265
23266 //Pass a date string (default format):
23267 monthField.setValue('5/4/06');
23268
23269 //Pass a date string (custom format):
23270 monthField.format = 'Y-m-d';
23271 monthField.setValue('2006-5-4');
23272 </code></pre>
23273      * @param {String/Date} date The date or valid date string
23274      */
23275     setValue : function(date){
23276         Roo.log('month setValue' + date);
23277         // can only be first of month..
23278         
23279         var val = this.parseDate(date);
23280         
23281         if (this.hiddenField) {
23282             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23283         }
23284         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23285         this.value = this.parseDate(date);
23286     },
23287
23288     // private
23289     parseDate : function(value){
23290         if(!value || value instanceof Date){
23291             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
23292             return value;
23293         }
23294         var v = Date.parseDate(value, this.format);
23295         if (!v && this.useIso) {
23296             v = Date.parseDate(value, 'Y-m-d');
23297         }
23298         if (v) {
23299             // 
23300             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
23301         }
23302         
23303         
23304         if(!v && this.altFormats){
23305             if(!this.altFormatsArray){
23306                 this.altFormatsArray = this.altFormats.split("|");
23307             }
23308             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23309                 v = Date.parseDate(value, this.altFormatsArray[i]);
23310             }
23311         }
23312         return v;
23313     },
23314
23315     // private
23316     formatDate : function(date, fmt){
23317         return (!date || !(date instanceof Date)) ?
23318                date : date.dateFormat(fmt || this.format);
23319     },
23320
23321     // private
23322     menuListeners : {
23323         select: function(m, d){
23324             this.setValue(d);
23325             this.fireEvent('select', this, d);
23326         },
23327         show : function(){ // retain focus styling
23328             this.onFocus();
23329         },
23330         hide : function(){
23331             this.focus.defer(10, this);
23332             var ml = this.menuListeners;
23333             this.menu.un("select", ml.select,  this);
23334             this.menu.un("show", ml.show,  this);
23335             this.menu.un("hide", ml.hide,  this);
23336         }
23337     },
23338     // private
23339     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23340     onTriggerClick : function(){
23341         if(this.disabled){
23342             return;
23343         }
23344         if(this.menu == null){
23345             this.menu = new Roo.menu.DateMenu();
23346            
23347         }
23348         
23349         Roo.apply(this.menu.picker,  {
23350             
23351             showClear: this.allowBlank,
23352             minDate : this.minValue,
23353             maxDate : this.maxValue,
23354             disabledDatesRE : this.ddMatch,
23355             disabledDatesText : this.disabledDatesText,
23356             
23357             format : this.useIso ? 'Y-m-d' : this.format,
23358             minText : String.format(this.minText, this.formatDate(this.minValue)),
23359             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23360             
23361         });
23362          this.menu.on(Roo.apply({}, this.menuListeners, {
23363             scope:this
23364         }));
23365        
23366         
23367         var m = this.menu;
23368         var p = m.picker;
23369         
23370         // hide month picker get's called when we called by 'before hide';
23371         
23372         var ignorehide = true;
23373         p.hideMonthPicker  = function(disableAnim){
23374             if (ignorehide) {
23375                 return;
23376             }
23377              if(this.monthPicker){
23378                 Roo.log("hideMonthPicker called");
23379                 if(disableAnim === true){
23380                     this.monthPicker.hide();
23381                 }else{
23382                     this.monthPicker.slideOut('t', {duration:.2});
23383                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
23384                     p.fireEvent("select", this, this.value);
23385                     m.hide();
23386                 }
23387             }
23388         }
23389         
23390         Roo.log('picker set value');
23391         Roo.log(this.getValue());
23392         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
23393         m.show(this.el, 'tl-bl?');
23394         ignorehide  = false;
23395         // this will trigger hideMonthPicker..
23396         
23397         
23398         // hidden the day picker
23399         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23400         
23401         
23402         
23403       
23404         
23405         p.showMonthPicker.defer(100, p);
23406     
23407         
23408        
23409     },
23410
23411     beforeBlur : function(){
23412         var v = this.parseDate(this.getRawValue());
23413         if(v){
23414             this.setValue(v);
23415         }
23416     }
23417
23418     /** @cfg {Boolean} grow @hide */
23419     /** @cfg {Number} growMin @hide */
23420     /** @cfg {Number} growMax @hide */
23421     /**
23422      * @hide
23423      * @method autoSize
23424      */
23425 });/*
23426  * Based on:
23427  * Ext JS Library 1.1.1
23428  * Copyright(c) 2006-2007, Ext JS, LLC.
23429  *
23430  * Originally Released Under LGPL - original licence link has changed is not relivant.
23431  *
23432  * Fork - LGPL
23433  * <script type="text/javascript">
23434  */
23435  
23436
23437 /**
23438  * @class Roo.form.ComboBox
23439  * @extends Roo.form.TriggerField
23440  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23441  * @constructor
23442  * Create a new ComboBox.
23443  * @param {Object} config Configuration options
23444  */
23445 Roo.form.ComboBox = function(config){
23446     Roo.form.ComboBox.superclass.constructor.call(this, config);
23447     this.addEvents({
23448         /**
23449          * @event expand
23450          * Fires when the dropdown list is expanded
23451              * @param {Roo.form.ComboBox} combo This combo box
23452              */
23453         'expand' : true,
23454         /**
23455          * @event collapse
23456          * Fires when the dropdown list is collapsed
23457              * @param {Roo.form.ComboBox} combo This combo box
23458              */
23459         'collapse' : true,
23460         /**
23461          * @event beforeselect
23462          * Fires before a list item is selected. Return false to cancel the selection.
23463              * @param {Roo.form.ComboBox} combo This combo box
23464              * @param {Roo.data.Record} record The data record returned from the underlying store
23465              * @param {Number} index The index of the selected item in the dropdown list
23466              */
23467         'beforeselect' : true,
23468         /**
23469          * @event select
23470          * Fires when a list item is selected
23471              * @param {Roo.form.ComboBox} combo This combo box
23472              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23473              * @param {Number} index The index of the selected item in the dropdown list
23474              */
23475         'select' : true,
23476         /**
23477          * @event beforequery
23478          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23479          * The event object passed has these properties:
23480              * @param {Roo.form.ComboBox} combo This combo box
23481              * @param {String} query The query
23482              * @param {Boolean} forceAll true to force "all" query
23483              * @param {Boolean} cancel true to cancel the query
23484              * @param {Object} e The query event object
23485              */
23486         'beforequery': true,
23487          /**
23488          * @event add
23489          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23490              * @param {Roo.form.ComboBox} combo This combo box
23491              */
23492         'add' : true,
23493         /**
23494          * @event edit
23495          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23496              * @param {Roo.form.ComboBox} combo This combo box
23497              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23498              */
23499         'edit' : true
23500         
23501         
23502     });
23503     if(this.transform){
23504         this.allowDomMove = false;
23505         var s = Roo.getDom(this.transform);
23506         if(!this.hiddenName){
23507             this.hiddenName = s.name;
23508         }
23509         if(!this.store){
23510             this.mode = 'local';
23511             var d = [], opts = s.options;
23512             for(var i = 0, len = opts.length;i < len; i++){
23513                 var o = opts[i];
23514                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23515                 if(o.selected) {
23516                     this.value = value;
23517                 }
23518                 d.push([value, o.text]);
23519             }
23520             this.store = new Roo.data.SimpleStore({
23521                 'id': 0,
23522                 fields: ['value', 'text'],
23523                 data : d
23524             });
23525             this.valueField = 'value';
23526             this.displayField = 'text';
23527         }
23528         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23529         if(!this.lazyRender){
23530             this.target = true;
23531             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23532             s.parentNode.removeChild(s); // remove it
23533             this.render(this.el.parentNode);
23534         }else{
23535             s.parentNode.removeChild(s); // remove it
23536         }
23537
23538     }
23539     if (this.store) {
23540         this.store = Roo.factory(this.store, Roo.data);
23541     }
23542     
23543     this.selectedIndex = -1;
23544     if(this.mode == 'local'){
23545         if(config.queryDelay === undefined){
23546             this.queryDelay = 10;
23547         }
23548         if(config.minChars === undefined){
23549             this.minChars = 0;
23550         }
23551     }
23552 };
23553
23554 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23555     /**
23556      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23557      */
23558     /**
23559      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23560      * rendering into an Roo.Editor, defaults to false)
23561      */
23562     /**
23563      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23564      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23565      */
23566     /**
23567      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23568      */
23569     /**
23570      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23571      * the dropdown list (defaults to undefined, with no header element)
23572      */
23573
23574      /**
23575      * @cfg {String/Roo.Template} tpl The template to use to render the output
23576      */
23577      
23578     // private
23579     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23580     /**
23581      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23582      */
23583     listWidth: undefined,
23584     /**
23585      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23586      * mode = 'remote' or 'text' if mode = 'local')
23587      */
23588     displayField: undefined,
23589     /**
23590      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23591      * mode = 'remote' or 'value' if mode = 'local'). 
23592      * Note: use of a valueField requires the user make a selection
23593      * in order for a value to be mapped.
23594      */
23595     valueField: undefined,
23596     
23597     
23598     /**
23599      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23600      * field's data value (defaults to the underlying DOM element's name)
23601      */
23602     hiddenName: undefined,
23603     /**
23604      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23605      */
23606     listClass: '',
23607     /**
23608      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23609      */
23610     selectedClass: 'x-combo-selected',
23611     /**
23612      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23613      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23614      * which displays a downward arrow icon).
23615      */
23616     triggerClass : 'x-form-arrow-trigger',
23617     /**
23618      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23619      */
23620     shadow:'sides',
23621     /**
23622      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23623      * anchor positions (defaults to 'tl-bl')
23624      */
23625     listAlign: 'tl-bl?',
23626     /**
23627      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23628      */
23629     maxHeight: 300,
23630     /**
23631      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23632      * query specified by the allQuery config option (defaults to 'query')
23633      */
23634     triggerAction: 'query',
23635     /**
23636      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23637      * (defaults to 4, does not apply if editable = false)
23638      */
23639     minChars : 4,
23640     /**
23641      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23642      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23643      */
23644     typeAhead: false,
23645     /**
23646      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23647      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23648      */
23649     queryDelay: 500,
23650     /**
23651      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23652      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23653      */
23654     pageSize: 0,
23655     /**
23656      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23657      * when editable = true (defaults to false)
23658      */
23659     selectOnFocus:false,
23660     /**
23661      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23662      */
23663     queryParam: 'query',
23664     /**
23665      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23666      * when mode = 'remote' (defaults to 'Loading...')
23667      */
23668     loadingText: 'Loading...',
23669     /**
23670      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23671      */
23672     resizable: false,
23673     /**
23674      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23675      */
23676     handleHeight : 8,
23677     /**
23678      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23679      * traditional select (defaults to true)
23680      */
23681     editable: true,
23682     /**
23683      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23684      */
23685     allQuery: '',
23686     /**
23687      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23688      */
23689     mode: 'remote',
23690     /**
23691      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23692      * listWidth has a higher value)
23693      */
23694     minListWidth : 70,
23695     /**
23696      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23697      * allow the user to set arbitrary text into the field (defaults to false)
23698      */
23699     forceSelection:false,
23700     /**
23701      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23702      * if typeAhead = true (defaults to 250)
23703      */
23704     typeAheadDelay : 250,
23705     /**
23706      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23707      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23708      */
23709     valueNotFoundText : undefined,
23710     /**
23711      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23712      */
23713     blockFocus : false,
23714     
23715     /**
23716      * @cfg {Boolean} disableClear Disable showing of clear button.
23717      */
23718     disableClear : false,
23719     /**
23720      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23721      */
23722     alwaysQuery : false,
23723     
23724     //private
23725     addicon : false,
23726     editicon: false,
23727     
23728     // element that contains real text value.. (when hidden is used..)
23729      
23730     // private
23731     onRender : function(ct, position){
23732         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23733         if(this.hiddenName){
23734             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23735                     'before', true);
23736             this.hiddenField.value =
23737                 this.hiddenValue !== undefined ? this.hiddenValue :
23738                 this.value !== undefined ? this.value : '';
23739
23740             // prevent input submission
23741             this.el.dom.removeAttribute('name');
23742              
23743              
23744         }
23745         if(Roo.isGecko){
23746             this.el.dom.setAttribute('autocomplete', 'off');
23747         }
23748
23749         var cls = 'x-combo-list';
23750
23751         this.list = new Roo.Layer({
23752             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23753         });
23754
23755         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23756         this.list.setWidth(lw);
23757         this.list.swallowEvent('mousewheel');
23758         this.assetHeight = 0;
23759
23760         if(this.title){
23761             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23762             this.assetHeight += this.header.getHeight();
23763         }
23764
23765         this.innerList = this.list.createChild({cls:cls+'-inner'});
23766         this.innerList.on('mouseover', this.onViewOver, this);
23767         this.innerList.on('mousemove', this.onViewMove, this);
23768         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23769         
23770         if(this.allowBlank && !this.pageSize && !this.disableClear){
23771             this.footer = this.list.createChild({cls:cls+'-ft'});
23772             this.pageTb = new Roo.Toolbar(this.footer);
23773            
23774         }
23775         if(this.pageSize){
23776             this.footer = this.list.createChild({cls:cls+'-ft'});
23777             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23778                     {pageSize: this.pageSize});
23779             
23780         }
23781         
23782         if (this.pageTb && this.allowBlank && !this.disableClear) {
23783             var _this = this;
23784             this.pageTb.add(new Roo.Toolbar.Fill(), {
23785                 cls: 'x-btn-icon x-btn-clear',
23786                 text: '&#160;',
23787                 handler: function()
23788                 {
23789                     _this.collapse();
23790                     _this.clearValue();
23791                     _this.onSelect(false, -1);
23792                 }
23793             });
23794         }
23795         if (this.footer) {
23796             this.assetHeight += this.footer.getHeight();
23797         }
23798         
23799
23800         if(!this.tpl){
23801             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23802         }
23803
23804         this.view = new Roo.View(this.innerList, this.tpl, {
23805             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23806         });
23807
23808         this.view.on('click', this.onViewClick, this);
23809
23810         this.store.on('beforeload', this.onBeforeLoad, this);
23811         this.store.on('load', this.onLoad, this);
23812         this.store.on('loadexception', this.onLoadException, this);
23813
23814         if(this.resizable){
23815             this.resizer = new Roo.Resizable(this.list,  {
23816                pinned:true, handles:'se'
23817             });
23818             this.resizer.on('resize', function(r, w, h){
23819                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23820                 this.listWidth = w;
23821                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23822                 this.restrictHeight();
23823             }, this);
23824             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23825         }
23826         if(!this.editable){
23827             this.editable = true;
23828             this.setEditable(false);
23829         }  
23830         
23831         
23832         if (typeof(this.events.add.listeners) != 'undefined') {
23833             
23834             this.addicon = this.wrap.createChild(
23835                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23836        
23837             this.addicon.on('click', function(e) {
23838                 this.fireEvent('add', this);
23839             }, this);
23840         }
23841         if (typeof(this.events.edit.listeners) != 'undefined') {
23842             
23843             this.editicon = this.wrap.createChild(
23844                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23845             if (this.addicon) {
23846                 this.editicon.setStyle('margin-left', '40px');
23847             }
23848             this.editicon.on('click', function(e) {
23849                 
23850                 // we fire even  if inothing is selected..
23851                 this.fireEvent('edit', this, this.lastData );
23852                 
23853             }, this);
23854         }
23855         
23856         
23857         
23858     },
23859
23860     // private
23861     initEvents : function(){
23862         Roo.form.ComboBox.superclass.initEvents.call(this);
23863
23864         this.keyNav = new Roo.KeyNav(this.el, {
23865             "up" : function(e){
23866                 this.inKeyMode = true;
23867                 this.selectPrev();
23868             },
23869
23870             "down" : function(e){
23871                 if(!this.isExpanded()){
23872                     this.onTriggerClick();
23873                 }else{
23874                     this.inKeyMode = true;
23875                     this.selectNext();
23876                 }
23877             },
23878
23879             "enter" : function(e){
23880                 this.onViewClick();
23881                 //return true;
23882             },
23883
23884             "esc" : function(e){
23885                 this.collapse();
23886             },
23887
23888             "tab" : function(e){
23889                 this.onViewClick(false);
23890                 this.fireEvent("specialkey", this, e);
23891                 return true;
23892             },
23893
23894             scope : this,
23895
23896             doRelay : function(foo, bar, hname){
23897                 if(hname == 'down' || this.scope.isExpanded()){
23898                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23899                 }
23900                 return true;
23901             },
23902
23903             forceKeyDown: true
23904         });
23905         this.queryDelay = Math.max(this.queryDelay || 10,
23906                 this.mode == 'local' ? 10 : 250);
23907         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23908         if(this.typeAhead){
23909             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23910         }
23911         if(this.editable !== false){
23912             this.el.on("keyup", this.onKeyUp, this);
23913         }
23914         if(this.forceSelection){
23915             this.on('blur', this.doForce, this);
23916         }
23917     },
23918
23919     onDestroy : function(){
23920         if(this.view){
23921             this.view.setStore(null);
23922             this.view.el.removeAllListeners();
23923             this.view.el.remove();
23924             this.view.purgeListeners();
23925         }
23926         if(this.list){
23927             this.list.destroy();
23928         }
23929         if(this.store){
23930             this.store.un('beforeload', this.onBeforeLoad, this);
23931             this.store.un('load', this.onLoad, this);
23932             this.store.un('loadexception', this.onLoadException, this);
23933         }
23934         Roo.form.ComboBox.superclass.onDestroy.call(this);
23935     },
23936
23937     // private
23938     fireKey : function(e){
23939         if(e.isNavKeyPress() && !this.list.isVisible()){
23940             this.fireEvent("specialkey", this, e);
23941         }
23942     },
23943
23944     // private
23945     onResize: function(w, h){
23946         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23947         
23948         if(typeof w != 'number'){
23949             // we do not handle it!?!?
23950             return;
23951         }
23952         var tw = this.trigger.getWidth();
23953         tw += this.addicon ? this.addicon.getWidth() : 0;
23954         tw += this.editicon ? this.editicon.getWidth() : 0;
23955         var x = w - tw;
23956         this.el.setWidth( this.adjustWidth('input', x));
23957             
23958         this.trigger.setStyle('left', x+'px');
23959         
23960         if(this.list && this.listWidth === undefined){
23961             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23962             this.list.setWidth(lw);
23963             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23964         }
23965         
23966     
23967         
23968     },
23969
23970     /**
23971      * Allow or prevent the user from directly editing the field text.  If false is passed,
23972      * the user will only be able to select from the items defined in the dropdown list.  This method
23973      * is the runtime equivalent of setting the 'editable' config option at config time.
23974      * @param {Boolean} value True to allow the user to directly edit the field text
23975      */
23976     setEditable : function(value){
23977         if(value == this.editable){
23978             return;
23979         }
23980         this.editable = value;
23981         if(!value){
23982             this.el.dom.setAttribute('readOnly', true);
23983             this.el.on('mousedown', this.onTriggerClick,  this);
23984             this.el.addClass('x-combo-noedit');
23985         }else{
23986             this.el.dom.setAttribute('readOnly', false);
23987             this.el.un('mousedown', this.onTriggerClick,  this);
23988             this.el.removeClass('x-combo-noedit');
23989         }
23990     },
23991
23992     // private
23993     onBeforeLoad : function(){
23994         if(!this.hasFocus){
23995             return;
23996         }
23997         this.innerList.update(this.loadingText ?
23998                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23999         this.restrictHeight();
24000         this.selectedIndex = -1;
24001     },
24002
24003     // private
24004     onLoad : function(){
24005         if(!this.hasFocus){
24006             return;
24007         }
24008         if(this.store.getCount() > 0){
24009             this.expand();
24010             this.restrictHeight();
24011             if(this.lastQuery == this.allQuery){
24012                 if(this.editable){
24013                     this.el.dom.select();
24014                 }
24015                 if(!this.selectByValue(this.value, true)){
24016                     this.select(0, true);
24017                 }
24018             }else{
24019                 this.selectNext();
24020                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
24021                     this.taTask.delay(this.typeAheadDelay);
24022                 }
24023             }
24024         }else{
24025             this.onEmptyResults();
24026         }
24027         //this.el.focus();
24028     },
24029     // private
24030     onLoadException : function()
24031     {
24032         this.collapse();
24033         Roo.log(this.store.reader.jsonData);
24034         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
24035             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
24036         }
24037         
24038         
24039     },
24040     // private
24041     onTypeAhead : function(){
24042         if(this.store.getCount() > 0){
24043             var r = this.store.getAt(0);
24044             var newValue = r.data[this.displayField];
24045             var len = newValue.length;
24046             var selStart = this.getRawValue().length;
24047             if(selStart != len){
24048                 this.setRawValue(newValue);
24049                 this.selectText(selStart, newValue.length);
24050             }
24051         }
24052     },
24053
24054     // private
24055     onSelect : function(record, index){
24056         if(this.fireEvent('beforeselect', this, record, index) !== false){
24057             this.setFromData(index > -1 ? record.data : false);
24058             this.collapse();
24059             this.fireEvent('select', this, record, index);
24060         }
24061     },
24062
24063     /**
24064      * Returns the currently selected field value or empty string if no value is set.
24065      * @return {String} value The selected value
24066      */
24067     getValue : function(){
24068         if(this.valueField){
24069             return typeof this.value != 'undefined' ? this.value : '';
24070         }else{
24071             return Roo.form.ComboBox.superclass.getValue.call(this);
24072         }
24073     },
24074
24075     /**
24076      * Clears any text/value currently set in the field
24077      */
24078     clearValue : function(){
24079         if(this.hiddenField){
24080             this.hiddenField.value = '';
24081         }
24082         this.value = '';
24083         this.setRawValue('');
24084         this.lastSelectionText = '';
24085         
24086     },
24087
24088     /**
24089      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
24090      * will be displayed in the field.  If the value does not match the data value of an existing item,
24091      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
24092      * Otherwise the field will be blank (although the value will still be set).
24093      * @param {String} value The value to match
24094      */
24095     setValue : function(v){
24096         var text = v;
24097         if(this.valueField){
24098             var r = this.findRecord(this.valueField, v);
24099             if(r){
24100                 text = r.data[this.displayField];
24101             }else if(this.valueNotFoundText !== undefined){
24102                 text = this.valueNotFoundText;
24103             }
24104         }
24105         this.lastSelectionText = text;
24106         if(this.hiddenField){
24107             this.hiddenField.value = v;
24108         }
24109         Roo.form.ComboBox.superclass.setValue.call(this, text);
24110         this.value = v;
24111     },
24112     /**
24113      * @property {Object} the last set data for the element
24114      */
24115     
24116     lastData : false,
24117     /**
24118      * Sets the value of the field based on a object which is related to the record format for the store.
24119      * @param {Object} value the value to set as. or false on reset?
24120      */
24121     setFromData : function(o){
24122         var dv = ''; // display value
24123         var vv = ''; // value value..
24124         this.lastData = o;
24125         if (this.displayField) {
24126             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
24127         } else {
24128             // this is an error condition!!!
24129             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
24130         }
24131         
24132         if(this.valueField){
24133             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
24134         }
24135         if(this.hiddenField){
24136             this.hiddenField.value = vv;
24137             
24138             this.lastSelectionText = dv;
24139             Roo.form.ComboBox.superclass.setValue.call(this, dv);
24140             this.value = vv;
24141             return;
24142         }
24143         // no hidden field.. - we store the value in 'value', but still display
24144         // display field!!!!
24145         this.lastSelectionText = dv;
24146         Roo.form.ComboBox.superclass.setValue.call(this, dv);
24147         this.value = vv;
24148         
24149         
24150     },
24151     // private
24152     reset : function(){
24153         // overridden so that last data is reset..
24154         this.setValue(this.originalValue);
24155         this.clearInvalid();
24156         this.lastData = false;
24157         if (this.view) {
24158             this.view.clearSelections();
24159         }
24160     },
24161     // private
24162     findRecord : function(prop, value){
24163         var record;
24164         if(this.store.getCount() > 0){
24165             this.store.each(function(r){
24166                 if(r.data[prop] == value){
24167                     record = r;
24168                     return false;
24169                 }
24170                 return true;
24171             });
24172         }
24173         return record;
24174     },
24175     
24176     getName: function()
24177     {
24178         // returns hidden if it's set..
24179         if (!this.rendered) {return ''};
24180         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24181         
24182     },
24183     // private
24184     onViewMove : function(e, t){
24185         this.inKeyMode = false;
24186     },
24187
24188     // private
24189     onViewOver : function(e, t){
24190         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24191             return;
24192         }
24193         var item = this.view.findItemFromChild(t);
24194         if(item){
24195             var index = this.view.indexOf(item);
24196             this.select(index, false);
24197         }
24198     },
24199
24200     // private
24201     onViewClick : function(doFocus)
24202     {
24203         var index = this.view.getSelectedIndexes()[0];
24204         var r = this.store.getAt(index);
24205         if(r){
24206             this.onSelect(r, index);
24207         }
24208         if(doFocus !== false && !this.blockFocus){
24209             this.el.focus();
24210         }
24211     },
24212
24213     // private
24214     restrictHeight : function(){
24215         this.innerList.dom.style.height = '';
24216         var inner = this.innerList.dom;
24217         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24218         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24219         this.list.beginUpdate();
24220         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24221         this.list.alignTo(this.el, this.listAlign);
24222         this.list.endUpdate();
24223     },
24224
24225     // private
24226     onEmptyResults : function(){
24227         this.collapse();
24228     },
24229
24230     /**
24231      * Returns true if the dropdown list is expanded, else false.
24232      */
24233     isExpanded : function(){
24234         return this.list.isVisible();
24235     },
24236
24237     /**
24238      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24239      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24240      * @param {String} value The data value of the item to select
24241      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24242      * selected item if it is not currently in view (defaults to true)
24243      * @return {Boolean} True if the value matched an item in the list, else false
24244      */
24245     selectByValue : function(v, scrollIntoView){
24246         if(v !== undefined && v !== null){
24247             var r = this.findRecord(this.valueField || this.displayField, v);
24248             if(r){
24249                 this.select(this.store.indexOf(r), scrollIntoView);
24250                 return true;
24251             }
24252         }
24253         return false;
24254     },
24255
24256     /**
24257      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24258      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24259      * @param {Number} index The zero-based index of the list item to select
24260      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24261      * selected item if it is not currently in view (defaults to true)
24262      */
24263     select : function(index, scrollIntoView){
24264         this.selectedIndex = index;
24265         this.view.select(index);
24266         if(scrollIntoView !== false){
24267             var el = this.view.getNode(index);
24268             if(el){
24269                 this.innerList.scrollChildIntoView(el, false);
24270             }
24271         }
24272     },
24273
24274     // private
24275     selectNext : function(){
24276         var ct = this.store.getCount();
24277         if(ct > 0){
24278             if(this.selectedIndex == -1){
24279                 this.select(0);
24280             }else if(this.selectedIndex < ct-1){
24281                 this.select(this.selectedIndex+1);
24282             }
24283         }
24284     },
24285
24286     // private
24287     selectPrev : function(){
24288         var ct = this.store.getCount();
24289         if(ct > 0){
24290             if(this.selectedIndex == -1){
24291                 this.select(0);
24292             }else if(this.selectedIndex != 0){
24293                 this.select(this.selectedIndex-1);
24294             }
24295         }
24296     },
24297
24298     // private
24299     onKeyUp : function(e){
24300         if(this.editable !== false && !e.isSpecialKey()){
24301             this.lastKey = e.getKey();
24302             this.dqTask.delay(this.queryDelay);
24303         }
24304     },
24305
24306     // private
24307     validateBlur : function(){
24308         return !this.list || !this.list.isVisible();   
24309     },
24310
24311     // private
24312     initQuery : function(){
24313         this.doQuery(this.getRawValue());
24314     },
24315
24316     // private
24317     doForce : function(){
24318         if(this.el.dom.value.length > 0){
24319             this.el.dom.value =
24320                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24321              
24322         }
24323     },
24324
24325     /**
24326      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24327      * query allowing the query action to be canceled if needed.
24328      * @param {String} query The SQL query to execute
24329      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24330      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24331      * saved in the current store (defaults to false)
24332      */
24333     doQuery : function(q, forceAll){
24334         if(q === undefined || q === null){
24335             q = '';
24336         }
24337         var qe = {
24338             query: q,
24339             forceAll: forceAll,
24340             combo: this,
24341             cancel:false
24342         };
24343         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24344             return false;
24345         }
24346         q = qe.query;
24347         forceAll = qe.forceAll;
24348         if(forceAll === true || (q.length >= this.minChars)){
24349             if(this.lastQuery != q || this.alwaysQuery){
24350                 this.lastQuery = q;
24351                 if(this.mode == 'local'){
24352                     this.selectedIndex = -1;
24353                     if(forceAll){
24354                         this.store.clearFilter();
24355                     }else{
24356                         this.store.filter(this.displayField, q);
24357                     }
24358                     this.onLoad();
24359                 }else{
24360                     this.store.baseParams[this.queryParam] = q;
24361                     this.store.load({
24362                         params: this.getParams(q)
24363                     });
24364                     this.expand();
24365                 }
24366             }else{
24367                 this.selectedIndex = -1;
24368                 this.onLoad();   
24369             }
24370         }
24371     },
24372
24373     // private
24374     getParams : function(q){
24375         var p = {};
24376         //p[this.queryParam] = q;
24377         if(this.pageSize){
24378             p.start = 0;
24379             p.limit = this.pageSize;
24380         }
24381         return p;
24382     },
24383
24384     /**
24385      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24386      */
24387     collapse : function(){
24388         if(!this.isExpanded()){
24389             return;
24390         }
24391         this.list.hide();
24392         Roo.get(document).un('mousedown', this.collapseIf, this);
24393         Roo.get(document).un('mousewheel', this.collapseIf, this);
24394         if (!this.editable) {
24395             Roo.get(document).un('keydown', this.listKeyPress, this);
24396         }
24397         this.fireEvent('collapse', this);
24398     },
24399
24400     // private
24401     collapseIf : function(e){
24402         if(!e.within(this.wrap) && !e.within(this.list)){
24403             this.collapse();
24404         }
24405     },
24406
24407     /**
24408      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24409      */
24410     expand : function(){
24411         if(this.isExpanded() || !this.hasFocus){
24412             return;
24413         }
24414         this.list.alignTo(this.el, this.listAlign);
24415         this.list.show();
24416         Roo.get(document).on('mousedown', this.collapseIf, this);
24417         Roo.get(document).on('mousewheel', this.collapseIf, this);
24418         if (!this.editable) {
24419             Roo.get(document).on('keydown', this.listKeyPress, this);
24420         }
24421         
24422         this.fireEvent('expand', this);
24423     },
24424
24425     // private
24426     // Implements the default empty TriggerField.onTriggerClick function
24427     onTriggerClick : function(){
24428         if(this.disabled){
24429             return;
24430         }
24431         if(this.isExpanded()){
24432             this.collapse();
24433             if (!this.blockFocus) {
24434                 this.el.focus();
24435             }
24436             
24437         }else {
24438             this.hasFocus = true;
24439             if(this.triggerAction == 'all') {
24440                 this.doQuery(this.allQuery, true);
24441             } else {
24442                 this.doQuery(this.getRawValue());
24443             }
24444             if (!this.blockFocus) {
24445                 this.el.focus();
24446             }
24447         }
24448     },
24449     listKeyPress : function(e)
24450     {
24451         //Roo.log('listkeypress');
24452         // scroll to first matching element based on key pres..
24453         if (e.isSpecialKey()) {
24454             return false;
24455         }
24456         var k = String.fromCharCode(e.getKey()).toUpperCase();
24457         //Roo.log(k);
24458         var match  = false;
24459         var csel = this.view.getSelectedNodes();
24460         var cselitem = false;
24461         if (csel.length) {
24462             var ix = this.view.indexOf(csel[0]);
24463             cselitem  = this.store.getAt(ix);
24464             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24465                 cselitem = false;
24466             }
24467             
24468         }
24469         
24470         this.store.each(function(v) { 
24471             if (cselitem) {
24472                 // start at existing selection.
24473                 if (cselitem.id == v.id) {
24474                     cselitem = false;
24475                 }
24476                 return;
24477             }
24478                 
24479             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24480                 match = this.store.indexOf(v);
24481                 return false;
24482             }
24483         }, this);
24484         
24485         if (match === false) {
24486             return true; // no more action?
24487         }
24488         // scroll to?
24489         this.view.select(match);
24490         var sn = Roo.get(this.view.getSelectedNodes()[0])
24491         sn.scrollIntoView(sn.dom.parentNode, false);
24492     }
24493
24494     /** 
24495     * @cfg {Boolean} grow 
24496     * @hide 
24497     */
24498     /** 
24499     * @cfg {Number} growMin 
24500     * @hide 
24501     */
24502     /** 
24503     * @cfg {Number} growMax 
24504     * @hide 
24505     */
24506     /**
24507      * @hide
24508      * @method autoSize
24509      */
24510 });/*
24511  * Copyright(c) 2010-2012, Roo J Solutions Limited
24512  *
24513  * Licence LGPL
24514  *
24515  */
24516
24517 /**
24518  * @class Roo.form.ComboBoxArray
24519  * @extends Roo.form.TextField
24520  * A facebook style adder... for lists of email / people / countries  etc...
24521  * pick multiple items from a combo box, and shows each one.
24522  *
24523  *  Fred [x]  Brian [x]  [Pick another |v]
24524  *
24525  *
24526  *  For this to work: it needs various extra information
24527  *    - normal combo problay has
24528  *      name, hiddenName
24529  *    + displayField, valueField
24530  *
24531  *    For our purpose...
24532  *
24533  *
24534  *   If we change from 'extends' to wrapping...
24535  *   
24536  *  
24537  *
24538  
24539  
24540  * @constructor
24541  * Create a new ComboBoxArray.
24542  * @param {Object} config Configuration options
24543  */
24544  
24545
24546 Roo.form.ComboBoxArray = function(config)
24547 {
24548     
24549     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24550     
24551     this.items = new Roo.util.MixedCollection(false);
24552     
24553     // construct the child combo...
24554     
24555     
24556     
24557     
24558    
24559     
24560 }
24561
24562  
24563 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24564
24565     /**
24566      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24567      */
24568     
24569     lastData : false,
24570     
24571     // behavies liek a hiddne field
24572     inputType:      'hidden',
24573     /**
24574      * @cfg {Number} width The width of the box that displays the selected element
24575      */ 
24576     width:          300,
24577
24578     
24579     
24580     /**
24581      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24582      */
24583     name : false,
24584     /**
24585      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24586      */
24587     hiddenName : false,
24588     
24589     
24590     // private the array of items that are displayed..
24591     items  : false,
24592     // private - the hidden field el.
24593     hiddenEl : false,
24594     // private - the filed el..
24595     el : false,
24596     
24597     //validateValue : function() { return true; }, // all values are ok!
24598     //onAddClick: function() { },
24599     
24600     onRender : function(ct, position) 
24601     {
24602         
24603         // create the standard hidden element
24604         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24605         
24606         
24607         // give fake names to child combo;
24608         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24609         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24610         
24611         this.combo = Roo.factory(this.combo, Roo.form);
24612         this.combo.onRender(ct, position);
24613         if (typeof(this.combo.width) != 'undefined') {
24614             this.combo.onResize(this.combo.width,0);
24615         }
24616         
24617         this.combo.initEvents();
24618         
24619         // assigned so form know we need to do this..
24620         this.store          = this.combo.store;
24621         this.valueField     = this.combo.valueField;
24622         this.displayField   = this.combo.displayField ;
24623         
24624         
24625         this.combo.wrap.addClass('x-cbarray-grp');
24626         
24627         var cbwrap = this.combo.wrap.createChild(
24628             {tag: 'div', cls: 'x-cbarray-cb'},
24629             this.combo.el.dom
24630         );
24631         
24632              
24633         this.hiddenEl = this.combo.wrap.createChild({
24634             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24635         });
24636         this.el = this.combo.wrap.createChild({
24637             tag: 'input',  type:'hidden' , name: this.name, value : ''
24638         });
24639          //   this.el.dom.removeAttribute("name");
24640         
24641         
24642         this.outerWrap = this.combo.wrap;
24643         this.wrap = cbwrap;
24644         
24645         this.outerWrap.setWidth(this.width);
24646         this.outerWrap.dom.removeChild(this.el.dom);
24647         
24648         this.wrap.dom.appendChild(this.el.dom);
24649         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24650         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24651         
24652         this.combo.trigger.setStyle('position','relative');
24653         this.combo.trigger.setStyle('left', '0px');
24654         this.combo.trigger.setStyle('top', '2px');
24655         
24656         this.combo.el.setStyle('vertical-align', 'text-bottom');
24657         
24658         //this.trigger.setStyle('vertical-align', 'top');
24659         
24660         // this should use the code from combo really... on('add' ....)
24661         if (this.adder) {
24662             
24663         
24664             this.adder = this.outerWrap.createChild(
24665                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24666             var _t = this;
24667             this.adder.on('click', function(e) {
24668                 _t.fireEvent('adderclick', this, e);
24669             }, _t);
24670         }
24671         //var _t = this;
24672         //this.adder.on('click', this.onAddClick, _t);
24673         
24674         
24675         this.combo.on('select', function(cb, rec, ix) {
24676             this.addItem(rec.data);
24677             
24678             cb.setValue('');
24679             cb.el.dom.value = '';
24680             //cb.lastData = rec.data;
24681             // add to list
24682             
24683         }, this);
24684         
24685         
24686     },
24687     
24688     
24689     getName: function()
24690     {
24691         // returns hidden if it's set..
24692         if (!this.rendered) {return ''};
24693         return  this.hiddenName ? this.hiddenName : this.name;
24694         
24695     },
24696     
24697     
24698     onResize: function(w, h){
24699         
24700         return;
24701         // not sure if this is needed..
24702         //this.combo.onResize(w,h);
24703         
24704         if(typeof w != 'number'){
24705             // we do not handle it!?!?
24706             return;
24707         }
24708         var tw = this.combo.trigger.getWidth();
24709         tw += this.addicon ? this.addicon.getWidth() : 0;
24710         tw += this.editicon ? this.editicon.getWidth() : 0;
24711         var x = w - tw;
24712         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24713             
24714         this.combo.trigger.setStyle('left', '0px');
24715         
24716         if(this.list && this.listWidth === undefined){
24717             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24718             this.list.setWidth(lw);
24719             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24720         }
24721         
24722     
24723         
24724     },
24725     
24726     addItem: function(rec)
24727     {
24728         var valueField = this.combo.valueField;
24729         var displayField = this.combo.displayField;
24730         if (this.items.indexOfKey(rec[valueField]) > -1) {
24731             //console.log("GOT " + rec.data.id);
24732             return;
24733         }
24734         
24735         var x = new Roo.form.ComboBoxArray.Item({
24736             //id : rec[this.idField],
24737             data : rec,
24738             displayField : displayField ,
24739             tipField : displayField ,
24740             cb : this
24741         });
24742         // use the 
24743         this.items.add(rec[valueField],x);
24744         // add it before the element..
24745         this.updateHiddenEl();
24746         x.render(this.outerWrap, this.wrap.dom);
24747         // add the image handler..
24748     },
24749     
24750     updateHiddenEl : function()
24751     {
24752         this.validate();
24753         if (!this.hiddenEl) {
24754             return;
24755         }
24756         var ar = [];
24757         var idField = this.combo.valueField;
24758         
24759         this.items.each(function(f) {
24760             ar.push(f.data[idField]);
24761            
24762         });
24763         this.hiddenEl.dom.value = ar.join(',');
24764         this.validate();
24765     },
24766     
24767     reset : function()
24768     {
24769         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24770         this.items.each(function(f) {
24771            f.remove(); 
24772         });
24773         this.el.dom.value = '';
24774         if (this.hiddenEl) {
24775             this.hiddenEl.dom.value = '';
24776         }
24777         
24778     },
24779     getValue: function()
24780     {
24781         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24782     },
24783     setValue: function(v) // not a valid action - must use addItems..
24784     {
24785          
24786         this.reset();
24787         
24788         
24789         
24790         if (this.store.isLocal && (typeof(v) == 'string')) {
24791             // then we can use the store to find the values..
24792             // comma seperated at present.. this needs to allow JSON based encoding..
24793             this.hiddenEl.value  = v;
24794             var v_ar = [];
24795             Roo.each(v.split(','), function(k) {
24796                 Roo.log("CHECK " + this.valueField + ',' + k);
24797                 var li = this.store.query(this.valueField, k);
24798                 if (!li.length) {
24799                     return;
24800                 }
24801                 var add = {};
24802                 add[this.valueField] = k;
24803                 add[this.displayField] = li.item(0).data[this.displayField];
24804                 
24805                 this.addItem(add);
24806             }, this) 
24807              
24808         }
24809         if (typeof(v) == 'object') {
24810             // then let's assume it's an array of objects..
24811             Roo.each(v, function(l) {
24812                 this.addItem(l);
24813             }, this);
24814              
24815         }
24816         
24817         
24818     },
24819     setFromData: function(v)
24820     {
24821         // this recieves an object, if setValues is called.
24822         this.reset();
24823         this.el.dom.value = v[this.displayField];
24824         this.hiddenEl.dom.value = v[this.valueField];
24825         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24826             return;
24827         }
24828         var kv = v[this.valueField];
24829         var dv = v[this.displayField];
24830         kv = typeof(kv) != 'string' ? '' : kv;
24831         dv = typeof(dv) != 'string' ? '' : dv;
24832         
24833         
24834         var keys = kv.split(',');
24835         var display = dv.split(',');
24836         for (var i = 0 ; i < keys.length; i++) {
24837             
24838             add = {};
24839             add[this.valueField] = keys[i];
24840             add[this.displayField] = display[i];
24841             this.addItem(add);
24842         }
24843       
24844         
24845     },
24846     
24847     
24848     validateValue : function(value){
24849         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24850         
24851     }
24852     
24853 });
24854
24855
24856
24857 /**
24858  * @class Roo.form.ComboBoxArray.Item
24859  * @extends Roo.BoxComponent
24860  * A selected item in the list
24861  *  Fred [x]  Brian [x]  [Pick another |v]
24862  * 
24863  * @constructor
24864  * Create a new item.
24865  * @param {Object} config Configuration options
24866  */
24867  
24868 Roo.form.ComboBoxArray.Item = function(config) {
24869     config.id = Roo.id();
24870     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24871 }
24872
24873 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24874     data : {},
24875     cb: false,
24876     displayField : false,
24877     tipField : false,
24878     
24879     
24880     defaultAutoCreate : {
24881         tag: 'div',
24882         cls: 'x-cbarray-item',
24883         cn : [ 
24884             { tag: 'div' },
24885             {
24886                 tag: 'img',
24887                 width:16,
24888                 height : 16,
24889                 src : Roo.BLANK_IMAGE_URL ,
24890                 align: 'center'
24891             }
24892         ]
24893         
24894     },
24895     
24896  
24897     onRender : function(ct, position)
24898     {
24899         Roo.form.Field.superclass.onRender.call(this, ct, position);
24900         
24901         if(!this.el){
24902             var cfg = this.getAutoCreate();
24903             this.el = ct.createChild(cfg, position);
24904         }
24905         
24906         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24907         
24908         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24909             this.cb.renderer(this.data) :
24910             String.format('{0}',this.data[this.displayField]);
24911         
24912             
24913         this.el.child('div').dom.setAttribute('qtip',
24914                         String.format('{0}',this.data[this.tipField])
24915         );
24916         
24917         this.el.child('img').on('click', this.remove, this);
24918         
24919     },
24920    
24921     remove : function()
24922     {
24923         
24924         this.cb.items.remove(this);
24925         this.el.child('img').un('click', this.remove, this);
24926         this.el.remove();
24927         this.cb.updateHiddenEl();
24928     }
24929     
24930     
24931 });/*
24932  * Based on:
24933  * Ext JS Library 1.1.1
24934  * Copyright(c) 2006-2007, Ext JS, LLC.
24935  *
24936  * Originally Released Under LGPL - original licence link has changed is not relivant.
24937  *
24938  * Fork - LGPL
24939  * <script type="text/javascript">
24940  */
24941 /**
24942  * @class Roo.form.Checkbox
24943  * @extends Roo.form.Field
24944  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24945  * @constructor
24946  * Creates a new Checkbox
24947  * @param {Object} config Configuration options
24948  */
24949 Roo.form.Checkbox = function(config){
24950     Roo.form.Checkbox.superclass.constructor.call(this, config);
24951     this.addEvents({
24952         /**
24953          * @event check
24954          * Fires when the checkbox is checked or unchecked.
24955              * @param {Roo.form.Checkbox} this This checkbox
24956              * @param {Boolean} checked The new checked value
24957              */
24958         check : true
24959     });
24960 };
24961
24962 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24963     /**
24964      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24965      */
24966     focusClass : undefined,
24967     /**
24968      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24969      */
24970     fieldClass: "x-form-field",
24971     /**
24972      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24973      */
24974     checked: false,
24975     /**
24976      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24977      * {tag: "input", type: "checkbox", autocomplete: "off"})
24978      */
24979     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24980     /**
24981      * @cfg {String} boxLabel The text that appears beside the checkbox
24982      */
24983     boxLabel : "",
24984     /**
24985      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24986      */  
24987     inputValue : '1',
24988     /**
24989      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24990      */
24991      valueOff: '0', // value when not checked..
24992
24993     actionMode : 'viewEl', 
24994     //
24995     // private
24996     itemCls : 'x-menu-check-item x-form-item',
24997     groupClass : 'x-menu-group-item',
24998     inputType : 'hidden',
24999     
25000     
25001     inSetChecked: false, // check that we are not calling self...
25002     
25003     inputElement: false, // real input element?
25004     basedOn: false, // ????
25005     
25006     isFormField: true, // not sure where this is needed!!!!
25007
25008     onResize : function(){
25009         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
25010         if(!this.boxLabel){
25011             this.el.alignTo(this.wrap, 'c-c');
25012         }
25013     },
25014
25015     initEvents : function(){
25016         Roo.form.Checkbox.superclass.initEvents.call(this);
25017         this.el.on("click", this.onClick,  this);
25018         this.el.on("change", this.onClick,  this);
25019     },
25020
25021
25022     getResizeEl : function(){
25023         return this.wrap;
25024     },
25025
25026     getPositionEl : function(){
25027         return this.wrap;
25028     },
25029
25030     // private
25031     onRender : function(ct, position){
25032         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25033         /*
25034         if(this.inputValue !== undefined){
25035             this.el.dom.value = this.inputValue;
25036         }
25037         */
25038         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25039         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25040         var viewEl = this.wrap.createChild({ 
25041             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25042         this.viewEl = viewEl;   
25043         this.wrap.on('click', this.onClick,  this); 
25044         
25045         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25046         this.el.on('propertychange', this.setFromHidden,  this);  //ie
25047         
25048         
25049         
25050         if(this.boxLabel){
25051             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25052         //    viewEl.on('click', this.onClick,  this); 
25053         }
25054         //if(this.checked){
25055             this.setChecked(this.checked);
25056         //}else{
25057             //this.checked = this.el.dom;
25058         //}
25059
25060     },
25061
25062     // private
25063     initValue : Roo.emptyFn,
25064
25065     /**
25066      * Returns the checked state of the checkbox.
25067      * @return {Boolean} True if checked, else false
25068      */
25069     getValue : function(){
25070         if(this.el){
25071             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
25072         }
25073         return this.valueOff;
25074         
25075     },
25076
25077         // private
25078     onClick : function(){ 
25079         this.setChecked(!this.checked);
25080
25081         //if(this.el.dom.checked != this.checked){
25082         //    this.setValue(this.el.dom.checked);
25083        // }
25084     },
25085
25086     /**
25087      * Sets the checked state of the checkbox.
25088      * On is always based on a string comparison between inputValue and the param.
25089      * @param {Boolean/String} value - the value to set 
25090      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
25091      */
25092     setValue : function(v,suppressEvent){
25093         
25094         
25095         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
25096         //if(this.el && this.el.dom){
25097         //    this.el.dom.checked = this.checked;
25098         //    this.el.dom.defaultChecked = this.checked;
25099         //}
25100         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
25101         //this.fireEvent("check", this, this.checked);
25102     },
25103     // private..
25104     setChecked : function(state,suppressEvent)
25105     {
25106         if (this.inSetChecked) {
25107             this.checked = state;
25108             return;
25109         }
25110         
25111     
25112         if(this.wrap){
25113             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
25114         }
25115         this.checked = state;
25116         if(suppressEvent !== true){
25117             this.fireEvent('check', this, state);
25118         }
25119         this.inSetChecked = true;
25120         this.el.dom.value = state ? this.inputValue : this.valueOff;
25121         this.inSetChecked = false;
25122         
25123     },
25124     // handle setting of hidden value by some other method!!?!?
25125     setFromHidden: function()
25126     {
25127         if(!this.el){
25128             return;
25129         }
25130         //console.log("SET FROM HIDDEN");
25131         //alert('setFrom hidden');
25132         this.setValue(this.el.dom.value);
25133     },
25134     
25135     onDestroy : function()
25136     {
25137         if(this.viewEl){
25138             Roo.get(this.viewEl).remove();
25139         }
25140          
25141         Roo.form.Checkbox.superclass.onDestroy.call(this);
25142     }
25143
25144 });/*
25145  * Based on:
25146  * Ext JS Library 1.1.1
25147  * Copyright(c) 2006-2007, Ext JS, LLC.
25148  *
25149  * Originally Released Under LGPL - original licence link has changed is not relivant.
25150  *
25151  * Fork - LGPL
25152  * <script type="text/javascript">
25153  */
25154  
25155 /**
25156  * @class Roo.form.Radio
25157  * @extends Roo.form.Checkbox
25158  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25159  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25160  * @constructor
25161  * Creates a new Radio
25162  * @param {Object} config Configuration options
25163  */
25164 Roo.form.Radio = function(){
25165     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25166 };
25167 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25168     inputType: 'radio',
25169
25170     /**
25171      * If this radio is part of a group, it will return the selected value
25172      * @return {String}
25173      */
25174     getGroupValue : function(){
25175         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25176     },
25177     
25178     
25179     onRender : function(ct, position){
25180         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
25181         
25182         if(this.inputValue !== undefined){
25183             this.el.dom.value = this.inputValue;
25184         }
25185          
25186         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
25187         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
25188         //var viewEl = this.wrap.createChild({ 
25189         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
25190         //this.viewEl = viewEl;   
25191         //this.wrap.on('click', this.onClick,  this); 
25192         
25193         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
25194         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
25195         
25196         
25197         
25198         if(this.boxLabel){
25199             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
25200         //    viewEl.on('click', this.onClick,  this); 
25201         }
25202          if(this.checked){
25203             this.el.dom.checked =   'checked' ;
25204         }
25205          
25206     } 
25207     
25208     
25209 });//<script type="text/javascript">
25210
25211 /*
25212  * Ext JS Library 1.1.1
25213  * Copyright(c) 2006-2007, Ext JS, LLC.
25214  * licensing@extjs.com
25215  * 
25216  * http://www.extjs.com/license
25217  */
25218  
25219  /*
25220   * 
25221   * Known bugs:
25222   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25223   * - IE ? - no idea how much works there.
25224   * 
25225   * 
25226   * 
25227   */
25228  
25229
25230 /**
25231  * @class Ext.form.HtmlEditor
25232  * @extends Ext.form.Field
25233  * Provides a lightweight HTML Editor component.
25234  *
25235  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25236  * 
25237  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25238  * supported by this editor.</b><br/><br/>
25239  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25240  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25241  */
25242 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25243       /**
25244      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25245      */
25246     toolbars : false,
25247     /**
25248      * @cfg {String} createLinkText The default text for the create link prompt
25249      */
25250     createLinkText : 'Please enter the URL for the link:',
25251     /**
25252      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25253      */
25254     defaultLinkValue : 'http:/'+'/',
25255    
25256      /**
25257      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25258      *                        Roo.resizable.
25259      */
25260     resizable : false,
25261      /**
25262      * @cfg {Number} height (in pixels)
25263      */   
25264     height: 300,
25265    /**
25266      * @cfg {Number} width (in pixels)
25267      */   
25268     width: 500,
25269     
25270     /**
25271      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25272      * 
25273      */
25274     stylesheets: false,
25275     
25276     // id of frame..
25277     frameId: false,
25278     
25279     // private properties
25280     validationEvent : false,
25281     deferHeight: true,
25282     initialized : false,
25283     activated : false,
25284     sourceEditMode : false,
25285     onFocus : Roo.emptyFn,
25286     iframePad:3,
25287     hideMode:'offsets',
25288     
25289     defaultAutoCreate : { // modified by initCompnoent..
25290         tag: "textarea",
25291         style:"width:500px;height:300px;",
25292         autocomplete: "off"
25293     },
25294
25295     // private
25296     initComponent : function(){
25297         this.addEvents({
25298             /**
25299              * @event initialize
25300              * Fires when the editor is fully initialized (including the iframe)
25301              * @param {HtmlEditor} this
25302              */
25303             initialize: true,
25304             /**
25305              * @event activate
25306              * Fires when the editor is first receives the focus. Any insertion must wait
25307              * until after this event.
25308              * @param {HtmlEditor} this
25309              */
25310             activate: true,
25311              /**
25312              * @event beforesync
25313              * Fires before the textarea is updated with content from the editor iframe. Return false
25314              * to cancel the sync.
25315              * @param {HtmlEditor} this
25316              * @param {String} html
25317              */
25318             beforesync: true,
25319              /**
25320              * @event beforepush
25321              * Fires before the iframe editor is updated with content from the textarea. Return false
25322              * to cancel the push.
25323              * @param {HtmlEditor} this
25324              * @param {String} html
25325              */
25326             beforepush: true,
25327              /**
25328              * @event sync
25329              * Fires when the textarea is updated with content from the editor iframe.
25330              * @param {HtmlEditor} this
25331              * @param {String} html
25332              */
25333             sync: true,
25334              /**
25335              * @event push
25336              * Fires when the iframe editor is updated with content from the textarea.
25337              * @param {HtmlEditor} this
25338              * @param {String} html
25339              */
25340             push: true,
25341              /**
25342              * @event editmodechange
25343              * Fires when the editor switches edit modes
25344              * @param {HtmlEditor} this
25345              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25346              */
25347             editmodechange: true,
25348             /**
25349              * @event editorevent
25350              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25351              * @param {HtmlEditor} this
25352              */
25353             editorevent: true
25354         });
25355         this.defaultAutoCreate =  {
25356             tag: "textarea",
25357             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25358             autocomplete: "off"
25359         };
25360     },
25361
25362     /**
25363      * Protected method that will not generally be called directly. It
25364      * is called when the editor creates its toolbar. Override this method if you need to
25365      * add custom toolbar buttons.
25366      * @param {HtmlEditor} editor
25367      */
25368     createToolbar : function(editor){
25369         if (!editor.toolbars || !editor.toolbars.length) {
25370             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25371         }
25372         
25373         for (var i =0 ; i < editor.toolbars.length;i++) {
25374             editor.toolbars[i] = Roo.factory(
25375                     typeof(editor.toolbars[i]) == 'string' ?
25376                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
25377                 Roo.form.HtmlEditor);
25378             editor.toolbars[i].init(editor);
25379         }
25380          
25381         
25382     },
25383
25384     /**
25385      * Protected method that will not generally be called directly. It
25386      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25387      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25388      */
25389     getDocMarkup : function(){
25390         // body styles..
25391         var st = '';
25392         if (this.stylesheets === false) {
25393             
25394             Roo.get(document.head).select('style').each(function(node) {
25395                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25396             });
25397             
25398             Roo.get(document.head).select('link').each(function(node) { 
25399                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25400             });
25401             
25402         } else if (!this.stylesheets.length) {
25403                 // simple..
25404                 st = '<style type="text/css">' +
25405                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25406                    '</style>';
25407         } else {
25408             Roo.each(this.stylesheets, function(s) {
25409                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25410             });
25411             
25412         }
25413         
25414         st +=  '<style type="text/css">' +
25415             'IMG { cursor: pointer } ' +
25416         '</style>';
25417
25418         
25419         return '<html><head>' + st  +
25420             //<style type="text/css">' +
25421             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25422             //'</style>' +
25423             ' </head><body class="roo-htmleditor-body"></body></html>';
25424     },
25425
25426     // private
25427     onRender : function(ct, position)
25428     {
25429         var _t = this;
25430         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25431         this.el.dom.style.border = '0 none';
25432         this.el.dom.setAttribute('tabIndex', -1);
25433         this.el.addClass('x-hidden');
25434         if(Roo.isIE){ // fix IE 1px bogus margin
25435             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25436         }
25437         this.wrap = this.el.wrap({
25438             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25439         });
25440         
25441         if (this.resizable) {
25442             this.resizeEl = new Roo.Resizable(this.wrap, {
25443                 pinned : true,
25444                 wrap: true,
25445                 dynamic : true,
25446                 minHeight : this.height,
25447                 height: this.height,
25448                 handles : this.resizable,
25449                 width: this.width,
25450                 listeners : {
25451                     resize : function(r, w, h) {
25452                         _t.onResize(w,h); // -something
25453                     }
25454                 }
25455             });
25456             
25457         }
25458
25459         this.frameId = Roo.id();
25460         
25461         this.createToolbar(this);
25462         
25463       
25464         
25465         var iframe = this.wrap.createChild({
25466             tag: 'iframe',
25467             id: this.frameId,
25468             name: this.frameId,
25469             frameBorder : 'no',
25470             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25471         }, this.el
25472         );
25473         
25474        // console.log(iframe);
25475         //this.wrap.dom.appendChild(iframe);
25476
25477         this.iframe = iframe.dom;
25478
25479          this.assignDocWin();
25480         
25481         this.doc.designMode = 'on';
25482        
25483         this.doc.open();
25484         this.doc.write(this.getDocMarkup());
25485         this.doc.close();
25486
25487         
25488         var task = { // must defer to wait for browser to be ready
25489             run : function(){
25490                 //console.log("run task?" + this.doc.readyState);
25491                 this.assignDocWin();
25492                 if(this.doc.body || this.doc.readyState == 'complete'){
25493                     try {
25494                         this.doc.designMode="on";
25495                     } catch (e) {
25496                         return;
25497                     }
25498                     Roo.TaskMgr.stop(task);
25499                     this.initEditor.defer(10, this);
25500                 }
25501             },
25502             interval : 10,
25503             duration:10000,
25504             scope: this
25505         };
25506         Roo.TaskMgr.start(task);
25507
25508         if(!this.width){
25509             this.setSize(this.wrap.getSize());
25510         }
25511         if (this.resizeEl) {
25512             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25513             // should trigger onReize..
25514         }
25515     },
25516
25517     // private
25518     onResize : function(w, h)
25519     {
25520         //Roo.log('resize: ' +w + ',' + h );
25521         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25522         if(this.el && this.iframe){
25523             if(typeof w == 'number'){
25524                 var aw = w - this.wrap.getFrameWidth('lr');
25525                 this.el.setWidth(this.adjustWidth('textarea', aw));
25526                 this.iframe.style.width = aw + 'px';
25527             }
25528             if(typeof h == 'number'){
25529                 var tbh = 0;
25530                 for (var i =0; i < this.toolbars.length;i++) {
25531                     // fixme - ask toolbars for heights?
25532                     tbh += this.toolbars[i].tb.el.getHeight();
25533                     if (this.toolbars[i].footer) {
25534                         tbh += this.toolbars[i].footer.el.getHeight();
25535                     }
25536                 }
25537                 
25538                 
25539                 
25540                 
25541                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25542                 ah -= 5; // knock a few pixes off for look..
25543                 this.el.setHeight(this.adjustWidth('textarea', ah));
25544                 this.iframe.style.height = ah + 'px';
25545                 if(this.doc){
25546                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25547                 }
25548             }
25549         }
25550     },
25551
25552     /**
25553      * Toggles the editor between standard and source edit mode.
25554      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25555      */
25556     toggleSourceEdit : function(sourceEditMode){
25557         
25558         this.sourceEditMode = sourceEditMode === true;
25559         
25560         if(this.sourceEditMode){
25561 //            Roo.log('in');
25562 //            Roo.log(this.syncValue());
25563             this.syncValue();
25564             this.iframe.className = 'x-hidden';
25565             this.el.removeClass('x-hidden');
25566             this.el.dom.removeAttribute('tabIndex');
25567             this.el.focus();
25568         }else{
25569 //            Roo.log('out')
25570 //            Roo.log(this.pushValue()); 
25571             this.pushValue();
25572             this.iframe.className = '';
25573             this.el.addClass('x-hidden');
25574             this.el.dom.setAttribute('tabIndex', -1);
25575             this.deferFocus();
25576         }
25577         this.setSize(this.wrap.getSize());
25578         this.fireEvent('editmodechange', this, this.sourceEditMode);
25579     },
25580
25581     // private used internally
25582     createLink : function(){
25583         var url = prompt(this.createLinkText, this.defaultLinkValue);
25584         if(url && url != 'http:/'+'/'){
25585             this.relayCmd('createlink', url);
25586         }
25587     },
25588
25589     // private (for BoxComponent)
25590     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25591
25592     // private (for BoxComponent)
25593     getResizeEl : function(){
25594         return this.wrap;
25595     },
25596
25597     // private (for BoxComponent)
25598     getPositionEl : function(){
25599         return this.wrap;
25600     },
25601
25602     // private
25603     initEvents : function(){
25604         this.originalValue = this.getValue();
25605     },
25606
25607     /**
25608      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25609      * @method
25610      */
25611     markInvalid : Roo.emptyFn,
25612     /**
25613      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25614      * @method
25615      */
25616     clearInvalid : Roo.emptyFn,
25617
25618     setValue : function(v){
25619         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25620         this.pushValue();
25621     },
25622
25623     /**
25624      * Protected method that will not generally be called directly. If you need/want
25625      * custom HTML cleanup, this is the method you should override.
25626      * @param {String} html The HTML to be cleaned
25627      * return {String} The cleaned HTML
25628      */
25629     cleanHtml : function(html){
25630         html = String(html);
25631         if(html.length > 5){
25632             if(Roo.isSafari){ // strip safari nonsense
25633                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25634             }
25635         }
25636         if(html == '&nbsp;'){
25637             html = '';
25638         }
25639         return html;
25640     },
25641
25642     /**
25643      * Protected method that will not generally be called directly. Syncs the contents
25644      * of the editor iframe with the textarea.
25645      */
25646     syncValue : function(){
25647         if(this.initialized){
25648             var bd = (this.doc.body || this.doc.documentElement);
25649             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25650             var html = bd.innerHTML;
25651             if(Roo.isSafari){
25652                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25653                 var m = bs.match(/text-align:(.*?);/i);
25654                 if(m && m[1]){
25655                     html = '<div style="'+m[0]+'">' + html + '</div>';
25656                 }
25657             }
25658             html = this.cleanHtml(html);
25659             // fix up the special chars.. normaly like back quotes in word...
25660             // however we do not want to do this with chinese..
25661             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25662                 var cc = b.charCodeAt();
25663                 if (
25664                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25665                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25666                     (cc >= 0xf900 && cc < 0xfb00 )
25667                 ) {
25668                         return b;
25669                 }
25670                 return "&#"+cc+";" 
25671             });
25672             if(this.fireEvent('beforesync', this, html) !== false){
25673                 this.el.dom.value = html;
25674                 this.fireEvent('sync', this, html);
25675             }
25676         }
25677     },
25678
25679     /**
25680      * Protected method that will not generally be called directly. Pushes the value of the textarea
25681      * into the iframe editor.
25682      */
25683     pushValue : function(){
25684         if(this.initialized){
25685             var v = this.el.dom.value;
25686             
25687             if(v.length < 1){
25688                 v = '&#160;';
25689             }
25690             
25691             if(this.fireEvent('beforepush', this, v) !== false){
25692                 var d = (this.doc.body || this.doc.documentElement);
25693                 d.innerHTML = v;
25694                 this.cleanUpPaste();
25695                 this.el.dom.value = d.innerHTML;
25696                 this.fireEvent('push', this, v);
25697             }
25698         }
25699     },
25700
25701     // private
25702     deferFocus : function(){
25703         this.focus.defer(10, this);
25704     },
25705
25706     // doc'ed in Field
25707     focus : function(){
25708         if(this.win && !this.sourceEditMode){
25709             this.win.focus();
25710         }else{
25711             this.el.focus();
25712         }
25713     },
25714     
25715     assignDocWin: function()
25716     {
25717         var iframe = this.iframe;
25718         
25719          if(Roo.isIE){
25720             this.doc = iframe.contentWindow.document;
25721             this.win = iframe.contentWindow;
25722         } else {
25723             if (!Roo.get(this.frameId)) {
25724                 return;
25725             }
25726             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25727             this.win = Roo.get(this.frameId).dom.contentWindow;
25728         }
25729     },
25730     
25731     // private
25732     initEditor : function(){
25733         //console.log("INIT EDITOR");
25734         this.assignDocWin();
25735         
25736         
25737         
25738         this.doc.designMode="on";
25739         this.doc.open();
25740         this.doc.write(this.getDocMarkup());
25741         this.doc.close();
25742         
25743         var dbody = (this.doc.body || this.doc.documentElement);
25744         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25745         // this copies styles from the containing element into thsi one..
25746         // not sure why we need all of this..
25747         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25748         ss['background-attachment'] = 'fixed'; // w3c
25749         dbody.bgProperties = 'fixed'; // ie
25750         Roo.DomHelper.applyStyles(dbody, ss);
25751         Roo.EventManager.on(this.doc, {
25752             //'mousedown': this.onEditorEvent,
25753             'mouseup': this.onEditorEvent,
25754             'dblclick': this.onEditorEvent,
25755             'click': this.onEditorEvent,
25756             'keyup': this.onEditorEvent,
25757             buffer:100,
25758             scope: this
25759         });
25760         if(Roo.isGecko){
25761             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25762         }
25763         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25764             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25765         }
25766         this.initialized = true;
25767
25768         this.fireEvent('initialize', this);
25769         this.pushValue();
25770     },
25771
25772     // private
25773     onDestroy : function(){
25774         
25775         
25776         
25777         if(this.rendered){
25778             
25779             for (var i =0; i < this.toolbars.length;i++) {
25780                 // fixme - ask toolbars for heights?
25781                 this.toolbars[i].onDestroy();
25782             }
25783             
25784             this.wrap.dom.innerHTML = '';
25785             this.wrap.remove();
25786         }
25787     },
25788
25789     // private
25790     onFirstFocus : function(){
25791         
25792         this.assignDocWin();
25793         
25794         
25795         this.activated = true;
25796         for (var i =0; i < this.toolbars.length;i++) {
25797             this.toolbars[i].onFirstFocus();
25798         }
25799        
25800         if(Roo.isGecko){ // prevent silly gecko errors
25801             this.win.focus();
25802             var s = this.win.getSelection();
25803             if(!s.focusNode || s.focusNode.nodeType != 3){
25804                 var r = s.getRangeAt(0);
25805                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25806                 r.collapse(true);
25807                 this.deferFocus();
25808             }
25809             try{
25810                 this.execCmd('useCSS', true);
25811                 this.execCmd('styleWithCSS', false);
25812             }catch(e){}
25813         }
25814         this.fireEvent('activate', this);
25815     },
25816
25817     // private
25818     adjustFont: function(btn){
25819         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25820         //if(Roo.isSafari){ // safari
25821         //    adjust *= 2;
25822        // }
25823         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25824         if(Roo.isSafari){ // safari
25825             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25826             v =  (v < 10) ? 10 : v;
25827             v =  (v > 48) ? 48 : v;
25828             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25829             
25830         }
25831         
25832         
25833         v = Math.max(1, v+adjust);
25834         
25835         this.execCmd('FontSize', v  );
25836     },
25837
25838     onEditorEvent : function(e){
25839         this.fireEvent('editorevent', this, e);
25840       //  this.updateToolbar();
25841         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25842     },
25843
25844     insertTag : function(tg)
25845     {
25846         // could be a bit smarter... -> wrap the current selected tRoo..
25847         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
25848             
25849             range = this.createRange(this.getSelection());
25850             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25851             wrappingNode.appendChild(range.extractContents());
25852             range.insertNode(wrappingNode);
25853
25854             return;
25855             
25856             
25857             
25858         }
25859         this.execCmd("formatblock",   tg);
25860         
25861     },
25862     
25863     insertText : function(txt)
25864     {
25865         
25866         
25867         var range = this.createRange();
25868         range.deleteContents();
25869                //alert(Sender.getAttribute('label'));
25870                
25871         range.insertNode(this.doc.createTextNode(txt));
25872     } ,
25873     
25874     // private
25875     relayBtnCmd : function(btn){
25876         this.relayCmd(btn.cmd);
25877     },
25878
25879     /**
25880      * Executes a Midas editor command on the editor document and performs necessary focus and
25881      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25882      * @param {String} cmd The Midas command
25883      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25884      */
25885     relayCmd : function(cmd, value){
25886         this.win.focus();
25887         this.execCmd(cmd, value);
25888         this.fireEvent('editorevent', this);
25889         //this.updateToolbar();
25890         this.deferFocus();
25891     },
25892
25893     /**
25894      * Executes a Midas editor command directly on the editor document.
25895      * For visual commands, you should use {@link #relayCmd} instead.
25896      * <b>This should only be called after the editor is initialized.</b>
25897      * @param {String} cmd The Midas command
25898      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25899      */
25900     execCmd : function(cmd, value){
25901         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25902         this.syncValue();
25903     },
25904  
25905  
25906    
25907     /**
25908      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25909      * to insert tRoo.
25910      * @param {String} text | dom node.. 
25911      */
25912     insertAtCursor : function(text)
25913     {
25914         
25915         
25916         
25917         if(!this.activated){
25918             return;
25919         }
25920         /*
25921         if(Roo.isIE){
25922             this.win.focus();
25923             var r = this.doc.selection.createRange();
25924             if(r){
25925                 r.collapse(true);
25926                 r.pasteHTML(text);
25927                 this.syncValue();
25928                 this.deferFocus();
25929             
25930             }
25931             return;
25932         }
25933         */
25934         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25935             this.win.focus();
25936             
25937             
25938             // from jquery ui (MIT licenced)
25939             var range, node;
25940             var win = this.win;
25941             
25942             if (win.getSelection && win.getSelection().getRangeAt) {
25943                 range = win.getSelection().getRangeAt(0);
25944                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25945                 range.insertNode(node);
25946             } else if (win.document.selection && win.document.selection.createRange) {
25947                 // no firefox support
25948                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25949                 win.document.selection.createRange().pasteHTML(txt);
25950             } else {
25951                 // no firefox support
25952                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25953                 this.execCmd('InsertHTML', txt);
25954             } 
25955             
25956             this.syncValue();
25957             
25958             this.deferFocus();
25959         }
25960     },
25961  // private
25962     mozKeyPress : function(e){
25963         if(e.ctrlKey){
25964             var c = e.getCharCode(), cmd;
25965           
25966             if(c > 0){
25967                 c = String.fromCharCode(c).toLowerCase();
25968                 switch(c){
25969                     case 'b':
25970                         cmd = 'bold';
25971                         break;
25972                     case 'i':
25973                         cmd = 'italic';
25974                         break;
25975                     
25976                     case 'u':
25977                         cmd = 'underline';
25978                         break;
25979                     
25980                     case 'v':
25981                         this.cleanUpPaste.defer(100, this);
25982                         return;
25983                         
25984                 }
25985                 if(cmd){
25986                     this.win.focus();
25987                     this.execCmd(cmd);
25988                     this.deferFocus();
25989                     e.preventDefault();
25990                 }
25991                 
25992             }
25993         }
25994     },
25995
25996     // private
25997     fixKeys : function(){ // load time branching for fastest keydown performance
25998         if(Roo.isIE){
25999             return function(e){
26000                 var k = e.getKey(), r;
26001                 if(k == e.TAB){
26002                     e.stopEvent();
26003                     r = this.doc.selection.createRange();
26004                     if(r){
26005                         r.collapse(true);
26006                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26007                         this.deferFocus();
26008                     }
26009                     return;
26010                 }
26011                 
26012                 if(k == e.ENTER){
26013                     r = this.doc.selection.createRange();
26014                     if(r){
26015                         var target = r.parentElement();
26016                         if(!target || target.tagName.toLowerCase() != 'li'){
26017                             e.stopEvent();
26018                             r.pasteHTML('<br />');
26019                             r.collapse(false);
26020                             r.select();
26021                         }
26022                     }
26023                 }
26024                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26025                     this.cleanUpPaste.defer(100, this);
26026                     return;
26027                 }
26028                 
26029                 
26030             };
26031         }else if(Roo.isOpera){
26032             return function(e){
26033                 var k = e.getKey();
26034                 if(k == e.TAB){
26035                     e.stopEvent();
26036                     this.win.focus();
26037                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26038                     this.deferFocus();
26039                 }
26040                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26041                     this.cleanUpPaste.defer(100, this);
26042                     return;
26043                 }
26044                 
26045             };
26046         }else if(Roo.isSafari){
26047             return function(e){
26048                 var k = e.getKey();
26049                 
26050                 if(k == e.TAB){
26051                     e.stopEvent();
26052                     this.execCmd('InsertText','\t');
26053                     this.deferFocus();
26054                     return;
26055                 }
26056                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26057                     this.cleanUpPaste.defer(100, this);
26058                     return;
26059                 }
26060                 
26061              };
26062         }
26063     }(),
26064     
26065     getAllAncestors: function()
26066     {
26067         var p = this.getSelectedNode();
26068         var a = [];
26069         if (!p) {
26070             a.push(p); // push blank onto stack..
26071             p = this.getParentElement();
26072         }
26073         
26074         
26075         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26076             a.push(p);
26077             p = p.parentNode;
26078         }
26079         a.push(this.doc.body);
26080         return a;
26081     },
26082     lastSel : false,
26083     lastSelNode : false,
26084     
26085     
26086     getSelection : function() 
26087     {
26088         this.assignDocWin();
26089         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26090     },
26091     
26092     getSelectedNode: function() 
26093     {
26094         // this may only work on Gecko!!!
26095         
26096         // should we cache this!!!!
26097         
26098         
26099         
26100          
26101         var range = this.createRange(this.getSelection()).cloneRange();
26102         
26103         if (Roo.isIE) {
26104             var parent = range.parentElement();
26105             while (true) {
26106                 var testRange = range.duplicate();
26107                 testRange.moveToElementText(parent);
26108                 if (testRange.inRange(range)) {
26109                     break;
26110                 }
26111                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26112                     break;
26113                 }
26114                 parent = parent.parentElement;
26115             }
26116             return parent;
26117         }
26118         
26119         // is ancestor a text element.
26120         var ac =  range.commonAncestorContainer;
26121         if (ac.nodeType == 3) {
26122             ac = ac.parentNode;
26123         }
26124         
26125         var ar = ac.childNodes;
26126          
26127         var nodes = [];
26128         var other_nodes = [];
26129         var has_other_nodes = false;
26130         for (var i=0;i<ar.length;i++) {
26131             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26132                 continue;
26133             }
26134             // fullly contained node.
26135             
26136             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26137                 nodes.push(ar[i]);
26138                 continue;
26139             }
26140             
26141             // probably selected..
26142             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26143                 other_nodes.push(ar[i]);
26144                 continue;
26145             }
26146             // outer..
26147             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26148                 continue;
26149             }
26150             
26151             
26152             has_other_nodes = true;
26153         }
26154         if (!nodes.length && other_nodes.length) {
26155             nodes= other_nodes;
26156         }
26157         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26158             return false;
26159         }
26160         
26161         return nodes[0];
26162     },
26163     createRange: function(sel)
26164     {
26165         // this has strange effects when using with 
26166         // top toolbar - not sure if it's a great idea.
26167         //this.editor.contentWindow.focus();
26168         if (typeof sel != "undefined") {
26169             try {
26170                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26171             } catch(e) {
26172                 return this.doc.createRange();
26173             }
26174         } else {
26175             return this.doc.createRange();
26176         }
26177     },
26178     getParentElement: function()
26179     {
26180         
26181         this.assignDocWin();
26182         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26183         
26184         var range = this.createRange(sel);
26185          
26186         try {
26187             var p = range.commonAncestorContainer;
26188             while (p.nodeType == 3) { // text node
26189                 p = p.parentNode;
26190             }
26191             return p;
26192         } catch (e) {
26193             return null;
26194         }
26195     
26196     },
26197     /***
26198      *
26199      * Range intersection.. the hard stuff...
26200      *  '-1' = before
26201      *  '0' = hits..
26202      *  '1' = after.
26203      *         [ -- selected range --- ]
26204      *   [fail]                        [fail]
26205      *
26206      *    basically..
26207      *      if end is before start or  hits it. fail.
26208      *      if start is after end or hits it fail.
26209      *
26210      *   if either hits (but other is outside. - then it's not 
26211      *   
26212      *    
26213      **/
26214     
26215     
26216     // @see http://www.thismuchiknow.co.uk/?p=64.
26217     rangeIntersectsNode : function(range, node)
26218     {
26219         var nodeRange = node.ownerDocument.createRange();
26220         try {
26221             nodeRange.selectNode(node);
26222         } catch (e) {
26223             nodeRange.selectNodeContents(node);
26224         }
26225     
26226         var rangeStartRange = range.cloneRange();
26227         rangeStartRange.collapse(true);
26228     
26229         var rangeEndRange = range.cloneRange();
26230         rangeEndRange.collapse(false);
26231     
26232         var nodeStartRange = nodeRange.cloneRange();
26233         nodeStartRange.collapse(true);
26234     
26235         var nodeEndRange = nodeRange.cloneRange();
26236         nodeEndRange.collapse(false);
26237     
26238         return rangeStartRange.compareBoundaryPoints(
26239                  Range.START_TO_START, nodeEndRange) == -1 &&
26240                rangeEndRange.compareBoundaryPoints(
26241                  Range.START_TO_START, nodeStartRange) == 1;
26242         
26243          
26244     },
26245     rangeCompareNode : function(range, node)
26246     {
26247         var nodeRange = node.ownerDocument.createRange();
26248         try {
26249             nodeRange.selectNode(node);
26250         } catch (e) {
26251             nodeRange.selectNodeContents(node);
26252         }
26253         
26254         
26255         range.collapse(true);
26256     
26257         nodeRange.collapse(true);
26258      
26259         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26260         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26261          
26262         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26263         
26264         var nodeIsBefore   =  ss == 1;
26265         var nodeIsAfter    = ee == -1;
26266         
26267         if (nodeIsBefore && nodeIsAfter)
26268             return 0; // outer
26269         if (!nodeIsBefore && nodeIsAfter)
26270             return 1; //right trailed.
26271         
26272         if (nodeIsBefore && !nodeIsAfter)
26273             return 2;  // left trailed.
26274         // fully contined.
26275         return 3;
26276     },
26277
26278     // private? - in a new class?
26279     cleanUpPaste :  function()
26280     {
26281         // cleans up the whole document..
26282          Roo.log('cleanuppaste');
26283         this.cleanUpChildren(this.doc.body);
26284         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26285         if (clean != this.doc.body.innerHTML) {
26286             this.doc.body.innerHTML = clean;
26287         }
26288         
26289     },
26290     
26291     cleanWordChars : function(input) {// change the chars to hex code
26292         var he = Roo.form.HtmlEditor;
26293         
26294         var output = input;
26295         Roo.each(he.swapCodes, function(sw) { 
26296             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26297             
26298             output = output.replace(swapper, sw[1]);
26299         });
26300         
26301         return output;
26302     },
26303     
26304     
26305     cleanUpChildren : function (n)
26306     {
26307         if (!n.childNodes.length) {
26308             return;
26309         }
26310         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26311            this.cleanUpChild(n.childNodes[i]);
26312         }
26313     },
26314     
26315     
26316         
26317     
26318     cleanUpChild : function (node)
26319     {
26320         var ed = this;
26321         //console.log(node);
26322         if (node.nodeName == "#text") {
26323             // clean up silly Windows -- stuff?
26324             return; 
26325         }
26326         if (node.nodeName == "#comment") {
26327             node.parentNode.removeChild(node);
26328             // clean up silly Windows -- stuff?
26329             return; 
26330         }
26331         
26332         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26333             // remove node.
26334             node.parentNode.removeChild(node);
26335             return;
26336             
26337         }
26338         
26339         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26340         
26341         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26342         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26343         
26344         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26345         //    remove_keep_children = true;
26346         //}
26347         
26348         if (remove_keep_children) {
26349             this.cleanUpChildren(node);
26350             // inserts everything just before this node...
26351             while (node.childNodes.length) {
26352                 var cn = node.childNodes[0];
26353                 node.removeChild(cn);
26354                 node.parentNode.insertBefore(cn, node);
26355             }
26356             node.parentNode.removeChild(node);
26357             return;
26358         }
26359         
26360         if (!node.attributes || !node.attributes.length) {
26361             this.cleanUpChildren(node);
26362             return;
26363         }
26364         
26365         function cleanAttr(n,v)
26366         {
26367             
26368             if (v.match(/^\./) || v.match(/^\//)) {
26369                 return;
26370             }
26371             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26372                 return;
26373             }
26374             if (v.match(/^#/)) {
26375                 return;
26376             }
26377 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26378             node.removeAttribute(n);
26379             
26380         }
26381         
26382         function cleanStyle(n,v)
26383         {
26384             if (v.match(/expression/)) { //XSS?? should we even bother..
26385                 node.removeAttribute(n);
26386                 return;
26387             }
26388             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.form.HtmlEditor.cwhite : ed.cwhite;
26389             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.form.HtmlEditor.cblack : ed.cblack;
26390             
26391             
26392             var parts = v.split(/;/);
26393             var clean = [];
26394             
26395             Roo.each(parts, function(p) {
26396                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26397                 if (!p.length) {
26398                     return true;
26399                 }
26400                 var l = p.split(':').shift().replace(/\s+/g,'');
26401                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26402                 
26403                 
26404                 if ( cblack.indexOf(l) > -1) {
26405 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26406                     //node.removeAttribute(n);
26407                     return true;
26408                 }
26409                 //Roo.log()
26410                 // only allow 'c whitelisted system attributes'
26411                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26412 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26413                     //node.removeAttribute(n);
26414                     return true;
26415                 }
26416                 
26417                 
26418                  
26419                 
26420                 clean.push(p);
26421                 return true;
26422             });
26423             if (clean.length) { 
26424                 node.setAttribute(n, clean.join(';'));
26425             } else {
26426                 node.removeAttribute(n);
26427             }
26428             
26429         }
26430         
26431         
26432         for (var i = node.attributes.length-1; i > -1 ; i--) {
26433             var a = node.attributes[i];
26434             //console.log(a);
26435             
26436             if (a.name.toLowerCase().substr(0,2)=='on')  {
26437                 node.removeAttribute(a.name);
26438                 continue;
26439             }
26440             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26441                 node.removeAttribute(a.name);
26442                 continue;
26443             }
26444             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26445                 cleanAttr(a.name,a.value); // fixme..
26446                 continue;
26447             }
26448             if (a.name == 'style') {
26449                 cleanStyle(a.name,a.value);
26450                 continue;
26451             }
26452             /// clean up MS crap..
26453             // tecnically this should be a list of valid class'es..
26454             
26455             
26456             if (a.name == 'class') {
26457                 if (a.value.match(/^Mso/)) {
26458                     node.className = '';
26459                 }
26460                 
26461                 if (a.value.match(/body/)) {
26462                     node.className = '';
26463                 }
26464                 continue;
26465             }
26466             
26467             // style cleanup!?
26468             // class cleanup?
26469             
26470         }
26471         
26472         
26473         this.cleanUpChildren(node);
26474         
26475         
26476     }
26477     
26478     
26479     // hide stuff that is not compatible
26480     /**
26481      * @event blur
26482      * @hide
26483      */
26484     /**
26485      * @event change
26486      * @hide
26487      */
26488     /**
26489      * @event focus
26490      * @hide
26491      */
26492     /**
26493      * @event specialkey
26494      * @hide
26495      */
26496     /**
26497      * @cfg {String} fieldClass @hide
26498      */
26499     /**
26500      * @cfg {String} focusClass @hide
26501      */
26502     /**
26503      * @cfg {String} autoCreate @hide
26504      */
26505     /**
26506      * @cfg {String} inputType @hide
26507      */
26508     /**
26509      * @cfg {String} invalidClass @hide
26510      */
26511     /**
26512      * @cfg {String} invalidText @hide
26513      */
26514     /**
26515      * @cfg {String} msgFx @hide
26516      */
26517     /**
26518      * @cfg {String} validateOnBlur @hide
26519      */
26520 });
26521
26522 Roo.form.HtmlEditor.white = [
26523         'area', 'br', 'img', 'input', 'hr', 'wbr',
26524         
26525        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26526        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26527        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26528        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26529        'table',   'ul',         'xmp', 
26530        
26531        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26532       'thead',   'tr', 
26533      
26534       'dir', 'menu', 'ol', 'ul', 'dl',
26535        
26536       'embed',  'object'
26537 ];
26538
26539
26540 Roo.form.HtmlEditor.black = [
26541     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26542         'applet', // 
26543         'base',   'basefont', 'bgsound', 'blink',  'body', 
26544         'frame',  'frameset', 'head',    'html',   'ilayer', 
26545         'iframe', 'layer',  'link',     'meta',    'object',   
26546         'script', 'style' ,'title',  'xml' // clean later..
26547 ];
26548 Roo.form.HtmlEditor.clean = [
26549     'script', 'style', 'title', 'xml'
26550 ];
26551 Roo.form.HtmlEditor.remove = [
26552     'font'
26553 ];
26554 // attributes..
26555
26556 Roo.form.HtmlEditor.ablack = [
26557     'on'
26558 ];
26559     
26560 Roo.form.HtmlEditor.aclean = [ 
26561     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26562 ];
26563
26564 // protocols..
26565 Roo.form.HtmlEditor.pwhite= [
26566         'http',  'https',  'mailto'
26567 ];
26568
26569 // white listed style attributes.
26570 Roo.form.HtmlEditor.cwhite= [
26571       //  'text-align', /// default is to allow most things..
26572       
26573          
26574 //        'font-size'//??
26575 ];
26576
26577 // black listed style attributes.
26578 Roo.form.HtmlEditor.cblack= [
26579       //  'font-size' -- this can be set by the project 
26580 ];
26581
26582
26583 Roo.form.HtmlEditor.swapCodes   =[ 
26584     [    8211, "--" ], 
26585     [    8212, "--" ], 
26586     [    8216,  "'" ],  
26587     [    8217, "'" ],  
26588     [    8220, '"' ],  
26589     [    8221, '"' ],  
26590     [    8226, "*" ],  
26591     [    8230, "..." ]
26592 ]; 
26593
26594     // <script type="text/javascript">
26595 /*
26596  * Based on
26597  * Ext JS Library 1.1.1
26598  * Copyright(c) 2006-2007, Ext JS, LLC.
26599  *  
26600  
26601  */
26602
26603 /**
26604  * @class Roo.form.HtmlEditorToolbar1
26605  * Basic Toolbar
26606  * 
26607  * Usage:
26608  *
26609  new Roo.form.HtmlEditor({
26610     ....
26611     toolbars : [
26612         new Roo.form.HtmlEditorToolbar1({
26613             disable : { fonts: 1 , format: 1, ..., ... , ...],
26614             btns : [ .... ]
26615         })
26616     }
26617      
26618  * 
26619  * @cfg {Object} disable List of elements to disable..
26620  * @cfg {Array} btns List of additional buttons.
26621  * 
26622  * 
26623  * NEEDS Extra CSS? 
26624  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26625  */
26626  
26627 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26628 {
26629     
26630     Roo.apply(this, config);
26631     
26632     // default disabled, based on 'good practice'..
26633     this.disable = this.disable || {};
26634     Roo.applyIf(this.disable, {
26635         fontSize : true,
26636         colors : true,
26637         specialElements : true
26638     });
26639     
26640     
26641     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26642     // dont call parent... till later.
26643 }
26644
26645 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26646     
26647     tb: false,
26648     
26649     rendered: false,
26650     
26651     editor : false,
26652     /**
26653      * @cfg {Object} disable  List of toolbar elements to disable
26654          
26655      */
26656     disable : false,
26657       /**
26658      * @cfg {Array} fontFamilies An array of available font families
26659      */
26660     fontFamilies : [
26661         'Arial',
26662         'Courier New',
26663         'Tahoma',
26664         'Times New Roman',
26665         'Verdana'
26666     ],
26667     
26668     specialChars : [
26669            "&#169;",
26670           "&#174;",     
26671           "&#8482;",    
26672           "&#163;" ,    
26673          // "&#8212;",    
26674           "&#8230;",    
26675           "&#247;" ,    
26676         //  "&#225;" ,     ?? a acute?
26677            "&#8364;"    , //Euro
26678        //   "&#8220;"    ,
26679         //  "&#8221;"    ,
26680         //  "&#8226;"    ,
26681           "&#176;"  //   , // degrees
26682
26683          // "&#233;"     , // e ecute
26684          // "&#250;"     , // u ecute?
26685     ],
26686     
26687     specialElements : [
26688         {
26689             text: "Insert Table",
26690             xtype: 'MenuItem',
26691             xns : Roo.Menu,
26692             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26693                 
26694         },
26695         {    
26696             text: "Insert Image",
26697             xtype: 'MenuItem',
26698             xns : Roo.Menu,
26699             ihtml : '<img src="about:blank"/>'
26700             
26701         }
26702         
26703          
26704     ],
26705     
26706     
26707     inputElements : [ 
26708             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26709             "input:submit", "input:button", "select", "textarea", "label" ],
26710     formats : [
26711         ["p"] ,  
26712         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26713         ["pre"],[ "code"], 
26714         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
26715         ['div'],['span']
26716     ],
26717      /**
26718      * @cfg {String} defaultFont default font to use.
26719      */
26720     defaultFont: 'tahoma',
26721    
26722     fontSelect : false,
26723     
26724     
26725     formatCombo : false,
26726     
26727     init : function(editor)
26728     {
26729         this.editor = editor;
26730         
26731         
26732         var fid = editor.frameId;
26733         var etb = this;
26734         function btn(id, toggle, handler){
26735             var xid = fid + '-'+ id ;
26736             return {
26737                 id : xid,
26738                 cmd : id,
26739                 cls : 'x-btn-icon x-edit-'+id,
26740                 enableToggle:toggle !== false,
26741                 scope: editor, // was editor...
26742                 handler:handler||editor.relayBtnCmd,
26743                 clickEvent:'mousedown',
26744                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26745                 tabIndex:-1
26746             };
26747         }
26748         
26749         
26750         
26751         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26752         this.tb = tb;
26753          // stop form submits
26754         tb.el.on('click', function(e){
26755             e.preventDefault(); // what does this do?
26756         });
26757
26758         if(!this.disable.font) { // && !Roo.isSafari){
26759             /* why no safari for fonts 
26760             editor.fontSelect = tb.el.createChild({
26761                 tag:'select',
26762                 tabIndex: -1,
26763                 cls:'x-font-select',
26764                 html: this.createFontOptions()
26765             });
26766             
26767             editor.fontSelect.on('change', function(){
26768                 var font = editor.fontSelect.dom.value;
26769                 editor.relayCmd('fontname', font);
26770                 editor.deferFocus();
26771             }, editor);
26772             
26773             tb.add(
26774                 editor.fontSelect.dom,
26775                 '-'
26776             );
26777             */
26778             
26779         };
26780         if(!this.disable.formats){
26781             this.formatCombo = new Roo.form.ComboBox({
26782                 store: new Roo.data.SimpleStore({
26783                     id : 'tag',
26784                     fields: ['tag'],
26785                     data : this.formats // from states.js
26786                 }),
26787                 blockFocus : true,
26788                 name : '',
26789                 //autoCreate : {tag: "div",  size: "20"},
26790                 displayField:'tag',
26791                 typeAhead: false,
26792                 mode: 'local',
26793                 editable : false,
26794                 triggerAction: 'all',
26795                 emptyText:'Add tag',
26796                 selectOnFocus:true,
26797                 width:135,
26798                 listeners : {
26799                     'select': function(c, r, i) {
26800                         editor.insertTag(r.get('tag'));
26801                         editor.focus();
26802                     }
26803                 }
26804
26805             });
26806             tb.addField(this.formatCombo);
26807             
26808         }
26809         
26810         if(!this.disable.format){
26811             tb.add(
26812                 btn('bold'),
26813                 btn('italic'),
26814                 btn('underline')
26815             );
26816         };
26817         if(!this.disable.fontSize){
26818             tb.add(
26819                 '-',
26820                 
26821                 
26822                 btn('increasefontsize', false, editor.adjustFont),
26823                 btn('decreasefontsize', false, editor.adjustFont)
26824             );
26825         };
26826         
26827         
26828         if(!this.disable.colors){
26829             tb.add(
26830                 '-', {
26831                     id:editor.frameId +'-forecolor',
26832                     cls:'x-btn-icon x-edit-forecolor',
26833                     clickEvent:'mousedown',
26834                     tooltip: this.buttonTips['forecolor'] || undefined,
26835                     tabIndex:-1,
26836                     menu : new Roo.menu.ColorMenu({
26837                         allowReselect: true,
26838                         focus: Roo.emptyFn,
26839                         value:'000000',
26840                         plain:true,
26841                         selectHandler: function(cp, color){
26842                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26843                             editor.deferFocus();
26844                         },
26845                         scope: editor,
26846                         clickEvent:'mousedown'
26847                     })
26848                 }, {
26849                     id:editor.frameId +'backcolor',
26850                     cls:'x-btn-icon x-edit-backcolor',
26851                     clickEvent:'mousedown',
26852                     tooltip: this.buttonTips['backcolor'] || undefined,
26853                     tabIndex:-1,
26854                     menu : new Roo.menu.ColorMenu({
26855                         focus: Roo.emptyFn,
26856                         value:'FFFFFF',
26857                         plain:true,
26858                         allowReselect: true,
26859                         selectHandler: function(cp, color){
26860                             if(Roo.isGecko){
26861                                 editor.execCmd('useCSS', false);
26862                                 editor.execCmd('hilitecolor', color);
26863                                 editor.execCmd('useCSS', true);
26864                                 editor.deferFocus();
26865                             }else{
26866                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26867                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26868                                 editor.deferFocus();
26869                             }
26870                         },
26871                         scope:editor,
26872                         clickEvent:'mousedown'
26873                     })
26874                 }
26875             );
26876         };
26877         // now add all the items...
26878         
26879
26880         if(!this.disable.alignments){
26881             tb.add(
26882                 '-',
26883                 btn('justifyleft'),
26884                 btn('justifycenter'),
26885                 btn('justifyright')
26886             );
26887         };
26888
26889         //if(!Roo.isSafari){
26890             if(!this.disable.links){
26891                 tb.add(
26892                     '-',
26893                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26894                 );
26895             };
26896
26897             if(!this.disable.lists){
26898                 tb.add(
26899                     '-',
26900                     btn('insertorderedlist'),
26901                     btn('insertunorderedlist')
26902                 );
26903             }
26904             if(!this.disable.sourceEdit){
26905                 tb.add(
26906                     '-',
26907                     btn('sourceedit', true, function(btn){
26908                         this.toggleSourceEdit(btn.pressed);
26909                     })
26910                 );
26911             }
26912         //}
26913         
26914         var smenu = { };
26915         // special menu.. - needs to be tidied up..
26916         if (!this.disable.special) {
26917             smenu = {
26918                 text: "&#169;",
26919                 cls: 'x-edit-none',
26920                 
26921                 menu : {
26922                     items : []
26923                 }
26924             };
26925             for (var i =0; i < this.specialChars.length; i++) {
26926                 smenu.menu.items.push({
26927                     
26928                     html: this.specialChars[i],
26929                     handler: function(a,b) {
26930                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26931                         //editor.insertAtCursor(a.html);
26932                         
26933                     },
26934                     tabIndex:-1
26935                 });
26936             }
26937             
26938             
26939             tb.add(smenu);
26940             
26941             
26942         }
26943          
26944         if (!this.disable.specialElements) {
26945             var semenu = {
26946                 text: "Other;",
26947                 cls: 'x-edit-none',
26948                 menu : {
26949                     items : []
26950                 }
26951             };
26952             for (var i =0; i < this.specialElements.length; i++) {
26953                 semenu.menu.items.push(
26954                     Roo.apply({ 
26955                         handler: function(a,b) {
26956                             editor.insertAtCursor(this.ihtml);
26957                         }
26958                     }, this.specialElements[i])
26959                 );
26960                     
26961             }
26962             
26963             tb.add(semenu);
26964             
26965             
26966         }
26967          
26968         
26969         if (this.btns) {
26970             for(var i =0; i< this.btns.length;i++) {
26971                 var b = Roo.factory(this.btns[i],Roo.form);
26972                 b.cls =  'x-edit-none';
26973                 b.scope = editor;
26974                 tb.add(b);
26975             }
26976         
26977         }
26978         
26979         
26980         
26981         // disable everything...
26982         
26983         this.tb.items.each(function(item){
26984            if(item.id != editor.frameId+ '-sourceedit'){
26985                 item.disable();
26986             }
26987         });
26988         this.rendered = true;
26989         
26990         // the all the btns;
26991         editor.on('editorevent', this.updateToolbar, this);
26992         // other toolbars need to implement this..
26993         //editor.on('editmodechange', this.updateToolbar, this);
26994     },
26995     
26996     
26997     
26998     /**
26999      * Protected method that will not generally be called directly. It triggers
27000      * a toolbar update by reading the markup state of the current selection in the editor.
27001      */
27002     updateToolbar: function(){
27003
27004         if(!this.editor.activated){
27005             this.editor.onFirstFocus();
27006             return;
27007         }
27008
27009         var btns = this.tb.items.map, 
27010             doc = this.editor.doc,
27011             frameId = this.editor.frameId;
27012
27013         if(!this.disable.font && !Roo.isSafari){
27014             /*
27015             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
27016             if(name != this.fontSelect.dom.value){
27017                 this.fontSelect.dom.value = name;
27018             }
27019             */
27020         }
27021         if(!this.disable.format){
27022             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
27023             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
27024             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
27025         }
27026         if(!this.disable.alignments){
27027             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
27028             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
27029             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
27030         }
27031         if(!Roo.isSafari && !this.disable.lists){
27032             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
27033             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
27034         }
27035         
27036         var ans = this.editor.getAllAncestors();
27037         if (this.formatCombo) {
27038             
27039             
27040             var store = this.formatCombo.store;
27041             this.formatCombo.setValue("");
27042             for (var i =0; i < ans.length;i++) {
27043                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27044                     // select it..
27045                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27046                     break;
27047                 }
27048             }
27049         }
27050         
27051         
27052         
27053         // hides menus... - so this cant be on a menu...
27054         Roo.menu.MenuMgr.hideAll();
27055
27056         //this.editorsyncValue();
27057     },
27058    
27059     
27060     createFontOptions : function(){
27061         var buf = [], fs = this.fontFamilies, ff, lc;
27062         
27063         
27064         
27065         for(var i = 0, len = fs.length; i< len; i++){
27066             ff = fs[i];
27067             lc = ff.toLowerCase();
27068             buf.push(
27069                 '<option value="',lc,'" style="font-family:',ff,';"',
27070                     (this.defaultFont == lc ? ' selected="true">' : '>'),
27071                     ff,
27072                 '</option>'
27073             );
27074         }
27075         return buf.join('');
27076     },
27077     
27078     toggleSourceEdit : function(sourceEditMode){
27079         if(sourceEditMode === undefined){
27080             sourceEditMode = !this.sourceEditMode;
27081         }
27082         this.sourceEditMode = sourceEditMode === true;
27083         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
27084         // just toggle the button?
27085         if(btn.pressed !== this.editor.sourceEditMode){
27086             btn.toggle(this.editor.sourceEditMode);
27087             return;
27088         }
27089         
27090         if(this.sourceEditMode){
27091             this.tb.items.each(function(item){
27092                 if(item.cmd != 'sourceedit'){
27093                     item.disable();
27094                 }
27095             });
27096           
27097         }else{
27098             if(this.initialized){
27099                 this.tb.items.each(function(item){
27100                     item.enable();
27101                 });
27102             }
27103             
27104         }
27105         // tell the editor that it's been pressed..
27106         this.editor.toggleSourceEdit(sourceEditMode);
27107        
27108     },
27109      /**
27110      * Object collection of toolbar tooltips for the buttons in the editor. The key
27111      * is the command id associated with that button and the value is a valid QuickTips object.
27112      * For example:
27113 <pre><code>
27114 {
27115     bold : {
27116         title: 'Bold (Ctrl+B)',
27117         text: 'Make the selected text bold.',
27118         cls: 'x-html-editor-tip'
27119     },
27120     italic : {
27121         title: 'Italic (Ctrl+I)',
27122         text: 'Make the selected text italic.',
27123         cls: 'x-html-editor-tip'
27124     },
27125     ...
27126 </code></pre>
27127     * @type Object
27128      */
27129     buttonTips : {
27130         bold : {
27131             title: 'Bold (Ctrl+B)',
27132             text: 'Make the selected text bold.',
27133             cls: 'x-html-editor-tip'
27134         },
27135         italic : {
27136             title: 'Italic (Ctrl+I)',
27137             text: 'Make the selected text italic.',
27138             cls: 'x-html-editor-tip'
27139         },
27140         underline : {
27141             title: 'Underline (Ctrl+U)',
27142             text: 'Underline the selected text.',
27143             cls: 'x-html-editor-tip'
27144         },
27145         increasefontsize : {
27146             title: 'Grow Text',
27147             text: 'Increase the font size.',
27148             cls: 'x-html-editor-tip'
27149         },
27150         decreasefontsize : {
27151             title: 'Shrink Text',
27152             text: 'Decrease the font size.',
27153             cls: 'x-html-editor-tip'
27154         },
27155         backcolor : {
27156             title: 'Text Highlight Color',
27157             text: 'Change the background color of the selected text.',
27158             cls: 'x-html-editor-tip'
27159         },
27160         forecolor : {
27161             title: 'Font Color',
27162             text: 'Change the color of the selected text.',
27163             cls: 'x-html-editor-tip'
27164         },
27165         justifyleft : {
27166             title: 'Align Text Left',
27167             text: 'Align text to the left.',
27168             cls: 'x-html-editor-tip'
27169         },
27170         justifycenter : {
27171             title: 'Center Text',
27172             text: 'Center text in the editor.',
27173             cls: 'x-html-editor-tip'
27174         },
27175         justifyright : {
27176             title: 'Align Text Right',
27177             text: 'Align text to the right.',
27178             cls: 'x-html-editor-tip'
27179         },
27180         insertunorderedlist : {
27181             title: 'Bullet List',
27182             text: 'Start a bulleted list.',
27183             cls: 'x-html-editor-tip'
27184         },
27185         insertorderedlist : {
27186             title: 'Numbered List',
27187             text: 'Start a numbered list.',
27188             cls: 'x-html-editor-tip'
27189         },
27190         createlink : {
27191             title: 'Hyperlink',
27192             text: 'Make the selected text a hyperlink.',
27193             cls: 'x-html-editor-tip'
27194         },
27195         sourceedit : {
27196             title: 'Source Edit',
27197             text: 'Switch to source editing mode.',
27198             cls: 'x-html-editor-tip'
27199         }
27200     },
27201     // private
27202     onDestroy : function(){
27203         if(this.rendered){
27204             
27205             this.tb.items.each(function(item){
27206                 if(item.menu){
27207                     item.menu.removeAll();
27208                     if(item.menu.el){
27209                         item.menu.el.destroy();
27210                     }
27211                 }
27212                 item.destroy();
27213             });
27214              
27215         }
27216     },
27217     onFirstFocus: function() {
27218         this.tb.items.each(function(item){
27219            item.enable();
27220         });
27221     }
27222 });
27223
27224
27225
27226
27227 // <script type="text/javascript">
27228 /*
27229  * Based on
27230  * Ext JS Library 1.1.1
27231  * Copyright(c) 2006-2007, Ext JS, LLC.
27232  *  
27233  
27234  */
27235
27236  
27237 /**
27238  * @class Roo.form.HtmlEditor.ToolbarContext
27239  * Context Toolbar
27240  * 
27241  * Usage:
27242  *
27243  new Roo.form.HtmlEditor({
27244     ....
27245     toolbars : [
27246         { xtype: 'ToolbarStandard', styles : {} }
27247         { xtype: 'ToolbarContext', disable : {} }
27248     ]
27249 })
27250
27251      
27252  * 
27253  * @config : {Object} disable List of elements to disable.. (not done yet.)
27254  * @config : {Object} styles  Map of styles available.
27255  * 
27256  */
27257
27258 Roo.form.HtmlEditor.ToolbarContext = function(config)
27259 {
27260     
27261     Roo.apply(this, config);
27262     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27263     // dont call parent... till later.
27264     this.styles = this.styles || {};
27265 }
27266
27267  
27268
27269 Roo.form.HtmlEditor.ToolbarContext.types = {
27270     'IMG' : {
27271         width : {
27272             title: "Width",
27273             width: 40
27274         },
27275         height:  {
27276             title: "Height",
27277             width: 40
27278         },
27279         align: {
27280             title: "Align",
27281             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27282             width : 80
27283             
27284         },
27285         border: {
27286             title: "Border",
27287             width: 40
27288         },
27289         alt: {
27290             title: "Alt",
27291             width: 120
27292         },
27293         src : {
27294             title: "Src",
27295             width: 220
27296         }
27297         
27298     },
27299     'A' : {
27300         name : {
27301             title: "Name",
27302             width: 50
27303         },
27304         href:  {
27305             title: "Href",
27306             width: 220
27307         } // border?
27308         
27309     },
27310     'TABLE' : {
27311         rows : {
27312             title: "Rows",
27313             width: 20
27314         },
27315         cols : {
27316             title: "Cols",
27317             width: 20
27318         },
27319         width : {
27320             title: "Width",
27321             width: 40
27322         },
27323         height : {
27324             title: "Height",
27325             width: 40
27326         },
27327         border : {
27328             title: "Border",
27329             width: 20
27330         }
27331     },
27332     'TD' : {
27333         width : {
27334             title: "Width",
27335             width: 40
27336         },
27337         height : {
27338             title: "Height",
27339             width: 40
27340         },   
27341         align: {
27342             title: "Align",
27343             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27344             width: 80
27345         },
27346         valign: {
27347             title: "Valign",
27348             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27349             width: 80
27350         },
27351         colspan: {
27352             title: "Colspan",
27353             width: 20
27354             
27355         },
27356          'font-family'  : {
27357             title : "Font",
27358             style : 'fontFamily',
27359             displayField: 'display',
27360             optname : 'font-family',
27361             width: 140
27362         }
27363     },
27364     'INPUT' : {
27365         name : {
27366             title: "name",
27367             width: 120
27368         },
27369         value : {
27370             title: "Value",
27371             width: 120
27372         },
27373         width : {
27374             title: "Width",
27375             width: 40
27376         }
27377     },
27378     'LABEL' : {
27379         'for' : {
27380             title: "For",
27381             width: 120
27382         }
27383     },
27384     'TEXTAREA' : {
27385           name : {
27386             title: "name",
27387             width: 120
27388         },
27389         rows : {
27390             title: "Rows",
27391             width: 20
27392         },
27393         cols : {
27394             title: "Cols",
27395             width: 20
27396         }
27397     },
27398     'SELECT' : {
27399         name : {
27400             title: "name",
27401             width: 120
27402         },
27403         selectoptions : {
27404             title: "Options",
27405             width: 200
27406         }
27407     },
27408     
27409     // should we really allow this??
27410     // should this just be 
27411     'BODY' : {
27412         title : {
27413             title: "Title",
27414             width: 200,
27415             disabled : true
27416         }
27417     },
27418     'SPAN' : {
27419         'font-family'  : {
27420             title : "Font",
27421             style : 'fontFamily',
27422             displayField: 'display',
27423             optname : 'font-family',
27424             width: 140
27425         }
27426     },
27427     'DIV' : {
27428         'font-family'  : {
27429             title : "Font",
27430             style : 'fontFamily',
27431             displayField: 'display',
27432             optname : 'font-family',
27433             width: 140
27434         }
27435     },
27436      'P' : {
27437         'font-family'  : {
27438             title : "Font",
27439             style : 'fontFamily',
27440             displayField: 'display',
27441             optname : 'font-family',
27442             width: 140
27443         }
27444     },
27445     
27446     '*' : {
27447         // empty..
27448     }
27449
27450 };
27451
27452 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
27453 Roo.form.HtmlEditor.ToolbarContext.stores = false;
27454
27455 Roo.form.HtmlEditor.ToolbarContext.options = {
27456         'font-family'  : [ 
27457                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
27458                 [ 'Courier New', 'Courier New'],
27459                 [ 'Tahoma', 'Tahoma'],
27460                 [ 'Times New Roman,serif', 'Times'],
27461                 [ 'Verdana','Verdana' ]
27462         ]
27463 };
27464
27465 // fixme - these need to be configurable..
27466  
27467
27468 Roo.form.HtmlEditor.ToolbarContext.types
27469
27470
27471 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27472     
27473     tb: false,
27474     
27475     rendered: false,
27476     
27477     editor : false,
27478     /**
27479      * @cfg {Object} disable  List of toolbar elements to disable
27480          
27481      */
27482     disable : false,
27483     /**
27484      * @cfg {Object} styles List of styles 
27485      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27486      *
27487      * These must be defined in the page, so they get rendered correctly..
27488      * .headline { }
27489      * TD.underline { }
27490      * 
27491      */
27492     styles : false,
27493     
27494     options: false,
27495     
27496     toolbars : false,
27497     
27498     init : function(editor)
27499     {
27500         this.editor = editor;
27501         
27502         
27503         var fid = editor.frameId;
27504         var etb = this;
27505         function btn(id, toggle, handler){
27506             var xid = fid + '-'+ id ;
27507             return {
27508                 id : xid,
27509                 cmd : id,
27510                 cls : 'x-btn-icon x-edit-'+id,
27511                 enableToggle:toggle !== false,
27512                 scope: editor, // was editor...
27513                 handler:handler||editor.relayBtnCmd,
27514                 clickEvent:'mousedown',
27515                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27516                 tabIndex:-1
27517             };
27518         }
27519         // create a new element.
27520         var wdiv = editor.wrap.createChild({
27521                 tag: 'div'
27522             }, editor.wrap.dom.firstChild.nextSibling, true);
27523         
27524         // can we do this more than once??
27525         
27526          // stop form submits
27527       
27528  
27529         // disable everything...
27530         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27531         this.toolbars = {};
27532            
27533         for (var i in  ty) {
27534           
27535             this.toolbars[i] = this.buildToolbar(ty[i],i);
27536         }
27537         this.tb = this.toolbars.BODY;
27538         this.tb.el.show();
27539         this.buildFooter();
27540         this.footer.show();
27541         editor.on('hide', function( ) { this.footer.hide() }, this);
27542         editor.on('show', function( ) { this.footer.show() }, this);
27543         
27544          
27545         this.rendered = true;
27546         
27547         // the all the btns;
27548         editor.on('editorevent', this.updateToolbar, this);
27549         // other toolbars need to implement this..
27550         //editor.on('editmodechange', this.updateToolbar, this);
27551     },
27552     
27553     
27554     
27555     /**
27556      * Protected method that will not generally be called directly. It triggers
27557      * a toolbar update by reading the markup state of the current selection in the editor.
27558      */
27559     updateToolbar: function(editor,ev,sel){
27560
27561         //Roo.log(ev);
27562         // capture mouse up - this is handy for selecting images..
27563         // perhaps should go somewhere else...
27564         if(!this.editor.activated){
27565              this.editor.onFirstFocus();
27566             return;
27567         }
27568         
27569         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27570         // selectNode - might want to handle IE?
27571         if (ev &&
27572             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27573             ev.target && ev.target.tagName == 'IMG') {
27574             // they have click on an image...
27575             // let's see if we can change the selection...
27576             sel = ev.target;
27577          
27578               var nodeRange = sel.ownerDocument.createRange();
27579             try {
27580                 nodeRange.selectNode(sel);
27581             } catch (e) {
27582                 nodeRange.selectNodeContents(sel);
27583             }
27584             //nodeRange.collapse(true);
27585             var s = editor.win.getSelection();
27586             s.removeAllRanges();
27587             s.addRange(nodeRange);
27588         }  
27589         
27590       
27591         var updateFooter = sel ? false : true;
27592         
27593         
27594         var ans = this.editor.getAllAncestors();
27595         
27596         // pick
27597         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27598         
27599         if (!sel) { 
27600             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27601             sel = sel ? sel : this.editor.doc.body;
27602             sel = sel.tagName.length ? sel : this.editor.doc.body;
27603             
27604         }
27605         // pick a menu that exists..
27606         var tn = sel.tagName.toUpperCase();
27607         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27608         
27609         tn = sel.tagName.toUpperCase();
27610         
27611         var lastSel = this.tb.selectedNode
27612         
27613         this.tb.selectedNode = sel;
27614         
27615         // if current menu does not match..
27616         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27617                 
27618             this.tb.el.hide();
27619             ///console.log("show: " + tn);
27620             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27621             this.tb.el.show();
27622             // update name
27623             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27624             
27625             
27626             // update attributes
27627             if (this.tb.fields) {
27628                 this.tb.fields.each(function(e) {
27629                     if (e.stylename) {
27630                         e.setValue(sel.style[e.stylename]);
27631                         return;
27632                     } 
27633                    e.setValue(sel.getAttribute(e.attrname));
27634                 });
27635             }
27636             
27637             var hasStyles = false;
27638             for(var i in this.styles) {
27639                 hasStyles = true;
27640                 break;
27641             }
27642             
27643             // update styles
27644             if (hasStyles) { 
27645                 var st = this.tb.fields.item(0);
27646                 
27647                 st.store.removeAll();
27648                
27649                 
27650                 var cn = sel.className.split(/\s+/);
27651                 
27652                 var avs = [];
27653                 if (this.styles['*']) {
27654                     
27655                     Roo.each(this.styles['*'], function(v) {
27656                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27657                     });
27658                 }
27659                 if (this.styles[tn]) { 
27660                     Roo.each(this.styles[tn], function(v) {
27661                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27662                     });
27663                 }
27664                 
27665                 st.store.loadData(avs);
27666                 st.collapse();
27667                 st.setValue(cn);
27668             }
27669             // flag our selected Node.
27670             this.tb.selectedNode = sel;
27671            
27672            
27673             Roo.menu.MenuMgr.hideAll();
27674
27675         }
27676         
27677         if (!updateFooter) {
27678             //this.footDisp.dom.innerHTML = ''; 
27679             return;
27680         }
27681         // update the footer
27682         //
27683         var html = '';
27684         
27685         this.footerEls = ans.reverse();
27686         Roo.each(this.footerEls, function(a,i) {
27687             if (!a) { return; }
27688             html += html.length ? ' &gt; '  :  '';
27689             
27690             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27691             
27692         });
27693        
27694         // 
27695         var sz = this.footDisp.up('td').getSize();
27696         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27697         this.footDisp.dom.style.marginLeft = '5px';
27698         
27699         this.footDisp.dom.style.overflow = 'hidden';
27700         
27701         this.footDisp.dom.innerHTML = html;
27702             
27703         //this.editorsyncValue();
27704     },
27705      
27706     
27707    
27708        
27709     // private
27710     onDestroy : function(){
27711         if(this.rendered){
27712             
27713             this.tb.items.each(function(item){
27714                 if(item.menu){
27715                     item.menu.removeAll();
27716                     if(item.menu.el){
27717                         item.menu.el.destroy();
27718                     }
27719                 }
27720                 item.destroy();
27721             });
27722              
27723         }
27724     },
27725     onFirstFocus: function() {
27726         // need to do this for all the toolbars..
27727         this.tb.items.each(function(item){
27728            item.enable();
27729         });
27730     },
27731     buildToolbar: function(tlist, nm)
27732     {
27733         var editor = this.editor;
27734          // create a new element.
27735         var wdiv = editor.wrap.createChild({
27736                 tag: 'div'
27737             }, editor.wrap.dom.firstChild.nextSibling, true);
27738         
27739        
27740         var tb = new Roo.Toolbar(wdiv);
27741         // add the name..
27742         
27743         tb.add(nm+ ":&nbsp;");
27744         
27745         var styles = [];
27746         for(var i in this.styles) {
27747             styles.push(i);
27748         }
27749         
27750         // styles...
27751         if (styles && styles.length) {
27752             
27753             // this needs a multi-select checkbox...
27754             tb.addField( new Roo.form.ComboBox({
27755                 store: new Roo.data.SimpleStore({
27756                     id : 'val',
27757                     fields: ['val', 'selected'],
27758                     data : [] 
27759                 }),
27760                 name : '-roo-edit-className',
27761                 attrname : 'className',
27762                 displayField: 'val',
27763                 typeAhead: false,
27764                 mode: 'local',
27765                 editable : false,
27766                 triggerAction: 'all',
27767                 emptyText:'Select Style',
27768                 selectOnFocus:true,
27769                 width: 130,
27770                 listeners : {
27771                     'select': function(c, r, i) {
27772                         // initial support only for on class per el..
27773                         tb.selectedNode.className =  r ? r.get('val') : '';
27774                         editor.syncValue();
27775                     }
27776                 }
27777     
27778             }));
27779         }
27780         
27781         var tbc = Roo.form.HtmlEditor.ToolbarContext;
27782         var tbops = tbc.options;
27783         
27784         for (var i in tlist) {
27785             
27786             var item = tlist[i];
27787             tb.add(item.title + ":&nbsp;");
27788             
27789             
27790             //optname == used so you can configure the options available..
27791             var opts = item.opts ? item.opts : false;
27792             if (item.optname) {
27793                 opts = tbops[item.optname];
27794            
27795             }
27796             
27797             if (opts) {
27798                 // opts == pulldown..
27799                 tb.addField( new Roo.form.ComboBox({
27800                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
27801                         id : 'val',
27802                         fields: ['val', 'display'],
27803                         data : opts  
27804                     }),
27805                     name : '-roo-edit-' + i,
27806                     attrname : i,
27807                     stylename : item.style ? item.style : false,
27808                     displayField: item.displayField ? item.displayField : 'val',
27809                     valueField :  'val',
27810                     typeAhead: false,
27811                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
27812                     editable : false,
27813                     triggerAction: 'all',
27814                     emptyText:'Select',
27815                     selectOnFocus:true,
27816                     width: item.width ? item.width  : 130,
27817                     listeners : {
27818                         'select': function(c, r, i) {
27819                             if (c.stylename) {
27820                                 tb.selectedNode.style[c.stylename] =  r.get('val');
27821                                 return;
27822                             }
27823                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27824                         }
27825                     }
27826
27827                 }));
27828                 continue;
27829                     
27830                  
27831                 
27832                 tb.addField( new Roo.form.TextField({
27833                     name: i,
27834                     width: 100,
27835                     //allowBlank:false,
27836                     value: ''
27837                 }));
27838                 continue;
27839             }
27840             tb.addField( new Roo.form.TextField({
27841                 name: '-roo-edit-' + i,
27842                 attrname : i,
27843                 
27844                 width: item.width,
27845                 //allowBlank:true,
27846                 value: '',
27847                 listeners: {
27848                     'change' : function(f, nv, ov) {
27849                         tb.selectedNode.setAttribute(f.attrname, nv);
27850                     }
27851                 }
27852             }));
27853              
27854         }
27855         tb.addFill();
27856         var _this = this;
27857         tb.addButton( {
27858             text: 'Remove Tag',
27859     
27860             listeners : {
27861                 click : function ()
27862                 {
27863                     // remove
27864                     // undo does not work.
27865                      
27866                     var sn = tb.selectedNode;
27867                     
27868                     var pn = sn.parentNode;
27869                     
27870                     var stn =  sn.childNodes[0];
27871                     var en = sn.childNodes[sn.childNodes.length - 1 ];
27872                     while (sn.childNodes.length) {
27873                         var node = sn.childNodes[0];
27874                         sn.removeChild(node);
27875                         //Roo.log(node);
27876                         pn.insertBefore(node, sn);
27877                         
27878                     }
27879                     pn.removeChild(sn);
27880                     var range = editor.createRange();
27881         
27882                     range.setStart(stn,0);
27883                     range.setEnd(en,0); //????
27884                     //range.selectNode(sel);
27885                     
27886                     
27887                     var selection = editor.getSelection();
27888                     selection.removeAllRanges();
27889                     selection.addRange(range);
27890                     
27891                     
27892                     
27893                     //_this.updateToolbar(null, null, pn);
27894                     _this.updateToolbar(null, null, null);
27895                     _this.footDisp.dom.innerHTML = ''; 
27896                 }
27897             }
27898             
27899                     
27900                 
27901             
27902         });
27903         
27904         
27905         tb.el.on('click', function(e){
27906             e.preventDefault(); // what does this do?
27907         });
27908         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27909         tb.el.hide();
27910         tb.name = nm;
27911         // dont need to disable them... as they will get hidden
27912         return tb;
27913          
27914         
27915     },
27916     buildFooter : function()
27917     {
27918         
27919         var fel = this.editor.wrap.createChild();
27920         this.footer = new Roo.Toolbar(fel);
27921         // toolbar has scrolly on left / right?
27922         var footDisp= new Roo.Toolbar.Fill();
27923         var _t = this;
27924         this.footer.add(
27925             {
27926                 text : '&lt;',
27927                 xtype: 'Button',
27928                 handler : function() {
27929                     _t.footDisp.scrollTo('left',0,true)
27930                 }
27931             }
27932         );
27933         this.footer.add( footDisp );
27934         this.footer.add( 
27935             {
27936                 text : '&gt;',
27937                 xtype: 'Button',
27938                 handler : function() {
27939                     // no animation..
27940                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27941                 }
27942             }
27943         );
27944         var fel = Roo.get(footDisp.el);
27945         fel.addClass('x-editor-context');
27946         this.footDispWrap = fel; 
27947         this.footDispWrap.overflow  = 'hidden';
27948         
27949         this.footDisp = fel.createChild();
27950         this.footDispWrap.on('click', this.onContextClick, this)
27951         
27952         
27953     },
27954     onContextClick : function (ev,dom)
27955     {
27956         ev.preventDefault();
27957         var  cn = dom.className;
27958         //Roo.log(cn);
27959         if (!cn.match(/x-ed-loc-/)) {
27960             return;
27961         }
27962         var n = cn.split('-').pop();
27963         var ans = this.footerEls;
27964         var sel = ans[n];
27965         
27966          // pick
27967         var range = this.editor.createRange();
27968         
27969         range.selectNodeContents(sel);
27970         //range.selectNode(sel);
27971         
27972         
27973         var selection = this.editor.getSelection();
27974         selection.removeAllRanges();
27975         selection.addRange(range);
27976         
27977         
27978         
27979         this.updateToolbar(null, null, sel);
27980         
27981         
27982     }
27983     
27984     
27985     
27986     
27987     
27988 });
27989
27990
27991
27992
27993
27994 /*
27995  * Based on:
27996  * Ext JS Library 1.1.1
27997  * Copyright(c) 2006-2007, Ext JS, LLC.
27998  *
27999  * Originally Released Under LGPL - original licence link has changed is not relivant.
28000  *
28001  * Fork - LGPL
28002  * <script type="text/javascript">
28003  */
28004  
28005 /**
28006  * @class Roo.form.BasicForm
28007  * @extends Roo.util.Observable
28008  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
28009  * @constructor
28010  * @param {String/HTMLElement/Roo.Element} el The form element or its id
28011  * @param {Object} config Configuration options
28012  */
28013 Roo.form.BasicForm = function(el, config){
28014     this.allItems = [];
28015     this.childForms = [];
28016     Roo.apply(this, config);
28017     /*
28018      * The Roo.form.Field items in this form.
28019      * @type MixedCollection
28020      */
28021      
28022      
28023     this.items = new Roo.util.MixedCollection(false, function(o){
28024         return o.id || (o.id = Roo.id());
28025     });
28026     this.addEvents({
28027         /**
28028          * @event beforeaction
28029          * Fires before any action is performed. Return false to cancel the action.
28030          * @param {Form} this
28031          * @param {Action} action The action to be performed
28032          */
28033         beforeaction: true,
28034         /**
28035          * @event actionfailed
28036          * Fires when an action fails.
28037          * @param {Form} this
28038          * @param {Action} action The action that failed
28039          */
28040         actionfailed : true,
28041         /**
28042          * @event actioncomplete
28043          * Fires when an action is completed.
28044          * @param {Form} this
28045          * @param {Action} action The action that completed
28046          */
28047         actioncomplete : true
28048     });
28049     if(el){
28050         this.initEl(el);
28051     }
28052     Roo.form.BasicForm.superclass.constructor.call(this);
28053 };
28054
28055 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
28056     /**
28057      * @cfg {String} method
28058      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
28059      */
28060     /**
28061      * @cfg {DataReader} reader
28062      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
28063      * This is optional as there is built-in support for processing JSON.
28064      */
28065     /**
28066      * @cfg {DataReader} errorReader
28067      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
28068      * This is completely optional as there is built-in support for processing JSON.
28069      */
28070     /**
28071      * @cfg {String} url
28072      * The URL to use for form actions if one isn't supplied in the action options.
28073      */
28074     /**
28075      * @cfg {Boolean} fileUpload
28076      * Set to true if this form is a file upload.
28077      */
28078      
28079     /**
28080      * @cfg {Object} baseParams
28081      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
28082      */
28083      /**
28084      
28085     /**
28086      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
28087      */
28088     timeout: 30,
28089
28090     // private
28091     activeAction : null,
28092
28093     /**
28094      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
28095      * or setValues() data instead of when the form was first created.
28096      */
28097     trackResetOnLoad : false,
28098     
28099     
28100     /**
28101      * childForms - used for multi-tab forms
28102      * @type {Array}
28103      */
28104     childForms : false,
28105     
28106     /**
28107      * allItems - full list of fields.
28108      * @type {Array}
28109      */
28110     allItems : false,
28111     
28112     /**
28113      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
28114      * element by passing it or its id or mask the form itself by passing in true.
28115      * @type Mixed
28116      */
28117     waitMsgTarget : false,
28118
28119     // private
28120     initEl : function(el){
28121         this.el = Roo.get(el);
28122         this.id = this.el.id || Roo.id();
28123         this.el.on('submit', this.onSubmit, this);
28124         this.el.addClass('x-form');
28125     },
28126
28127     // private
28128     onSubmit : function(e){
28129         e.stopEvent();
28130     },
28131
28132     /**
28133      * Returns true if client-side validation on the form is successful.
28134      * @return Boolean
28135      */
28136     isValid : function(){
28137         var valid = true;
28138         this.items.each(function(f){
28139            if(!f.validate()){
28140                valid = false;
28141            }
28142         });
28143         return valid;
28144     },
28145
28146     /**
28147      * Returns true if any fields in this form have changed since their original load.
28148      * @return Boolean
28149      */
28150     isDirty : function(){
28151         var dirty = false;
28152         this.items.each(function(f){
28153            if(f.isDirty()){
28154                dirty = true;
28155                return false;
28156            }
28157         });
28158         return dirty;
28159     },
28160
28161     /**
28162      * Performs a predefined action (submit or load) or custom actions you define on this form.
28163      * @param {String} actionName The name of the action type
28164      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
28165      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
28166      * accept other config options):
28167      * <pre>
28168 Property          Type             Description
28169 ----------------  ---------------  ----------------------------------------------------------------------------------
28170 url               String           The url for the action (defaults to the form's url)
28171 method            String           The form method to use (defaults to the form's method, or POST if not defined)
28172 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
28173 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
28174                                    validate the form on the client (defaults to false)
28175      * </pre>
28176      * @return {BasicForm} this
28177      */
28178     doAction : function(action, options){
28179         if(typeof action == 'string'){
28180             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
28181         }
28182         if(this.fireEvent('beforeaction', this, action) !== false){
28183             this.beforeAction(action);
28184             action.run.defer(100, action);
28185         }
28186         return this;
28187     },
28188
28189     /**
28190      * Shortcut to do a submit action.
28191      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28192      * @return {BasicForm} this
28193      */
28194     submit : function(options){
28195         this.doAction('submit', options);
28196         return this;
28197     },
28198
28199     /**
28200      * Shortcut to do a load action.
28201      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
28202      * @return {BasicForm} this
28203      */
28204     load : function(options){
28205         this.doAction('load', options);
28206         return this;
28207     },
28208
28209     /**
28210      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
28211      * @param {Record} record The record to edit
28212      * @return {BasicForm} this
28213      */
28214     updateRecord : function(record){
28215         record.beginEdit();
28216         var fs = record.fields;
28217         fs.each(function(f){
28218             var field = this.findField(f.name);
28219             if(field){
28220                 record.set(f.name, field.getValue());
28221             }
28222         }, this);
28223         record.endEdit();
28224         return this;
28225     },
28226
28227     /**
28228      * Loads an Roo.data.Record into this form.
28229      * @param {Record} record The record to load
28230      * @return {BasicForm} this
28231      */
28232     loadRecord : function(record){
28233         this.setValues(record.data);
28234         return this;
28235     },
28236
28237     // private
28238     beforeAction : function(action){
28239         var o = action.options;
28240         
28241        
28242         if(this.waitMsgTarget === true){
28243             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
28244         }else if(this.waitMsgTarget){
28245             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
28246             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
28247         }else {
28248             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
28249         }
28250          
28251     },
28252
28253     // private
28254     afterAction : function(action, success){
28255         this.activeAction = null;
28256         var o = action.options;
28257         
28258         if(this.waitMsgTarget === true){
28259             this.el.unmask();
28260         }else if(this.waitMsgTarget){
28261             this.waitMsgTarget.unmask();
28262         }else{
28263             Roo.MessageBox.updateProgress(1);
28264             Roo.MessageBox.hide();
28265         }
28266          
28267         if(success){
28268             if(o.reset){
28269                 this.reset();
28270             }
28271             Roo.callback(o.success, o.scope, [this, action]);
28272             this.fireEvent('actioncomplete', this, action);
28273             
28274         }else{
28275             
28276             // failure condition..
28277             // we have a scenario where updates need confirming.
28278             // eg. if a locking scenario exists..
28279             // we look for { errors : { needs_confirm : true }} in the response.
28280             if (
28281                 (typeof(action.result) != 'undefined')  &&
28282                 (typeof(action.result.errors) != 'undefined')  &&
28283                 (typeof(action.result.errors.needs_confirm) != 'undefined')
28284            ){
28285                 var _t = this;
28286                 Roo.MessageBox.confirm(
28287                     "Change requires confirmation",
28288                     action.result.errorMsg,
28289                     function(r) {
28290                         if (r != 'yes') {
28291                             return;
28292                         }
28293                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
28294                     }
28295                     
28296                 );
28297                 
28298                 
28299                 
28300                 return;
28301             }
28302             
28303             Roo.callback(o.failure, o.scope, [this, action]);
28304             // show an error message if no failed handler is set..
28305             if (!this.hasListener('actionfailed')) {
28306                 Roo.MessageBox.alert("Error",
28307                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
28308                         action.result.errorMsg :
28309                         "Saving Failed, please check your entries or try again"
28310                 );
28311             }
28312             
28313             this.fireEvent('actionfailed', this, action);
28314         }
28315         
28316     },
28317
28318     /**
28319      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
28320      * @param {String} id The value to search for
28321      * @return Field
28322      */
28323     findField : function(id){
28324         var field = this.items.get(id);
28325         if(!field){
28326             this.items.each(function(f){
28327                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
28328                     field = f;
28329                     return false;
28330                 }
28331             });
28332         }
28333         return field || null;
28334     },
28335
28336     /**
28337      * Add a secondary form to this one, 
28338      * Used to provide tabbed forms. One form is primary, with hidden values 
28339      * which mirror the elements from the other forms.
28340      * 
28341      * @param {Roo.form.Form} form to add.
28342      * 
28343      */
28344     addForm : function(form)
28345     {
28346        
28347         if (this.childForms.indexOf(form) > -1) {
28348             // already added..
28349             return;
28350         }
28351         this.childForms.push(form);
28352         var n = '';
28353         Roo.each(form.allItems, function (fe) {
28354             
28355             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
28356             if (this.findField(n)) { // already added..
28357                 return;
28358             }
28359             var add = new Roo.form.Hidden({
28360                 name : n
28361             });
28362             add.render(this.el);
28363             
28364             this.add( add );
28365         }, this);
28366         
28367     },
28368     /**
28369      * Mark fields in this form invalid in bulk.
28370      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
28371      * @return {BasicForm} this
28372      */
28373     markInvalid : function(errors){
28374         if(errors instanceof Array){
28375             for(var i = 0, len = errors.length; i < len; i++){
28376                 var fieldError = errors[i];
28377                 var f = this.findField(fieldError.id);
28378                 if(f){
28379                     f.markInvalid(fieldError.msg);
28380                 }
28381             }
28382         }else{
28383             var field, id;
28384             for(id in errors){
28385                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28386                     field.markInvalid(errors[id]);
28387                 }
28388             }
28389         }
28390         Roo.each(this.childForms || [], function (f) {
28391             f.markInvalid(errors);
28392         });
28393         
28394         return this;
28395     },
28396
28397     /**
28398      * Set values for fields in this form in bulk.
28399      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28400      * @return {BasicForm} this
28401      */
28402     setValues : function(values){
28403         if(values instanceof Array){ // array of objects
28404             for(var i = 0, len = values.length; i < len; i++){
28405                 var v = values[i];
28406                 var f = this.findField(v.id);
28407                 if(f){
28408                     f.setValue(v.value);
28409                     if(this.trackResetOnLoad){
28410                         f.originalValue = f.getValue();
28411                     }
28412                 }
28413             }
28414         }else{ // object hash
28415             var field, id;
28416             for(id in values){
28417                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28418                     
28419                     if (field.setFromData && 
28420                         field.valueField && 
28421                         field.displayField &&
28422                         // combos' with local stores can 
28423                         // be queried via setValue()
28424                         // to set their value..
28425                         (field.store && !field.store.isLocal)
28426                         ) {
28427                         // it's a combo
28428                         var sd = { };
28429                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28430                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28431                         field.setFromData(sd);
28432                         
28433                     } else {
28434                         field.setValue(values[id]);
28435                     }
28436                     
28437                     
28438                     if(this.trackResetOnLoad){
28439                         field.originalValue = field.getValue();
28440                     }
28441                 }
28442             }
28443         }
28444          
28445         Roo.each(this.childForms || [], function (f) {
28446             f.setValues(values);
28447         });
28448                 
28449         return this;
28450     },
28451
28452     /**
28453      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28454      * they are returned as an array.
28455      * @param {Boolean} asString
28456      * @return {Object}
28457      */
28458     getValues : function(asString){
28459         if (this.childForms) {
28460             // copy values from the child forms
28461             Roo.each(this.childForms, function (f) {
28462                 this.setValues(f.getValues());
28463             }, this);
28464         }
28465         
28466         
28467         
28468         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28469         if(asString === true){
28470             return fs;
28471         }
28472         return Roo.urlDecode(fs);
28473     },
28474     
28475     /**
28476      * Returns the fields in this form as an object with key/value pairs. 
28477      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28478      * @return {Object}
28479      */
28480     getFieldValues : function(with_hidden)
28481     {
28482         if (this.childForms) {
28483             // copy values from the child forms
28484             // should this call getFieldValues - probably not as we do not currently copy
28485             // hidden fields when we generate..
28486             Roo.each(this.childForms, function (f) {
28487                 this.setValues(f.getValues());
28488             }, this);
28489         }
28490         
28491         var ret = {};
28492         this.items.each(function(f){
28493             if (!f.getName()) {
28494                 return;
28495             }
28496             var v = f.getValue();
28497             if (f.inputType =='radio') {
28498                 if (typeof(ret[f.getName()]) == 'undefined') {
28499                     ret[f.getName()] = ''; // empty..
28500                 }
28501                 
28502                 if (!f.el.dom.checked) {
28503                     return;
28504                     
28505                 }
28506                 v = f.el.dom.value;
28507                 
28508             }
28509             
28510             // not sure if this supported any more..
28511             if ((typeof(v) == 'object') && f.getRawValue) {
28512                 v = f.getRawValue() ; // dates..
28513             }
28514             // combo boxes where name != hiddenName...
28515             if (f.name != f.getName()) {
28516                 ret[f.name] = f.getRawValue();
28517             }
28518             ret[f.getName()] = v;
28519         });
28520         
28521         return ret;
28522     },
28523
28524     /**
28525      * Clears all invalid messages in this form.
28526      * @return {BasicForm} this
28527      */
28528     clearInvalid : function(){
28529         this.items.each(function(f){
28530            f.clearInvalid();
28531         });
28532         
28533         Roo.each(this.childForms || [], function (f) {
28534             f.clearInvalid();
28535         });
28536         
28537         
28538         return this;
28539     },
28540
28541     /**
28542      * Resets this form.
28543      * @return {BasicForm} this
28544      */
28545     reset : function(){
28546         this.items.each(function(f){
28547             f.reset();
28548         });
28549         
28550         Roo.each(this.childForms || [], function (f) {
28551             f.reset();
28552         });
28553        
28554         
28555         return this;
28556     },
28557
28558     /**
28559      * Add Roo.form components to this form.
28560      * @param {Field} field1
28561      * @param {Field} field2 (optional)
28562      * @param {Field} etc (optional)
28563      * @return {BasicForm} this
28564      */
28565     add : function(){
28566         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28567         return this;
28568     },
28569
28570
28571     /**
28572      * Removes a field from the items collection (does NOT remove its markup).
28573      * @param {Field} field
28574      * @return {BasicForm} this
28575      */
28576     remove : function(field){
28577         this.items.remove(field);
28578         return this;
28579     },
28580
28581     /**
28582      * Looks at the fields in this form, checks them for an id attribute,
28583      * and calls applyTo on the existing dom element with that id.
28584      * @return {BasicForm} this
28585      */
28586     render : function(){
28587         this.items.each(function(f){
28588             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28589                 f.applyTo(f.id);
28590             }
28591         });
28592         return this;
28593     },
28594
28595     /**
28596      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28597      * @param {Object} values
28598      * @return {BasicForm} this
28599      */
28600     applyToFields : function(o){
28601         this.items.each(function(f){
28602            Roo.apply(f, o);
28603         });
28604         return this;
28605     },
28606
28607     /**
28608      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28609      * @param {Object} values
28610      * @return {BasicForm} this
28611      */
28612     applyIfToFields : function(o){
28613         this.items.each(function(f){
28614            Roo.applyIf(f, o);
28615         });
28616         return this;
28617     }
28618 });
28619
28620 // back compat
28621 Roo.BasicForm = Roo.form.BasicForm;/*
28622  * Based on:
28623  * Ext JS Library 1.1.1
28624  * Copyright(c) 2006-2007, Ext JS, LLC.
28625  *
28626  * Originally Released Under LGPL - original licence link has changed is not relivant.
28627  *
28628  * Fork - LGPL
28629  * <script type="text/javascript">
28630  */
28631
28632 /**
28633  * @class Roo.form.Form
28634  * @extends Roo.form.BasicForm
28635  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28636  * @constructor
28637  * @param {Object} config Configuration options
28638  */
28639 Roo.form.Form = function(config){
28640     var xitems =  [];
28641     if (config.items) {
28642         xitems = config.items;
28643         delete config.items;
28644     }
28645    
28646     
28647     Roo.form.Form.superclass.constructor.call(this, null, config);
28648     this.url = this.url || this.action;
28649     if(!this.root){
28650         this.root = new Roo.form.Layout(Roo.applyIf({
28651             id: Roo.id()
28652         }, config));
28653     }
28654     this.active = this.root;
28655     /**
28656      * Array of all the buttons that have been added to this form via {@link addButton}
28657      * @type Array
28658      */
28659     this.buttons = [];
28660     this.allItems = [];
28661     this.addEvents({
28662         /**
28663          * @event clientvalidation
28664          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28665          * @param {Form} this
28666          * @param {Boolean} valid true if the form has passed client-side validation
28667          */
28668         clientvalidation: true,
28669         /**
28670          * @event rendered
28671          * Fires when the form is rendered
28672          * @param {Roo.form.Form} form
28673          */
28674         rendered : true
28675     });
28676     
28677     if (this.progressUrl) {
28678             // push a hidden field onto the list of fields..
28679             this.addxtype( {
28680                     xns: Roo.form, 
28681                     xtype : 'Hidden', 
28682                     name : 'UPLOAD_IDENTIFIER' 
28683             });
28684         }
28685         
28686     
28687     Roo.each(xitems, this.addxtype, this);
28688     
28689     
28690     
28691 };
28692
28693 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28694     /**
28695      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28696      */
28697     /**
28698      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28699      */
28700     /**
28701      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28702      */
28703     buttonAlign:'center',
28704
28705     /**
28706      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28707      */
28708     minButtonWidth:75,
28709
28710     /**
28711      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28712      * This property cascades to child containers if not set.
28713      */
28714     labelAlign:'left',
28715
28716     /**
28717      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28718      * fires a looping event with that state. This is required to bind buttons to the valid
28719      * state using the config value formBind:true on the button.
28720      */
28721     monitorValid : false,
28722
28723     /**
28724      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28725      */
28726     monitorPoll : 200,
28727     
28728     /**
28729      * @cfg {String} progressUrl - Url to return progress data 
28730      */
28731     
28732     progressUrl : false,
28733   
28734     /**
28735      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28736      * fields are added and the column is closed. If no fields are passed the column remains open
28737      * until end() is called.
28738      * @param {Object} config The config to pass to the column
28739      * @param {Field} field1 (optional)
28740      * @param {Field} field2 (optional)
28741      * @param {Field} etc (optional)
28742      * @return Column The column container object
28743      */
28744     column : function(c){
28745         var col = new Roo.form.Column(c);
28746         this.start(col);
28747         if(arguments.length > 1){ // duplicate code required because of Opera
28748             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28749             this.end();
28750         }
28751         return col;
28752     },
28753
28754     /**
28755      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28756      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28757      * until end() is called.
28758      * @param {Object} config The config to pass to the fieldset
28759      * @param {Field} field1 (optional)
28760      * @param {Field} field2 (optional)
28761      * @param {Field} etc (optional)
28762      * @return FieldSet The fieldset container object
28763      */
28764     fieldset : function(c){
28765         var fs = new Roo.form.FieldSet(c);
28766         this.start(fs);
28767         if(arguments.length > 1){ // duplicate code required because of Opera
28768             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28769             this.end();
28770         }
28771         return fs;
28772     },
28773
28774     /**
28775      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28776      * fields are added and the container is closed. If no fields are passed the container remains open
28777      * until end() is called.
28778      * @param {Object} config The config to pass to the Layout
28779      * @param {Field} field1 (optional)
28780      * @param {Field} field2 (optional)
28781      * @param {Field} etc (optional)
28782      * @return Layout The container object
28783      */
28784     container : function(c){
28785         var l = new Roo.form.Layout(c);
28786         this.start(l);
28787         if(arguments.length > 1){ // duplicate code required because of Opera
28788             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28789             this.end();
28790         }
28791         return l;
28792     },
28793
28794     /**
28795      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28796      * @param {Object} container A Roo.form.Layout or subclass of Layout
28797      * @return {Form} this
28798      */
28799     start : function(c){
28800         // cascade label info
28801         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28802         this.active.stack.push(c);
28803         c.ownerCt = this.active;
28804         this.active = c;
28805         return this;
28806     },
28807
28808     /**
28809      * Closes the current open container
28810      * @return {Form} this
28811      */
28812     end : function(){
28813         if(this.active == this.root){
28814             return this;
28815         }
28816         this.active = this.active.ownerCt;
28817         return this;
28818     },
28819
28820     /**
28821      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28822      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28823      * as the label of the field.
28824      * @param {Field} field1
28825      * @param {Field} field2 (optional)
28826      * @param {Field} etc. (optional)
28827      * @return {Form} this
28828      */
28829     add : function(){
28830         this.active.stack.push.apply(this.active.stack, arguments);
28831         this.allItems.push.apply(this.allItems,arguments);
28832         var r = [];
28833         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28834             if(a[i].isFormField){
28835                 r.push(a[i]);
28836             }
28837         }
28838         if(r.length > 0){
28839             Roo.form.Form.superclass.add.apply(this, r);
28840         }
28841         return this;
28842     },
28843     
28844
28845     
28846     
28847     
28848      /**
28849      * Find any element that has been added to a form, using it's ID or name
28850      * This can include framesets, columns etc. along with regular fields..
28851      * @param {String} id - id or name to find.
28852      
28853      * @return {Element} e - or false if nothing found.
28854      */
28855     findbyId : function(id)
28856     {
28857         var ret = false;
28858         if (!id) {
28859             return ret;
28860         }
28861         Roo.each(this.allItems, function(f){
28862             if (f.id == id || f.name == id ){
28863                 ret = f;
28864                 return false;
28865             }
28866         });
28867         return ret;
28868     },
28869
28870     
28871     
28872     /**
28873      * Render this form into the passed container. This should only be called once!
28874      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28875      * @return {Form} this
28876      */
28877     render : function(ct)
28878     {
28879         
28880         
28881         
28882         ct = Roo.get(ct);
28883         var o = this.autoCreate || {
28884             tag: 'form',
28885             method : this.method || 'POST',
28886             id : this.id || Roo.id()
28887         };
28888         this.initEl(ct.createChild(o));
28889
28890         this.root.render(this.el);
28891         
28892        
28893              
28894         this.items.each(function(f){
28895             f.render('x-form-el-'+f.id);
28896         });
28897
28898         if(this.buttons.length > 0){
28899             // tables are required to maintain order and for correct IE layout
28900             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28901                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28902                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28903             }}, null, true);
28904             var tr = tb.getElementsByTagName('tr')[0];
28905             for(var i = 0, len = this.buttons.length; i < len; i++) {
28906                 var b = this.buttons[i];
28907                 var td = document.createElement('td');
28908                 td.className = 'x-form-btn-td';
28909                 b.render(tr.appendChild(td));
28910             }
28911         }
28912         if(this.monitorValid){ // initialize after render
28913             this.startMonitoring();
28914         }
28915         this.fireEvent('rendered', this);
28916         return this;
28917     },
28918
28919     /**
28920      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28921      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28922      * object or a valid Roo.DomHelper element config
28923      * @param {Function} handler The function called when the button is clicked
28924      * @param {Object} scope (optional) The scope of the handler function
28925      * @return {Roo.Button}
28926      */
28927     addButton : function(config, handler, scope){
28928         var bc = {
28929             handler: handler,
28930             scope: scope,
28931             minWidth: this.minButtonWidth,
28932             hideParent:true
28933         };
28934         if(typeof config == "string"){
28935             bc.text = config;
28936         }else{
28937             Roo.apply(bc, config);
28938         }
28939         var btn = new Roo.Button(null, bc);
28940         this.buttons.push(btn);
28941         return btn;
28942     },
28943
28944      /**
28945      * Adds a series of form elements (using the xtype property as the factory method.
28946      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28947      * @param {Object} config 
28948      */
28949     
28950     addxtype : function()
28951     {
28952         var ar = Array.prototype.slice.call(arguments, 0);
28953         var ret = false;
28954         for(var i = 0; i < ar.length; i++) {
28955             if (!ar[i]) {
28956                 continue; // skip -- if this happends something invalid got sent, we 
28957                 // should ignore it, as basically that interface element will not show up
28958                 // and that should be pretty obvious!!
28959             }
28960             
28961             if (Roo.form[ar[i].xtype]) {
28962                 ar[i].form = this;
28963                 var fe = Roo.factory(ar[i], Roo.form);
28964                 if (!ret) {
28965                     ret = fe;
28966                 }
28967                 fe.form = this;
28968                 if (fe.store) {
28969                     fe.store.form = this;
28970                 }
28971                 if (fe.isLayout) {  
28972                          
28973                     this.start(fe);
28974                     this.allItems.push(fe);
28975                     if (fe.items && fe.addxtype) {
28976                         fe.addxtype.apply(fe, fe.items);
28977                         delete fe.items;
28978                     }
28979                      this.end();
28980                     continue;
28981                 }
28982                 
28983                 
28984                  
28985                 this.add(fe);
28986               //  console.log('adding ' + ar[i].xtype);
28987             }
28988             if (ar[i].xtype == 'Button') {  
28989                 //console.log('adding button');
28990                 //console.log(ar[i]);
28991                 this.addButton(ar[i]);
28992                 this.allItems.push(fe);
28993                 continue;
28994             }
28995             
28996             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28997                 alert('end is not supported on xtype any more, use items');
28998             //    this.end();
28999             //    //console.log('adding end');
29000             }
29001             
29002         }
29003         return ret;
29004     },
29005     
29006     /**
29007      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
29008      * option "monitorValid"
29009      */
29010     startMonitoring : function(){
29011         if(!this.bound){
29012             this.bound = true;
29013             Roo.TaskMgr.start({
29014                 run : this.bindHandler,
29015                 interval : this.monitorPoll || 200,
29016                 scope: this
29017             });
29018         }
29019     },
29020
29021     /**
29022      * Stops monitoring of the valid state of this form
29023      */
29024     stopMonitoring : function(){
29025         this.bound = false;
29026     },
29027
29028     // private
29029     bindHandler : function(){
29030         if(!this.bound){
29031             return false; // stops binding
29032         }
29033         var valid = true;
29034         this.items.each(function(f){
29035             if(!f.isValid(true)){
29036                 valid = false;
29037                 return false;
29038             }
29039         });
29040         for(var i = 0, len = this.buttons.length; i < len; i++){
29041             var btn = this.buttons[i];
29042             if(btn.formBind === true && btn.disabled === valid){
29043                 btn.setDisabled(!valid);
29044             }
29045         }
29046         this.fireEvent('clientvalidation', this, valid);
29047     }
29048     
29049     
29050     
29051     
29052     
29053     
29054     
29055     
29056 });
29057
29058
29059 // back compat
29060 Roo.Form = Roo.form.Form;
29061 /*
29062  * Based on:
29063  * Ext JS Library 1.1.1
29064  * Copyright(c) 2006-2007, Ext JS, LLC.
29065  *
29066  * Originally Released Under LGPL - original licence link has changed is not relivant.
29067  *
29068  * Fork - LGPL
29069  * <script type="text/javascript">
29070  */
29071  
29072  /**
29073  * @class Roo.form.Action
29074  * Internal Class used to handle form actions
29075  * @constructor
29076  * @param {Roo.form.BasicForm} el The form element or its id
29077  * @param {Object} config Configuration options
29078  */
29079  
29080  
29081 // define the action interface
29082 Roo.form.Action = function(form, options){
29083     this.form = form;
29084     this.options = options || {};
29085 };
29086 /**
29087  * Client Validation Failed
29088  * @const 
29089  */
29090 Roo.form.Action.CLIENT_INVALID = 'client';
29091 /**
29092  * Server Validation Failed
29093  * @const 
29094  */
29095  Roo.form.Action.SERVER_INVALID = 'server';
29096  /**
29097  * Connect to Server Failed
29098  * @const 
29099  */
29100 Roo.form.Action.CONNECT_FAILURE = 'connect';
29101 /**
29102  * Reading Data from Server Failed
29103  * @const 
29104  */
29105 Roo.form.Action.LOAD_FAILURE = 'load';
29106
29107 Roo.form.Action.prototype = {
29108     type : 'default',
29109     failureType : undefined,
29110     response : undefined,
29111     result : undefined,
29112
29113     // interface method
29114     run : function(options){
29115
29116     },
29117
29118     // interface method
29119     success : function(response){
29120
29121     },
29122
29123     // interface method
29124     handleResponse : function(response){
29125
29126     },
29127
29128     // default connection failure
29129     failure : function(response){
29130         
29131         this.response = response;
29132         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29133         this.form.afterAction(this, false);
29134     },
29135
29136     processResponse : function(response){
29137         this.response = response;
29138         if(!response.responseText){
29139             return true;
29140         }
29141         this.result = this.handleResponse(response);
29142         return this.result;
29143     },
29144
29145     // utility functions used internally
29146     getUrl : function(appendParams){
29147         var url = this.options.url || this.form.url || this.form.el.dom.action;
29148         if(appendParams){
29149             var p = this.getParams();
29150             if(p){
29151                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
29152             }
29153         }
29154         return url;
29155     },
29156
29157     getMethod : function(){
29158         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
29159     },
29160
29161     getParams : function(){
29162         var bp = this.form.baseParams;
29163         var p = this.options.params;
29164         if(p){
29165             if(typeof p == "object"){
29166                 p = Roo.urlEncode(Roo.applyIf(p, bp));
29167             }else if(typeof p == 'string' && bp){
29168                 p += '&' + Roo.urlEncode(bp);
29169             }
29170         }else if(bp){
29171             p = Roo.urlEncode(bp);
29172         }
29173         return p;
29174     },
29175
29176     createCallback : function(){
29177         return {
29178             success: this.success,
29179             failure: this.failure,
29180             scope: this,
29181             timeout: (this.form.timeout*1000),
29182             upload: this.form.fileUpload ? this.success : undefined
29183         };
29184     }
29185 };
29186
29187 Roo.form.Action.Submit = function(form, options){
29188     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
29189 };
29190
29191 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
29192     type : 'submit',
29193
29194     haveProgress : false,
29195     uploadComplete : false,
29196     
29197     // uploadProgress indicator.
29198     uploadProgress : function()
29199     {
29200         if (!this.form.progressUrl) {
29201             return;
29202         }
29203         
29204         if (!this.haveProgress) {
29205             Roo.MessageBox.progress("Uploading", "Uploading");
29206         }
29207         if (this.uploadComplete) {
29208            Roo.MessageBox.hide();
29209            return;
29210         }
29211         
29212         this.haveProgress = true;
29213    
29214         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
29215         
29216         var c = new Roo.data.Connection();
29217         c.request({
29218             url : this.form.progressUrl,
29219             params: {
29220                 id : uid
29221             },
29222             method: 'GET',
29223             success : function(req){
29224                //console.log(data);
29225                 var rdata = false;
29226                 var edata;
29227                 try  {
29228                    rdata = Roo.decode(req.responseText)
29229                 } catch (e) {
29230                     Roo.log("Invalid data from server..");
29231                     Roo.log(edata);
29232                     return;
29233                 }
29234                 if (!rdata || !rdata.success) {
29235                     Roo.log(rdata);
29236                     Roo.MessageBox.alert(Roo.encode(rdata));
29237                     return;
29238                 }
29239                 var data = rdata.data;
29240                 
29241                 if (this.uploadComplete) {
29242                    Roo.MessageBox.hide();
29243                    return;
29244                 }
29245                    
29246                 if (data){
29247                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
29248                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
29249                     );
29250                 }
29251                 this.uploadProgress.defer(2000,this);
29252             },
29253        
29254             failure: function(data) {
29255                 Roo.log('progress url failed ');
29256                 Roo.log(data);
29257             },
29258             scope : this
29259         });
29260            
29261     },
29262     
29263     
29264     run : function()
29265     {
29266         // run get Values on the form, so it syncs any secondary forms.
29267         this.form.getValues();
29268         
29269         var o = this.options;
29270         var method = this.getMethod();
29271         var isPost = method == 'POST';
29272         if(o.clientValidation === false || this.form.isValid()){
29273             
29274             if (this.form.progressUrl) {
29275                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
29276                     (new Date() * 1) + '' + Math.random());
29277                     
29278             } 
29279             
29280             
29281             Roo.Ajax.request(Roo.apply(this.createCallback(), {
29282                 form:this.form.el.dom,
29283                 url:this.getUrl(!isPost),
29284                 method: method,
29285                 params:isPost ? this.getParams() : null,
29286                 isUpload: this.form.fileUpload
29287             }));
29288             
29289             this.uploadProgress();
29290
29291         }else if (o.clientValidation !== false){ // client validation failed
29292             this.failureType = Roo.form.Action.CLIENT_INVALID;
29293             this.form.afterAction(this, false);
29294         }
29295     },
29296
29297     success : function(response)
29298     {
29299         this.uploadComplete= true;
29300         if (this.haveProgress) {
29301             Roo.MessageBox.hide();
29302         }
29303         
29304         
29305         var result = this.processResponse(response);
29306         if(result === true || result.success){
29307             this.form.afterAction(this, true);
29308             return;
29309         }
29310         if(result.errors){
29311             this.form.markInvalid(result.errors);
29312             this.failureType = Roo.form.Action.SERVER_INVALID;
29313         }
29314         this.form.afterAction(this, false);
29315     },
29316     failure : function(response)
29317     {
29318         this.uploadComplete= true;
29319         if (this.haveProgress) {
29320             Roo.MessageBox.hide();
29321         }
29322         
29323         this.response = response;
29324         this.failureType = Roo.form.Action.CONNECT_FAILURE;
29325         this.form.afterAction(this, false);
29326     },
29327     
29328     handleResponse : function(response){
29329         if(this.form.errorReader){
29330             var rs = this.form.errorReader.read(response);
29331             var errors = [];
29332             if(rs.records){
29333                 for(var i = 0, len = rs.records.length; i < len; i++) {
29334                     var r = rs.records[i];
29335                     errors[i] = r.data;
29336                 }
29337             }
29338             if(errors.length < 1){
29339                 errors = null;
29340             }
29341             return {
29342                 success : rs.success,
29343                 errors : errors
29344             };
29345         }
29346         var ret = false;
29347         try {
29348             ret = Roo.decode(response.responseText);
29349         } catch (e) {
29350             ret = {
29351                 success: false,
29352                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
29353                 errors : []
29354             };
29355         }
29356         return ret;
29357         
29358     }
29359 });
29360
29361
29362 Roo.form.Action.Load = function(form, options){
29363     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
29364     this.reader = this.form.reader;
29365 };
29366
29367 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
29368     type : 'load',
29369
29370     run : function(){
29371         
29372         Roo.Ajax.request(Roo.apply(
29373                 this.createCallback(), {
29374                     method:this.getMethod(),
29375                     url:this.getUrl(false),
29376                     params:this.getParams()
29377         }));
29378     },
29379
29380     success : function(response){
29381         
29382         var result = this.processResponse(response);
29383         if(result === true || !result.success || !result.data){
29384             this.failureType = Roo.form.Action.LOAD_FAILURE;
29385             this.form.afterAction(this, false);
29386             return;
29387         }
29388         this.form.clearInvalid();
29389         this.form.setValues(result.data);
29390         this.form.afterAction(this, true);
29391     },
29392
29393     handleResponse : function(response){
29394         if(this.form.reader){
29395             var rs = this.form.reader.read(response);
29396             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29397             return {
29398                 success : rs.success,
29399                 data : data
29400             };
29401         }
29402         return Roo.decode(response.responseText);
29403     }
29404 });
29405
29406 Roo.form.Action.ACTION_TYPES = {
29407     'load' : Roo.form.Action.Load,
29408     'submit' : Roo.form.Action.Submit
29409 };/*
29410  * Based on:
29411  * Ext JS Library 1.1.1
29412  * Copyright(c) 2006-2007, Ext JS, LLC.
29413  *
29414  * Originally Released Under LGPL - original licence link has changed is not relivant.
29415  *
29416  * Fork - LGPL
29417  * <script type="text/javascript">
29418  */
29419  
29420 /**
29421  * @class Roo.form.Layout
29422  * @extends Roo.Component
29423  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29424  * @constructor
29425  * @param {Object} config Configuration options
29426  */
29427 Roo.form.Layout = function(config){
29428     var xitems = [];
29429     if (config.items) {
29430         xitems = config.items;
29431         delete config.items;
29432     }
29433     Roo.form.Layout.superclass.constructor.call(this, config);
29434     this.stack = [];
29435     Roo.each(xitems, this.addxtype, this);
29436      
29437 };
29438
29439 Roo.extend(Roo.form.Layout, Roo.Component, {
29440     /**
29441      * @cfg {String/Object} autoCreate
29442      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29443      */
29444     /**
29445      * @cfg {String/Object/Function} style
29446      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29447      * a function which returns such a specification.
29448      */
29449     /**
29450      * @cfg {String} labelAlign
29451      * Valid values are "left," "top" and "right" (defaults to "left")
29452      */
29453     /**
29454      * @cfg {Number} labelWidth
29455      * Fixed width in pixels of all field labels (defaults to undefined)
29456      */
29457     /**
29458      * @cfg {Boolean} clear
29459      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29460      */
29461     clear : true,
29462     /**
29463      * @cfg {String} labelSeparator
29464      * The separator to use after field labels (defaults to ':')
29465      */
29466     labelSeparator : ':',
29467     /**
29468      * @cfg {Boolean} hideLabels
29469      * True to suppress the display of field labels in this layout (defaults to false)
29470      */
29471     hideLabels : false,
29472
29473     // private
29474     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29475     
29476     isLayout : true,
29477     
29478     // private
29479     onRender : function(ct, position){
29480         if(this.el){ // from markup
29481             this.el = Roo.get(this.el);
29482         }else {  // generate
29483             var cfg = this.getAutoCreate();
29484             this.el = ct.createChild(cfg, position);
29485         }
29486         if(this.style){
29487             this.el.applyStyles(this.style);
29488         }
29489         if(this.labelAlign){
29490             this.el.addClass('x-form-label-'+this.labelAlign);
29491         }
29492         if(this.hideLabels){
29493             this.labelStyle = "display:none";
29494             this.elementStyle = "padding-left:0;";
29495         }else{
29496             if(typeof this.labelWidth == 'number'){
29497                 this.labelStyle = "width:"+this.labelWidth+"px;";
29498                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29499             }
29500             if(this.labelAlign == 'top'){
29501                 this.labelStyle = "width:auto;";
29502                 this.elementStyle = "padding-left:0;";
29503             }
29504         }
29505         var stack = this.stack;
29506         var slen = stack.length;
29507         if(slen > 0){
29508             if(!this.fieldTpl){
29509                 var t = new Roo.Template(
29510                     '<div class="x-form-item {5}">',
29511                         '<label for="{0}" style="{2}">{1}{4}</label>',
29512                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29513                         '</div>',
29514                     '</div><div class="x-form-clear-left"></div>'
29515                 );
29516                 t.disableFormats = true;
29517                 t.compile();
29518                 Roo.form.Layout.prototype.fieldTpl = t;
29519             }
29520             for(var i = 0; i < slen; i++) {
29521                 if(stack[i].isFormField){
29522                     this.renderField(stack[i]);
29523                 }else{
29524                     this.renderComponent(stack[i]);
29525                 }
29526             }
29527         }
29528         if(this.clear){
29529             this.el.createChild({cls:'x-form-clear'});
29530         }
29531     },
29532
29533     // private
29534     renderField : function(f){
29535         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29536                f.id, //0
29537                f.fieldLabel, //1
29538                f.labelStyle||this.labelStyle||'', //2
29539                this.elementStyle||'', //3
29540                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29541                f.itemCls||this.itemCls||''  //5
29542        ], true).getPrevSibling());
29543     },
29544
29545     // private
29546     renderComponent : function(c){
29547         c.render(c.isLayout ? this.el : this.el.createChild());    
29548     },
29549     /**
29550      * Adds a object form elements (using the xtype property as the factory method.)
29551      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29552      * @param {Object} config 
29553      */
29554     addxtype : function(o)
29555     {
29556         // create the lement.
29557         o.form = this.form;
29558         var fe = Roo.factory(o, Roo.form);
29559         this.form.allItems.push(fe);
29560         this.stack.push(fe);
29561         
29562         if (fe.isFormField) {
29563             this.form.items.add(fe);
29564         }
29565          
29566         return fe;
29567     }
29568 });
29569
29570 /**
29571  * @class Roo.form.Column
29572  * @extends Roo.form.Layout
29573  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29574  * @constructor
29575  * @param {Object} config Configuration options
29576  */
29577 Roo.form.Column = function(config){
29578     Roo.form.Column.superclass.constructor.call(this, config);
29579 };
29580
29581 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29582     /**
29583      * @cfg {Number/String} width
29584      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29585      */
29586     /**
29587      * @cfg {String/Object} autoCreate
29588      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29589      */
29590
29591     // private
29592     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29593
29594     // private
29595     onRender : function(ct, position){
29596         Roo.form.Column.superclass.onRender.call(this, ct, position);
29597         if(this.width){
29598             this.el.setWidth(this.width);
29599         }
29600     }
29601 });
29602
29603
29604 /**
29605  * @class Roo.form.Row
29606  * @extends Roo.form.Layout
29607  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29608  * @constructor
29609  * @param {Object} config Configuration options
29610  */
29611
29612  
29613 Roo.form.Row = function(config){
29614     Roo.form.Row.superclass.constructor.call(this, config);
29615 };
29616  
29617 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29618       /**
29619      * @cfg {Number/String} width
29620      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29621      */
29622     /**
29623      * @cfg {Number/String} height
29624      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29625      */
29626     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29627     
29628     padWidth : 20,
29629     // private
29630     onRender : function(ct, position){
29631         //console.log('row render');
29632         if(!this.rowTpl){
29633             var t = new Roo.Template(
29634                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29635                     '<label for="{0}" style="{2}">{1}{4}</label>',
29636                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29637                     '</div>',
29638                 '</div>'
29639             );
29640             t.disableFormats = true;
29641             t.compile();
29642             Roo.form.Layout.prototype.rowTpl = t;
29643         }
29644         this.fieldTpl = this.rowTpl;
29645         
29646         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29647         var labelWidth = 100;
29648         
29649         if ((this.labelAlign != 'top')) {
29650             if (typeof this.labelWidth == 'number') {
29651                 labelWidth = this.labelWidth
29652             }
29653             this.padWidth =  20 + labelWidth;
29654             
29655         }
29656         
29657         Roo.form.Column.superclass.onRender.call(this, ct, position);
29658         if(this.width){
29659             this.el.setWidth(this.width);
29660         }
29661         if(this.height){
29662             this.el.setHeight(this.height);
29663         }
29664     },
29665     
29666     // private
29667     renderField : function(f){
29668         f.fieldEl = this.fieldTpl.append(this.el, [
29669                f.id, f.fieldLabel,
29670                f.labelStyle||this.labelStyle||'',
29671                this.elementStyle||'',
29672                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29673                f.itemCls||this.itemCls||'',
29674                f.width ? f.width + this.padWidth : 160 + this.padWidth
29675        ],true);
29676     }
29677 });
29678  
29679
29680 /**
29681  * @class Roo.form.FieldSet
29682  * @extends Roo.form.Layout
29683  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29684  * @constructor
29685  * @param {Object} config Configuration options
29686  */
29687 Roo.form.FieldSet = function(config){
29688     Roo.form.FieldSet.superclass.constructor.call(this, config);
29689 };
29690
29691 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29692     /**
29693      * @cfg {String} legend
29694      * The text to display as the legend for the FieldSet (defaults to '')
29695      */
29696     /**
29697      * @cfg {String/Object} autoCreate
29698      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29699      */
29700
29701     // private
29702     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29703
29704     // private
29705     onRender : function(ct, position){
29706         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29707         if(this.legend){
29708             this.setLegend(this.legend);
29709         }
29710     },
29711
29712     // private
29713     setLegend : function(text){
29714         if(this.rendered){
29715             this.el.child('legend').update(text);
29716         }
29717     }
29718 });/*
29719  * Based on:
29720  * Ext JS Library 1.1.1
29721  * Copyright(c) 2006-2007, Ext JS, LLC.
29722  *
29723  * Originally Released Under LGPL - original licence link has changed is not relivant.
29724  *
29725  * Fork - LGPL
29726  * <script type="text/javascript">
29727  */
29728 /**
29729  * @class Roo.form.VTypes
29730  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29731  * @singleton
29732  */
29733 Roo.form.VTypes = function(){
29734     // closure these in so they are only created once.
29735     var alpha = /^[a-zA-Z_]+$/;
29736     var alphanum = /^[a-zA-Z0-9_]+$/;
29737     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29738     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29739
29740     // All these messages and functions are configurable
29741     return {
29742         /**
29743          * The function used to validate email addresses
29744          * @param {String} value The email address
29745          */
29746         'email' : function(v){
29747             return email.test(v);
29748         },
29749         /**
29750          * The error text to display when the email validation function returns false
29751          * @type String
29752          */
29753         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29754         /**
29755          * The keystroke filter mask to be applied on email input
29756          * @type RegExp
29757          */
29758         'emailMask' : /[a-z0-9_\.\-@]/i,
29759
29760         /**
29761          * The function used to validate URLs
29762          * @param {String} value The URL
29763          */
29764         'url' : function(v){
29765             return url.test(v);
29766         },
29767         /**
29768          * The error text to display when the url validation function returns false
29769          * @type String
29770          */
29771         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29772         
29773         /**
29774          * The function used to validate alpha values
29775          * @param {String} value The value
29776          */
29777         'alpha' : function(v){
29778             return alpha.test(v);
29779         },
29780         /**
29781          * The error text to display when the alpha validation function returns false
29782          * @type String
29783          */
29784         'alphaText' : 'This field should only contain letters and _',
29785         /**
29786          * The keystroke filter mask to be applied on alpha input
29787          * @type RegExp
29788          */
29789         'alphaMask' : /[a-z_]/i,
29790
29791         /**
29792          * The function used to validate alphanumeric values
29793          * @param {String} value The value
29794          */
29795         'alphanum' : function(v){
29796             return alphanum.test(v);
29797         },
29798         /**
29799          * The error text to display when the alphanumeric validation function returns false
29800          * @type String
29801          */
29802         'alphanumText' : 'This field should only contain letters, numbers and _',
29803         /**
29804          * The keystroke filter mask to be applied on alphanumeric input
29805          * @type RegExp
29806          */
29807         'alphanumMask' : /[a-z0-9_]/i
29808     };
29809 }();//<script type="text/javascript">
29810
29811 /**
29812  * @class Roo.form.FCKeditor
29813  * @extends Roo.form.TextArea
29814  * Wrapper around the FCKEditor http://www.fckeditor.net
29815  * @constructor
29816  * Creates a new FCKeditor
29817  * @param {Object} config Configuration options
29818  */
29819 Roo.form.FCKeditor = function(config){
29820     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29821     this.addEvents({
29822          /**
29823          * @event editorinit
29824          * Fired when the editor is initialized - you can add extra handlers here..
29825          * @param {FCKeditor} this
29826          * @param {Object} the FCK object.
29827          */
29828         editorinit : true
29829     });
29830     
29831     
29832 };
29833 Roo.form.FCKeditor.editors = { };
29834 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29835 {
29836     //defaultAutoCreate : {
29837     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29838     //},
29839     // private
29840     /**
29841      * @cfg {Object} fck options - see fck manual for details.
29842      */
29843     fckconfig : false,
29844     
29845     /**
29846      * @cfg {Object} fck toolbar set (Basic or Default)
29847      */
29848     toolbarSet : 'Basic',
29849     /**
29850      * @cfg {Object} fck BasePath
29851      */ 
29852     basePath : '/fckeditor/',
29853     
29854     
29855     frame : false,
29856     
29857     value : '',
29858     
29859    
29860     onRender : function(ct, position)
29861     {
29862         if(!this.el){
29863             this.defaultAutoCreate = {
29864                 tag: "textarea",
29865                 style:"width:300px;height:60px;",
29866                 autocomplete: "off"
29867             };
29868         }
29869         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29870         /*
29871         if(this.grow){
29872             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29873             if(this.preventScrollbars){
29874                 this.el.setStyle("overflow", "hidden");
29875             }
29876             this.el.setHeight(this.growMin);
29877         }
29878         */
29879         //console.log('onrender' + this.getId() );
29880         Roo.form.FCKeditor.editors[this.getId()] = this;
29881          
29882
29883         this.replaceTextarea() ;
29884         
29885     },
29886     
29887     getEditor : function() {
29888         return this.fckEditor;
29889     },
29890     /**
29891      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29892      * @param {Mixed} value The value to set
29893      */
29894     
29895     
29896     setValue : function(value)
29897     {
29898         //console.log('setValue: ' + value);
29899         
29900         if(typeof(value) == 'undefined') { // not sure why this is happending...
29901             return;
29902         }
29903         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29904         
29905         //if(!this.el || !this.getEditor()) {
29906         //    this.value = value;
29907             //this.setValue.defer(100,this,[value]);    
29908         //    return;
29909         //} 
29910         
29911         if(!this.getEditor()) {
29912             return;
29913         }
29914         
29915         this.getEditor().SetData(value);
29916         
29917         //
29918
29919     },
29920
29921     /**
29922      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29923      * @return {Mixed} value The field value
29924      */
29925     getValue : function()
29926     {
29927         
29928         if (this.frame && this.frame.dom.style.display == 'none') {
29929             return Roo.form.FCKeditor.superclass.getValue.call(this);
29930         }
29931         
29932         if(!this.el || !this.getEditor()) {
29933            
29934            // this.getValue.defer(100,this); 
29935             return this.value;
29936         }
29937        
29938         
29939         var value=this.getEditor().GetData();
29940         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29941         return Roo.form.FCKeditor.superclass.getValue.call(this);
29942         
29943
29944     },
29945
29946     /**
29947      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29948      * @return {Mixed} value The field value
29949      */
29950     getRawValue : function()
29951     {
29952         if (this.frame && this.frame.dom.style.display == 'none') {
29953             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29954         }
29955         
29956         if(!this.el || !this.getEditor()) {
29957             //this.getRawValue.defer(100,this); 
29958             return this.value;
29959             return;
29960         }
29961         
29962         
29963         
29964         var value=this.getEditor().GetData();
29965         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29966         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29967          
29968     },
29969     
29970     setSize : function(w,h) {
29971         
29972         
29973         
29974         //if (this.frame && this.frame.dom.style.display == 'none') {
29975         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29976         //    return;
29977         //}
29978         //if(!this.el || !this.getEditor()) {
29979         //    this.setSize.defer(100,this, [w,h]); 
29980         //    return;
29981         //}
29982         
29983         
29984         
29985         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29986         
29987         this.frame.dom.setAttribute('width', w);
29988         this.frame.dom.setAttribute('height', h);
29989         this.frame.setSize(w,h);
29990         
29991     },
29992     
29993     toggleSourceEdit : function(value) {
29994         
29995       
29996          
29997         this.el.dom.style.display = value ? '' : 'none';
29998         this.frame.dom.style.display = value ?  'none' : '';
29999         
30000     },
30001     
30002     
30003     focus: function(tag)
30004     {
30005         if (this.frame.dom.style.display == 'none') {
30006             return Roo.form.FCKeditor.superclass.focus.call(this);
30007         }
30008         if(!this.el || !this.getEditor()) {
30009             this.focus.defer(100,this, [tag]); 
30010             return;
30011         }
30012         
30013         
30014         
30015         
30016         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
30017         this.getEditor().Focus();
30018         if (tgs.length) {
30019             if (!this.getEditor().Selection.GetSelection()) {
30020                 this.focus.defer(100,this, [tag]); 
30021                 return;
30022             }
30023             
30024             
30025             var r = this.getEditor().EditorDocument.createRange();
30026             r.setStart(tgs[0],0);
30027             r.setEnd(tgs[0],0);
30028             this.getEditor().Selection.GetSelection().removeAllRanges();
30029             this.getEditor().Selection.GetSelection().addRange(r);
30030             this.getEditor().Focus();
30031         }
30032         
30033     },
30034     
30035     
30036     
30037     replaceTextarea : function()
30038     {
30039         if ( document.getElementById( this.getId() + '___Frame' ) )
30040             return ;
30041         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
30042         //{
30043             // We must check the elements firstly using the Id and then the name.
30044         var oTextarea = document.getElementById( this.getId() );
30045         
30046         var colElementsByName = document.getElementsByName( this.getId() ) ;
30047          
30048         oTextarea.style.display = 'none' ;
30049
30050         if ( oTextarea.tabIndex ) {            
30051             this.TabIndex = oTextarea.tabIndex ;
30052         }
30053         
30054         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
30055         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
30056         this.frame = Roo.get(this.getId() + '___Frame')
30057     },
30058     
30059     _getConfigHtml : function()
30060     {
30061         var sConfig = '' ;
30062
30063         for ( var o in this.fckconfig ) {
30064             sConfig += sConfig.length > 0  ? '&amp;' : '';
30065             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
30066         }
30067
30068         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
30069     },
30070     
30071     
30072     _getIFrameHtml : function()
30073     {
30074         var sFile = 'fckeditor.html' ;
30075         /* no idea what this is about..
30076         try
30077         {
30078             if ( (/fcksource=true/i).test( window.top.location.search ) )
30079                 sFile = 'fckeditor.original.html' ;
30080         }
30081         catch (e) { 
30082         */
30083
30084         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
30085         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
30086         
30087         
30088         var html = '<iframe id="' + this.getId() +
30089             '___Frame" src="' + sLink +
30090             '" width="' + this.width +
30091             '" height="' + this.height + '"' +
30092             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
30093             ' frameborder="0" scrolling="no"></iframe>' ;
30094
30095         return html ;
30096     },
30097     
30098     _insertHtmlBefore : function( html, element )
30099     {
30100         if ( element.insertAdjacentHTML )       {
30101             // IE
30102             element.insertAdjacentHTML( 'beforeBegin', html ) ;
30103         } else { // Gecko
30104             var oRange = document.createRange() ;
30105             oRange.setStartBefore( element ) ;
30106             var oFragment = oRange.createContextualFragment( html );
30107             element.parentNode.insertBefore( oFragment, element ) ;
30108         }
30109     }
30110     
30111     
30112   
30113     
30114     
30115     
30116     
30117
30118 });
30119
30120 //Roo.reg('fckeditor', Roo.form.FCKeditor);
30121
30122 function FCKeditor_OnComplete(editorInstance){
30123     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
30124     f.fckEditor = editorInstance;
30125     //console.log("loaded");
30126     f.fireEvent('editorinit', f, editorInstance);
30127
30128   
30129
30130  
30131
30132
30133
30134
30135
30136
30137
30138
30139
30140
30141
30142
30143
30144
30145
30146 //<script type="text/javascript">
30147 /**
30148  * @class Roo.form.GridField
30149  * @extends Roo.form.Field
30150  * Embed a grid (or editable grid into a form)
30151  * STATUS ALPHA
30152  * 
30153  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
30154  * it needs 
30155  * xgrid.store = Roo.data.Store
30156  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
30157  * xgrid.store.reader = Roo.data.JsonReader 
30158  * 
30159  * 
30160  * @constructor
30161  * Creates a new GridField
30162  * @param {Object} config Configuration options
30163  */
30164 Roo.form.GridField = function(config){
30165     Roo.form.GridField.superclass.constructor.call(this, config);
30166      
30167 };
30168
30169 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
30170     /**
30171      * @cfg {Number} width  - used to restrict width of grid..
30172      */
30173     width : 100,
30174     /**
30175      * @cfg {Number} height - used to restrict height of grid..
30176      */
30177     height : 50,
30178      /**
30179      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
30180          * 
30181          *}
30182      */
30183     xgrid : false, 
30184     /**
30185      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30186      * {tag: "input", type: "checkbox", autocomplete: "off"})
30187      */
30188    // defaultAutoCreate : { tag: 'div' },
30189     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30190     /**
30191      * @cfg {String} addTitle Text to include for adding a title.
30192      */
30193     addTitle : false,
30194     //
30195     onResize : function(){
30196         Roo.form.Field.superclass.onResize.apply(this, arguments);
30197     },
30198
30199     initEvents : function(){
30200         // Roo.form.Checkbox.superclass.initEvents.call(this);
30201         // has no events...
30202        
30203     },
30204
30205
30206     getResizeEl : function(){
30207         return this.wrap;
30208     },
30209
30210     getPositionEl : function(){
30211         return this.wrap;
30212     },
30213
30214     // private
30215     onRender : function(ct, position){
30216         
30217         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
30218         var style = this.style;
30219         delete this.style;
30220         
30221         Roo.form.GridField.superclass.onRender.call(this, ct, position);
30222         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
30223         this.viewEl = this.wrap.createChild({ tag: 'div' });
30224         if (style) {
30225             this.viewEl.applyStyles(style);
30226         }
30227         if (this.width) {
30228             this.viewEl.setWidth(this.width);
30229         }
30230         if (this.height) {
30231             this.viewEl.setHeight(this.height);
30232         }
30233         //if(this.inputValue !== undefined){
30234         //this.setValue(this.value);
30235         
30236         
30237         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
30238         
30239         
30240         this.grid.render();
30241         this.grid.getDataSource().on('remove', this.refreshValue, this);
30242         this.grid.getDataSource().on('update', this.refreshValue, this);
30243         this.grid.on('afteredit', this.refreshValue, this);
30244  
30245     },
30246      
30247     
30248     /**
30249      * Sets the value of the item. 
30250      * @param {String} either an object  or a string..
30251      */
30252     setValue : function(v){
30253         //this.value = v;
30254         v = v || []; // empty set..
30255         // this does not seem smart - it really only affects memoryproxy grids..
30256         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
30257             var ds = this.grid.getDataSource();
30258             // assumes a json reader..
30259             var data = {}
30260             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
30261             ds.loadData( data);
30262         }
30263         // clear selection so it does not get stale.
30264         if (this.grid.sm) { 
30265             this.grid.sm.clearSelections();
30266         }
30267         
30268         Roo.form.GridField.superclass.setValue.call(this, v);
30269         this.refreshValue();
30270         // should load data in the grid really....
30271     },
30272     
30273     // private
30274     refreshValue: function() {
30275          var val = [];
30276         this.grid.getDataSource().each(function(r) {
30277             val.push(r.data);
30278         });
30279         this.el.dom.value = Roo.encode(val);
30280     }
30281     
30282      
30283     
30284     
30285 });/*
30286  * Based on:
30287  * Ext JS Library 1.1.1
30288  * Copyright(c) 2006-2007, Ext JS, LLC.
30289  *
30290  * Originally Released Under LGPL - original licence link has changed is not relivant.
30291  *
30292  * Fork - LGPL
30293  * <script type="text/javascript">
30294  */
30295 /**
30296  * @class Roo.form.DisplayField
30297  * @extends Roo.form.Field
30298  * A generic Field to display non-editable data.
30299  * @constructor
30300  * Creates a new Display Field item.
30301  * @param {Object} config Configuration options
30302  */
30303 Roo.form.DisplayField = function(config){
30304     Roo.form.DisplayField.superclass.constructor.call(this, config);
30305     
30306 };
30307
30308 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
30309     inputType:      'hidden',
30310     allowBlank:     true,
30311     readOnly:         true,
30312     
30313  
30314     /**
30315      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30316      */
30317     focusClass : undefined,
30318     /**
30319      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30320      */
30321     fieldClass: 'x-form-field',
30322     
30323      /**
30324      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
30325      */
30326     valueRenderer: undefined,
30327     
30328     width: 100,
30329     /**
30330      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30331      * {tag: "input", type: "checkbox", autocomplete: "off"})
30332      */
30333      
30334  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
30335
30336     onResize : function(){
30337         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
30338         
30339     },
30340
30341     initEvents : function(){
30342         // Roo.form.Checkbox.superclass.initEvents.call(this);
30343         // has no events...
30344        
30345     },
30346
30347
30348     getResizeEl : function(){
30349         return this.wrap;
30350     },
30351
30352     getPositionEl : function(){
30353         return this.wrap;
30354     },
30355
30356     // private
30357     onRender : function(ct, position){
30358         
30359         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
30360         //if(this.inputValue !== undefined){
30361         this.wrap = this.el.wrap();
30362         
30363         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
30364         
30365         if (this.bodyStyle) {
30366             this.viewEl.applyStyles(this.bodyStyle);
30367         }
30368         //this.viewEl.setStyle('padding', '2px');
30369         
30370         this.setValue(this.value);
30371         
30372     },
30373 /*
30374     // private
30375     initValue : Roo.emptyFn,
30376
30377   */
30378
30379         // private
30380     onClick : function(){
30381         
30382     },
30383
30384     /**
30385      * Sets the checked state of the checkbox.
30386      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
30387      */
30388     setValue : function(v){
30389         this.value = v;
30390         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
30391         // this might be called before we have a dom element..
30392         if (!this.viewEl) {
30393             return;
30394         }
30395         this.viewEl.dom.innerHTML = html;
30396         Roo.form.DisplayField.superclass.setValue.call(this, v);
30397
30398     }
30399 });/*
30400  * 
30401  * Licence- LGPL
30402  * 
30403  */
30404
30405 /**
30406  * @class Roo.form.DayPicker
30407  * @extends Roo.form.Field
30408  * A Day picker show [M] [T] [W] ....
30409  * @constructor
30410  * Creates a new Day Picker
30411  * @param {Object} config Configuration options
30412  */
30413 Roo.form.DayPicker= function(config){
30414     Roo.form.DayPicker.superclass.constructor.call(this, config);
30415      
30416 };
30417
30418 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30419     /**
30420      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30421      */
30422     focusClass : undefined,
30423     /**
30424      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30425      */
30426     fieldClass: "x-form-field",
30427    
30428     /**
30429      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30430      * {tag: "input", type: "checkbox", autocomplete: "off"})
30431      */
30432     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30433     
30434    
30435     actionMode : 'viewEl', 
30436     //
30437     // private
30438  
30439     inputType : 'hidden',
30440     
30441      
30442     inputElement: false, // real input element?
30443     basedOn: false, // ????
30444     
30445     isFormField: true, // not sure where this is needed!!!!
30446
30447     onResize : function(){
30448         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30449         if(!this.boxLabel){
30450             this.el.alignTo(this.wrap, 'c-c');
30451         }
30452     },
30453
30454     initEvents : function(){
30455         Roo.form.Checkbox.superclass.initEvents.call(this);
30456         this.el.on("click", this.onClick,  this);
30457         this.el.on("change", this.onClick,  this);
30458     },
30459
30460
30461     getResizeEl : function(){
30462         return this.wrap;
30463     },
30464
30465     getPositionEl : function(){
30466         return this.wrap;
30467     },
30468
30469     
30470     // private
30471     onRender : function(ct, position){
30472         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30473        
30474         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30475         
30476         var r1 = '<table><tr>';
30477         var r2 = '<tr class="x-form-daypick-icons">';
30478         for (var i=0; i < 7; i++) {
30479             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30480             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30481         }
30482         
30483         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30484         viewEl.select('img').on('click', this.onClick, this);
30485         this.viewEl = viewEl;   
30486         
30487         
30488         // this will not work on Chrome!!!
30489         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30490         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30491         
30492         
30493           
30494
30495     },
30496
30497     // private
30498     initValue : Roo.emptyFn,
30499
30500     /**
30501      * Returns the checked state of the checkbox.
30502      * @return {Boolean} True if checked, else false
30503      */
30504     getValue : function(){
30505         return this.el.dom.value;
30506         
30507     },
30508
30509         // private
30510     onClick : function(e){ 
30511         //this.setChecked(!this.checked);
30512         Roo.get(e.target).toggleClass('x-menu-item-checked');
30513         this.refreshValue();
30514         //if(this.el.dom.checked != this.checked){
30515         //    this.setValue(this.el.dom.checked);
30516        // }
30517     },
30518     
30519     // private
30520     refreshValue : function()
30521     {
30522         var val = '';
30523         this.viewEl.select('img',true).each(function(e,i,n)  {
30524             val += e.is(".x-menu-item-checked") ? String(n) : '';
30525         });
30526         this.setValue(val, true);
30527     },
30528
30529     /**
30530      * Sets the checked state of the checkbox.
30531      * On is always based on a string comparison between inputValue and the param.
30532      * @param {Boolean/String} value - the value to set 
30533      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30534      */
30535     setValue : function(v,suppressEvent){
30536         if (!this.el.dom) {
30537             return;
30538         }
30539         var old = this.el.dom.value ;
30540         this.el.dom.value = v;
30541         if (suppressEvent) {
30542             return ;
30543         }
30544          
30545         // update display..
30546         this.viewEl.select('img',true).each(function(e,i,n)  {
30547             
30548             var on = e.is(".x-menu-item-checked");
30549             var newv = v.indexOf(String(n)) > -1;
30550             if (on != newv) {
30551                 e.toggleClass('x-menu-item-checked');
30552             }
30553             
30554         });
30555         
30556         
30557         this.fireEvent('change', this, v, old);
30558         
30559         
30560     },
30561    
30562     // handle setting of hidden value by some other method!!?!?
30563     setFromHidden: function()
30564     {
30565         if(!this.el){
30566             return;
30567         }
30568         //console.log("SET FROM HIDDEN");
30569         //alert('setFrom hidden');
30570         this.setValue(this.el.dom.value);
30571     },
30572     
30573     onDestroy : function()
30574     {
30575         if(this.viewEl){
30576             Roo.get(this.viewEl).remove();
30577         }
30578          
30579         Roo.form.DayPicker.superclass.onDestroy.call(this);
30580     }
30581
30582 });/*
30583  * RooJS Library 1.1.1
30584  * Copyright(c) 2008-2011  Alan Knowles
30585  *
30586  * License - LGPL
30587  */
30588  
30589
30590 /**
30591  * @class Roo.form.ComboCheck
30592  * @extends Roo.form.ComboBox
30593  * A combobox for multiple select items.
30594  *
30595  * FIXME - could do with a reset button..
30596  * 
30597  * @constructor
30598  * Create a new ComboCheck
30599  * @param {Object} config Configuration options
30600  */
30601 Roo.form.ComboCheck = function(config){
30602     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30603     // should verify some data...
30604     // like
30605     // hiddenName = required..
30606     // displayField = required
30607     // valudField == required
30608     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30609     var _t = this;
30610     Roo.each(req, function(e) {
30611         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30612             throw "Roo.form.ComboCheck : missing value for: " + e;
30613         }
30614     });
30615     
30616     
30617 };
30618
30619 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30620      
30621      
30622     editable : false,
30623      
30624     selectedClass: 'x-menu-item-checked', 
30625     
30626     // private
30627     onRender : function(ct, position){
30628         var _t = this;
30629         
30630         
30631         
30632         if(!this.tpl){
30633             var cls = 'x-combo-list';
30634
30635             
30636             this.tpl =  new Roo.Template({
30637                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30638                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30639                    '<span>{' + this.displayField + '}</span>' +
30640                     '</div>' 
30641                 
30642             });
30643         }
30644  
30645         
30646         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30647         this.view.singleSelect = false;
30648         this.view.multiSelect = true;
30649         this.view.toggleSelect = true;
30650         this.pageTb.add(new Roo.Toolbar.Fill(), {
30651             
30652             text: 'Done',
30653             handler: function()
30654             {
30655                 _t.collapse();
30656             }
30657         });
30658     },
30659     
30660     onViewOver : function(e, t){
30661         // do nothing...
30662         return;
30663         
30664     },
30665     
30666     onViewClick : function(doFocus,index){
30667         return;
30668         
30669     },
30670     select: function () {
30671         //Roo.log("SELECT CALLED");
30672     },
30673      
30674     selectByValue : function(xv, scrollIntoView){
30675         var ar = this.getValueArray();
30676         var sels = [];
30677         
30678         Roo.each(ar, function(v) {
30679             if(v === undefined || v === null){
30680                 return;
30681             }
30682             var r = this.findRecord(this.valueField, v);
30683             if(r){
30684                 sels.push(this.store.indexOf(r))
30685                 
30686             }
30687         },this);
30688         this.view.select(sels);
30689         return false;
30690     },
30691     
30692     
30693     
30694     onSelect : function(record, index){
30695        // Roo.log("onselect Called");
30696        // this is only called by the clear button now..
30697         this.view.clearSelections();
30698         this.setValue('[]');
30699         if (this.value != this.valueBefore) {
30700             this.fireEvent('change', this, this.value, this.valueBefore);
30701             this.valueBefore = this.value;
30702         }
30703     },
30704     getValueArray : function()
30705     {
30706         var ar = [] ;
30707         
30708         try {
30709             //Roo.log(this.value);
30710             if (typeof(this.value) == 'undefined') {
30711                 return [];
30712             }
30713             var ar = Roo.decode(this.value);
30714             return  ar instanceof Array ? ar : []; //?? valid?
30715             
30716         } catch(e) {
30717             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30718             return [];
30719         }
30720          
30721     },
30722     expand : function ()
30723     {
30724         
30725         Roo.form.ComboCheck.superclass.expand.call(this);
30726         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
30727         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
30728         
30729
30730     },
30731     
30732     collapse : function(){
30733         Roo.form.ComboCheck.superclass.collapse.call(this);
30734         var sl = this.view.getSelectedIndexes();
30735         var st = this.store;
30736         var nv = [];
30737         var tv = [];
30738         var r;
30739         Roo.each(sl, function(i) {
30740             r = st.getAt(i);
30741             nv.push(r.get(this.valueField));
30742         },this);
30743         this.setValue(Roo.encode(nv));
30744         if (this.value != this.valueBefore) {
30745
30746             this.fireEvent('change', this, this.value, this.valueBefore);
30747             this.valueBefore = this.value;
30748         }
30749         
30750     },
30751     
30752     setValue : function(v){
30753         // Roo.log(v);
30754         this.value = v;
30755         
30756         var vals = this.getValueArray();
30757         var tv = [];
30758         Roo.each(vals, function(k) {
30759             var r = this.findRecord(this.valueField, k);
30760             if(r){
30761                 tv.push(r.data[this.displayField]);
30762             }else if(this.valueNotFoundText !== undefined){
30763                 tv.push( this.valueNotFoundText );
30764             }
30765         },this);
30766        // Roo.log(tv);
30767         
30768         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30769         this.hiddenField.value = v;
30770         this.value = v;
30771     }
30772     
30773 });/*
30774  * Based on:
30775  * Ext JS Library 1.1.1
30776  * Copyright(c) 2006-2007, Ext JS, LLC.
30777  *
30778  * Originally Released Under LGPL - original licence link has changed is not relivant.
30779  *
30780  * Fork - LGPL
30781  * <script type="text/javascript">
30782  */
30783  
30784 /**
30785  * @class Roo.form.Signature
30786  * @extends Roo.form.Field
30787  * Signature field.  
30788  * @constructor
30789  * 
30790  * @param {Object} config Configuration options
30791  */
30792
30793 Roo.form.Signature = function(config){
30794     Roo.form.Signature.superclass.constructor.call(this, config);
30795     
30796     this.addEvents({// not in used??
30797          /**
30798          * @event confirm
30799          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
30800              * @param {Roo.form.Signature} combo This combo box
30801              */
30802         'confirm' : true,
30803         /**
30804          * @event reset
30805          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
30806              * @param {Roo.form.ComboBox} combo This combo box
30807              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
30808              */
30809         'reset' : true
30810     });
30811 };
30812
30813 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
30814     /**
30815      * @cfg {Object} labels Label to use when rendering a form.
30816      * defaults to 
30817      * labels : { 
30818      *      clear : "Clear",
30819      *      confirm : "Confirm"
30820      *  }
30821      */
30822     labels : { 
30823         clear : "Clear",
30824         confirm : "Confirm"
30825     },
30826     /**
30827      * @cfg {Number} width The signature panel width (defaults to 300)
30828      */
30829     width: 300,
30830     /**
30831      * @cfg {Number} height The signature panel height (defaults to 100)
30832      */
30833     height : 100,
30834     /**
30835      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
30836      */
30837     allowBlank : false,
30838     
30839     //private
30840     // {Object} signPanel The signature SVG panel element (defaults to {})
30841     signPanel : {},
30842     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
30843     isMouseDown : false,
30844     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
30845     isConfirmed : false,
30846     // {String} signatureTmp SVG mapping string (defaults to empty string)
30847     signatureTmp : '',
30848     
30849     
30850     defaultAutoCreate : { // modified by initCompnoent..
30851         tag: "input",
30852         type:"hidden"
30853     },
30854
30855     // private
30856     onRender : function(ct, position){
30857         
30858         Roo.form.Signature.superclass.onRender.call(this, ct, position);
30859         
30860         this.wrap = this.el.wrap({
30861             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
30862         });
30863         
30864         this.createToolbar(this);
30865         this.signPanel = this.wrap.createChild({
30866                 tag: 'div',
30867                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
30868             }, this.el
30869         );
30870             
30871         this.svgID = Roo.id();
30872         this.svgEl = this.signPanel.createChild({
30873               xmlns : 'http://www.w3.org/2000/svg',
30874               tag : 'svg',
30875               id : this.svgID + "-svg",
30876               width: this.width,
30877               height: this.height,
30878               viewBox: '0 0 '+this.width+' '+this.height,
30879               cn : [
30880                 {
30881                     tag: "rect",
30882                     id: this.svgID + "-svg-r",
30883                     width: this.width,
30884                     height: this.height,
30885                     fill: "#ffa"
30886                 },
30887                 {
30888                     tag: "line",
30889                     id: this.svgID + "-svg-l",
30890                     x1: "0", // start
30891                     y1: (this.height*0.8), // start set the line in 80% of height
30892                     x2: this.width, // end
30893                     y2: (this.height*0.8), // end set the line in 80% of height
30894                     'stroke': "#666",
30895                     'stroke-width': "1",
30896                     'stroke-dasharray': "3",
30897                     'shape-rendering': "crispEdges",
30898                     'pointer-events': "none"
30899                 },
30900                 {
30901                     tag: "path",
30902                     id: this.svgID + "-svg-p",
30903                     'stroke': "navy",
30904                     'stroke-width': "3",
30905                     'fill': "none",
30906                     'pointer-events': 'none'
30907                 }
30908               ]
30909         });
30910         this.createSVG();
30911         this.svgBox = this.svgEl.dom.getScreenCTM();
30912     },
30913     createSVG : function(){ 
30914         var svg = this.signPanel;
30915         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
30916         var t = this;
30917
30918         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
30919         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
30920         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
30921         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
30922         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
30923         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
30924         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
30925         
30926     },
30927     isTouchEvent : function(e){
30928         return e.type.match(/^touch/);
30929     },
30930     getCoords : function (e) {
30931         var pt    = this.svgEl.dom.createSVGPoint();
30932         pt.x = e.clientX; 
30933         pt.y = e.clientY;
30934         if (this.isTouchEvent(e)) {
30935             pt.x =  e.targetTouches[0].clientX 
30936             pt.y = e.targetTouches[0].clientY;
30937         }
30938         var a = this.svgEl.dom.getScreenCTM();
30939         var b = a.inverse();
30940         var mx = pt.matrixTransform(b);
30941         return mx.x + ',' + mx.y;
30942     },
30943     //mouse event headler 
30944     down : function (e) {
30945         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
30946         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
30947         
30948         this.isMouseDown = true;
30949         
30950         e.preventDefault();
30951     },
30952     move : function (e) {
30953         if (this.isMouseDown) {
30954             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
30955             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
30956         }
30957         
30958         e.preventDefault();
30959     },
30960     up : function (e) {
30961         this.isMouseDown = false;
30962         var sp = this.signatureTmp.split(' ');
30963         
30964         if(sp.length > 1){
30965             if(!sp[sp.length-2].match(/^L/)){
30966                 sp.pop();
30967                 sp.pop();
30968                 sp.push("");
30969                 this.signatureTmp = sp.join(" ");
30970             }
30971         }
30972         if(this.getValue() != this.signatureTmp){
30973             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
30974             this.isConfirmed = false;
30975         }
30976         e.preventDefault();
30977     },
30978     
30979     /**
30980      * Protected method that will not generally be called directly. It
30981      * is called when the editor creates its toolbar. Override this method if you need to
30982      * add custom toolbar buttons.
30983      * @param {HtmlEditor} editor
30984      */
30985     createToolbar : function(editor){
30986          function btn(id, toggle, handler){
30987             var xid = fid + '-'+ id ;
30988             return {
30989                 id : xid,
30990                 cmd : id,
30991                 cls : 'x-btn-icon x-edit-'+id,
30992                 enableToggle:toggle !== false,
30993                 scope: editor, // was editor...
30994                 handler:handler||editor.relayBtnCmd,
30995                 clickEvent:'mousedown',
30996                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
30997                 tabIndex:-1
30998             };
30999         }
31000         
31001         
31002         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
31003         this.tb = tb;
31004         this.tb.add(
31005            {
31006                 cls : ' x-signature-btn x-signature-'+id,
31007                 scope: editor, // was editor...
31008                 handler: this.reset,
31009                 clickEvent:'mousedown',
31010                 text: this.labels.clear
31011             },
31012             {
31013                  xtype : 'Fill',
31014                  xns: Roo.Toolbar
31015             }, 
31016             {
31017                 cls : '  x-signature-btn x-signature-'+id,
31018                 scope: editor, // was editor...
31019                 handler: this.confirmHandler,
31020                 clickEvent:'mousedown',
31021                 text: this.labels.confirm
31022             }
31023         );
31024     
31025     },
31026     //public
31027     /**
31028      * when user is clicked confirm then show this image.....
31029      * 
31030      * @return {String} Image Data URI
31031      */
31032     getImageDataURI : function(){
31033         var svg = this.svgEl.dom.parentNode.innerHTML;
31034         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
31035         return src; 
31036     },
31037     /**
31038      * 
31039      * @return {Boolean} this.isConfirmed
31040      */
31041     getConfirmed : function(){
31042         return this.isConfirmed;
31043     },
31044     /**
31045      * 
31046      * @return {Number} this.width
31047      */
31048     getWidth : function(){
31049         return this.width;
31050     },
31051     /**
31052      * 
31053      * @return {Number} this.height
31054      */
31055     getHeight : function(){
31056         return this.height;
31057     },
31058     // private
31059     getSignature : function(){
31060         return this.signatureTmp;
31061     },
31062     // private
31063     reset : function(){
31064         this.signatureTmp = '';
31065         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31066         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
31067         this.isConfirmed = false;
31068         Roo.form.Signature.superclass.reset.call(this);
31069     },
31070     setSignature : function(s){
31071         this.signatureTmp = s;
31072         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
31073         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
31074         this.setValue(s);
31075         this.isConfirmed = false;
31076         Roo.form.Signature.superclass.reset.call(this);
31077     }, 
31078     test : function(){
31079 //        Roo.log(this.signPanel.dom.contentWindow.up())
31080     },
31081     //private
31082     setConfirmed : function(){
31083         
31084         
31085         
31086 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
31087     },
31088     // private
31089     confirmHandler : function(){
31090         if(!this.getSignature()){
31091             return;
31092         }
31093         
31094         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
31095         this.setValue(this.getSignature());
31096         this.isConfirmed = true;
31097         
31098         this.fireEvent('confirm', this);
31099     },
31100     // private
31101     // Subclasses should provide the validation implementation by overriding this
31102     validateValue : function(value){
31103         if(this.allowBlank){
31104             return true;
31105         }
31106         
31107         if(this.isConfirmed){
31108             return true;
31109         }
31110         return false;
31111     }
31112 });//<script type="text/javasscript">
31113  
31114
31115 /**
31116  * @class Roo.DDView
31117  * A DnD enabled version of Roo.View.
31118  * @param {Element/String} container The Element in which to create the View.
31119  * @param {String} tpl The template string used to create the markup for each element of the View
31120  * @param {Object} config The configuration properties. These include all the config options of
31121  * {@link Roo.View} plus some specific to this class.<br>
31122  * <p>
31123  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
31124  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
31125  * <p>
31126  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
31127 .x-view-drag-insert-above {
31128         border-top:1px dotted #3366cc;
31129 }
31130 .x-view-drag-insert-below {
31131         border-bottom:1px dotted #3366cc;
31132 }
31133 </code></pre>
31134  * 
31135  */
31136  
31137 Roo.DDView = function(container, tpl, config) {
31138     Roo.DDView.superclass.constructor.apply(this, arguments);
31139     this.getEl().setStyle("outline", "0px none");
31140     this.getEl().unselectable();
31141     if (this.dragGroup) {
31142                 this.setDraggable(this.dragGroup.split(","));
31143     }
31144     if (this.dropGroup) {
31145                 this.setDroppable(this.dropGroup.split(","));
31146     }
31147     if (this.deletable) {
31148         this.setDeletable();
31149     }
31150     this.isDirtyFlag = false;
31151         this.addEvents({
31152                 "drop" : true
31153         });
31154 };
31155
31156 Roo.extend(Roo.DDView, Roo.View, {
31157 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
31158 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
31159 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
31160 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
31161
31162         isFormField: true,
31163
31164         reset: Roo.emptyFn,
31165         
31166         clearInvalid: Roo.form.Field.prototype.clearInvalid,
31167
31168         validate: function() {
31169                 return true;
31170         },
31171         
31172         destroy: function() {
31173                 this.purgeListeners();
31174                 this.getEl.removeAllListeners();
31175                 this.getEl().remove();
31176                 if (this.dragZone) {
31177                         if (this.dragZone.destroy) {
31178                                 this.dragZone.destroy();
31179                         }
31180                 }
31181                 if (this.dropZone) {
31182                         if (this.dropZone.destroy) {
31183                                 this.dropZone.destroy();
31184                         }
31185                 }
31186         },
31187
31188 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
31189         getName: function() {
31190                 return this.name;
31191         },
31192
31193 /**     Loads the View from a JSON string representing the Records to put into the Store. */
31194         setValue: function(v) {
31195                 if (!this.store) {
31196                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
31197                 }
31198                 var data = {};
31199                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
31200                 this.store.proxy = new Roo.data.MemoryProxy(data);
31201                 this.store.load();
31202         },
31203
31204 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
31205         getValue: function() {
31206                 var result = '(';
31207                 this.store.each(function(rec) {
31208                         result += rec.id + ',';
31209                 });
31210                 return result.substr(0, result.length - 1) + ')';
31211         },
31212         
31213         getIds: function() {
31214                 var i = 0, result = new Array(this.store.getCount());
31215                 this.store.each(function(rec) {
31216                         result[i++] = rec.id;
31217                 });
31218                 return result;
31219         },
31220         
31221         isDirty: function() {
31222                 return this.isDirtyFlag;
31223         },
31224
31225 /**
31226  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
31227  *      whole Element becomes the target, and this causes the drop gesture to append.
31228  */
31229     getTargetFromEvent : function(e) {
31230                 var target = e.getTarget();
31231                 while ((target !== null) && (target.parentNode != this.el.dom)) {
31232                 target = target.parentNode;
31233                 }
31234                 if (!target) {
31235                         target = this.el.dom.lastChild || this.el.dom;
31236                 }
31237                 return target;
31238     },
31239
31240 /**
31241  *      Create the drag data which consists of an object which has the property "ddel" as
31242  *      the drag proxy element. 
31243  */
31244     getDragData : function(e) {
31245         var target = this.findItemFromChild(e.getTarget());
31246                 if(target) {
31247                         this.handleSelection(e);
31248                         var selNodes = this.getSelectedNodes();
31249             var dragData = {
31250                 source: this,
31251                 copy: this.copy || (this.allowCopy && e.ctrlKey),
31252                 nodes: selNodes,
31253                 records: []
31254                         };
31255                         var selectedIndices = this.getSelectedIndexes();
31256                         for (var i = 0; i < selectedIndices.length; i++) {
31257                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
31258                         }
31259                         if (selNodes.length == 1) {
31260                                 dragData.ddel = target.cloneNode(true); // the div element
31261                         } else {
31262                                 var div = document.createElement('div'); // create the multi element drag "ghost"
31263                                 div.className = 'multi-proxy';
31264                                 for (var i = 0, len = selNodes.length; i < len; i++) {
31265                                         div.appendChild(selNodes[i].cloneNode(true));
31266                                 }
31267                                 dragData.ddel = div;
31268                         }
31269             //console.log(dragData)
31270             //console.log(dragData.ddel.innerHTML)
31271                         return dragData;
31272                 }
31273         //console.log('nodragData')
31274                 return false;
31275     },
31276     
31277 /**     Specify to which ddGroup items in this DDView may be dragged. */
31278     setDraggable: function(ddGroup) {
31279         if (ddGroup instanceof Array) {
31280                 Roo.each(ddGroup, this.setDraggable, this);
31281                 return;
31282         }
31283         if (this.dragZone) {
31284                 this.dragZone.addToGroup(ddGroup);
31285         } else {
31286                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
31287                                 containerScroll: true,
31288                                 ddGroup: ddGroup 
31289
31290                         });
31291 //                      Draggability implies selection. DragZone's mousedown selects the element.
31292                         if (!this.multiSelect) { this.singleSelect = true; }
31293
31294 //                      Wire the DragZone's handlers up to methods in *this*
31295                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
31296                 }
31297     },
31298
31299 /**     Specify from which ddGroup this DDView accepts drops. */
31300     setDroppable: function(ddGroup) {
31301         if (ddGroup instanceof Array) {
31302                 Roo.each(ddGroup, this.setDroppable, this);
31303                 return;
31304         }
31305         if (this.dropZone) {
31306                 this.dropZone.addToGroup(ddGroup);
31307         } else {
31308                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
31309                                 containerScroll: true,
31310                                 ddGroup: ddGroup
31311                         });
31312
31313 //                      Wire the DropZone's handlers up to methods in *this*
31314                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
31315                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
31316                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
31317                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
31318                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
31319                 }
31320     },
31321
31322 /**     Decide whether to drop above or below a View node. */
31323     getDropPoint : function(e, n, dd){
31324         if (n == this.el.dom) { return "above"; }
31325                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
31326                 var c = t + (b - t) / 2;
31327                 var y = Roo.lib.Event.getPageY(e);
31328                 if(y <= c) {
31329                         return "above";
31330                 }else{
31331                         return "below";
31332                 }
31333     },
31334
31335     onNodeEnter : function(n, dd, e, data){
31336                 return false;
31337     },
31338     
31339     onNodeOver : function(n, dd, e, data){
31340                 var pt = this.getDropPoint(e, n, dd);
31341                 // set the insert point style on the target node
31342                 var dragElClass = this.dropNotAllowed;
31343                 if (pt) {
31344                         var targetElClass;
31345                         if (pt == "above"){
31346                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
31347                                 targetElClass = "x-view-drag-insert-above";
31348                         } else {
31349                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
31350                                 targetElClass = "x-view-drag-insert-below";
31351                         }
31352                         if (this.lastInsertClass != targetElClass){
31353                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
31354                                 this.lastInsertClass = targetElClass;
31355                         }
31356                 }
31357                 return dragElClass;
31358         },
31359
31360     onNodeOut : function(n, dd, e, data){
31361                 this.removeDropIndicators(n);
31362     },
31363
31364     onNodeDrop : function(n, dd, e, data){
31365         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
31366                 return false;
31367         }
31368         var pt = this.getDropPoint(e, n, dd);
31369                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
31370                 if (pt == "below") { insertAt++; }
31371                 for (var i = 0; i < data.records.length; i++) {
31372                         var r = data.records[i];
31373                         var dup = this.store.getById(r.id);
31374                         if (dup && (dd != this.dragZone)) {
31375                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
31376                         } else {
31377                                 if (data.copy) {
31378                                         this.store.insert(insertAt++, r.copy());
31379                                 } else {
31380                                         data.source.isDirtyFlag = true;
31381                                         r.store.remove(r);
31382                                         this.store.insert(insertAt++, r);
31383                                 }
31384                                 this.isDirtyFlag = true;
31385                         }
31386                 }
31387                 this.dragZone.cachedTarget = null;
31388                 return true;
31389     },
31390
31391     removeDropIndicators : function(n){
31392                 if(n){
31393                         Roo.fly(n).removeClass([
31394                                 "x-view-drag-insert-above",
31395                                 "x-view-drag-insert-below"]);
31396                         this.lastInsertClass = "_noclass";
31397                 }
31398     },
31399
31400 /**
31401  *      Utility method. Add a delete option to the DDView's context menu.
31402  *      @param {String} imageUrl The URL of the "delete" icon image.
31403  */
31404         setDeletable: function(imageUrl) {
31405                 if (!this.singleSelect && !this.multiSelect) {
31406                         this.singleSelect = true;
31407                 }
31408                 var c = this.getContextMenu();
31409                 this.contextMenu.on("itemclick", function(item) {
31410                         switch (item.id) {
31411                                 case "delete":
31412                                         this.remove(this.getSelectedIndexes());
31413                                         break;
31414                         }
31415                 }, this);
31416                 this.contextMenu.add({
31417                         icon: imageUrl,
31418                         id: "delete",
31419                         text: 'Delete'
31420                 });
31421         },
31422         
31423 /**     Return the context menu for this DDView. */
31424         getContextMenu: function() {
31425                 if (!this.contextMenu) {
31426 //                      Create the View's context menu
31427                         this.contextMenu = new Roo.menu.Menu({
31428                                 id: this.id + "-contextmenu"
31429                         });
31430                         this.el.on("contextmenu", this.showContextMenu, this);
31431                 }
31432                 return this.contextMenu;
31433         },
31434         
31435         disableContextMenu: function() {
31436                 if (this.contextMenu) {
31437                         this.el.un("contextmenu", this.showContextMenu, this);
31438                 }
31439         },
31440
31441         showContextMenu: function(e, item) {
31442         item = this.findItemFromChild(e.getTarget());
31443                 if (item) {
31444                         e.stopEvent();
31445                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
31446                         this.contextMenu.showAt(e.getXY());
31447             }
31448     },
31449
31450 /**
31451  *      Remove {@link Roo.data.Record}s at the specified indices.
31452  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
31453  */
31454     remove: function(selectedIndices) {
31455                 selectedIndices = [].concat(selectedIndices);
31456                 for (var i = 0; i < selectedIndices.length; i++) {
31457                         var rec = this.store.getAt(selectedIndices[i]);
31458                         this.store.remove(rec);
31459                 }
31460     },
31461
31462 /**
31463  *      Double click fires the event, but also, if this is draggable, and there is only one other
31464  *      related DropZone, it transfers the selected node.
31465  */
31466     onDblClick : function(e){
31467         var item = this.findItemFromChild(e.getTarget());
31468         if(item){
31469             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
31470                 return false;
31471             }
31472             if (this.dragGroup) {
31473                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
31474                     while (targets.indexOf(this.dropZone) > -1) {
31475                             targets.remove(this.dropZone);
31476                                 }
31477                     if (targets.length == 1) {
31478                                         this.dragZone.cachedTarget = null;
31479                         var el = Roo.get(targets[0].getEl());
31480                         var box = el.getBox(true);
31481                         targets[0].onNodeDrop(el.dom, {
31482                                 target: el.dom,
31483                                 xy: [box.x, box.y + box.height - 1]
31484                         }, null, this.getDragData(e));
31485                     }
31486                 }
31487         }
31488     },
31489     
31490     handleSelection: function(e) {
31491                 this.dragZone.cachedTarget = null;
31492         var item = this.findItemFromChild(e.getTarget());
31493         if (!item) {
31494                 this.clearSelections(true);
31495                 return;
31496         }
31497                 if (item && (this.multiSelect || this.singleSelect)){
31498                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
31499                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
31500                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
31501                                 this.unselect(item);
31502                         } else {
31503                                 this.select(item, this.multiSelect && e.ctrlKey);
31504                                 this.lastSelection = item;
31505                         }
31506                 }
31507     },
31508
31509     onItemClick : function(item, index, e){
31510                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
31511                         return false;
31512                 }
31513                 return true;
31514     },
31515
31516     unselect : function(nodeInfo, suppressEvent){
31517                 var node = this.getNode(nodeInfo);
31518                 if(node && this.isSelected(node)){
31519                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
31520                                 Roo.fly(node).removeClass(this.selectedClass);
31521                                 this.selections.remove(node);
31522                                 if(!suppressEvent){
31523                                         this.fireEvent("selectionchange", this, this.selections);
31524                                 }
31525                         }
31526                 }
31527     }
31528 });
31529 /*
31530  * Based on:
31531  * Ext JS Library 1.1.1
31532  * Copyright(c) 2006-2007, Ext JS, LLC.
31533  *
31534  * Originally Released Under LGPL - original licence link has changed is not relivant.
31535  *
31536  * Fork - LGPL
31537  * <script type="text/javascript">
31538  */
31539  
31540 /**
31541  * @class Roo.LayoutManager
31542  * @extends Roo.util.Observable
31543  * Base class for layout managers.
31544  */
31545 Roo.LayoutManager = function(container, config){
31546     Roo.LayoutManager.superclass.constructor.call(this);
31547     this.el = Roo.get(container);
31548     // ie scrollbar fix
31549     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31550         document.body.scroll = "no";
31551     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31552         this.el.position('relative');
31553     }
31554     this.id = this.el.id;
31555     this.el.addClass("x-layout-container");
31556     /** false to disable window resize monitoring @type Boolean */
31557     this.monitorWindowResize = true;
31558     this.regions = {};
31559     this.addEvents({
31560         /**
31561          * @event layout
31562          * Fires when a layout is performed. 
31563          * @param {Roo.LayoutManager} this
31564          */
31565         "layout" : true,
31566         /**
31567          * @event regionresized
31568          * Fires when the user resizes a region. 
31569          * @param {Roo.LayoutRegion} region The resized region
31570          * @param {Number} newSize The new size (width for east/west, height for north/south)
31571          */
31572         "regionresized" : true,
31573         /**
31574          * @event regioncollapsed
31575          * Fires when a region is collapsed. 
31576          * @param {Roo.LayoutRegion} region The collapsed region
31577          */
31578         "regioncollapsed" : true,
31579         /**
31580          * @event regionexpanded
31581          * Fires when a region is expanded.  
31582          * @param {Roo.LayoutRegion} region The expanded region
31583          */
31584         "regionexpanded" : true
31585     });
31586     this.updating = false;
31587     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31588 };
31589
31590 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
31591     /**
31592      * Returns true if this layout is currently being updated
31593      * @return {Boolean}
31594      */
31595     isUpdating : function(){
31596         return this.updating; 
31597     },
31598     
31599     /**
31600      * Suspend the LayoutManager from doing auto-layouts while
31601      * making multiple add or remove calls
31602      */
31603     beginUpdate : function(){
31604         this.updating = true;    
31605     },
31606     
31607     /**
31608      * Restore auto-layouts and optionally disable the manager from performing a layout
31609      * @param {Boolean} noLayout true to disable a layout update 
31610      */
31611     endUpdate : function(noLayout){
31612         this.updating = false;
31613         if(!noLayout){
31614             this.layout();
31615         }    
31616     },
31617     
31618     layout: function(){
31619         
31620     },
31621     
31622     onRegionResized : function(region, newSize){
31623         this.fireEvent("regionresized", region, newSize);
31624         this.layout();
31625     },
31626     
31627     onRegionCollapsed : function(region){
31628         this.fireEvent("regioncollapsed", region);
31629     },
31630     
31631     onRegionExpanded : function(region){
31632         this.fireEvent("regionexpanded", region);
31633     },
31634         
31635     /**
31636      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31637      * performs box-model adjustments.
31638      * @return {Object} The size as an object {width: (the width), height: (the height)}
31639      */
31640     getViewSize : function(){
31641         var size;
31642         if(this.el.dom != document.body){
31643             size = this.el.getSize();
31644         }else{
31645             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31646         }
31647         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31648         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31649         return size;
31650     },
31651     
31652     /**
31653      * Returns the Element this layout is bound to.
31654      * @return {Roo.Element}
31655      */
31656     getEl : function(){
31657         return this.el;
31658     },
31659     
31660     /**
31661      * Returns the specified region.
31662      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31663      * @return {Roo.LayoutRegion}
31664      */
31665     getRegion : function(target){
31666         return this.regions[target.toLowerCase()];
31667     },
31668     
31669     onWindowResize : function(){
31670         if(this.monitorWindowResize){
31671             this.layout();
31672         }
31673     }
31674 });/*
31675  * Based on:
31676  * Ext JS Library 1.1.1
31677  * Copyright(c) 2006-2007, Ext JS, LLC.
31678  *
31679  * Originally Released Under LGPL - original licence link has changed is not relivant.
31680  *
31681  * Fork - LGPL
31682  * <script type="text/javascript">
31683  */
31684 /**
31685  * @class Roo.BorderLayout
31686  * @extends Roo.LayoutManager
31687  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31688  * please see: <br><br>
31689  * <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>
31690  * <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>
31691  * Example:
31692  <pre><code>
31693  var layout = new Roo.BorderLayout(document.body, {
31694     north: {
31695         initialSize: 25,
31696         titlebar: false
31697     },
31698     west: {
31699         split:true,
31700         initialSize: 200,
31701         minSize: 175,
31702         maxSize: 400,
31703         titlebar: true,
31704         collapsible: true
31705     },
31706     east: {
31707         split:true,
31708         initialSize: 202,
31709         minSize: 175,
31710         maxSize: 400,
31711         titlebar: true,
31712         collapsible: true
31713     },
31714     south: {
31715         split:true,
31716         initialSize: 100,
31717         minSize: 100,
31718         maxSize: 200,
31719         titlebar: true,
31720         collapsible: true
31721     },
31722     center: {
31723         titlebar: true,
31724         autoScroll:true,
31725         resizeTabs: true,
31726         minTabWidth: 50,
31727         preferredTabWidth: 150
31728     }
31729 });
31730
31731 // shorthand
31732 var CP = Roo.ContentPanel;
31733
31734 layout.beginUpdate();
31735 layout.add("north", new CP("north", "North"));
31736 layout.add("south", new CP("south", {title: "South", closable: true}));
31737 layout.add("west", new CP("west", {title: "West"}));
31738 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31739 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31740 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31741 layout.getRegion("center").showPanel("center1");
31742 layout.endUpdate();
31743 </code></pre>
31744
31745 <b>The container the layout is rendered into can be either the body element or any other element.
31746 If it is not the body element, the container needs to either be an absolute positioned element,
31747 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31748 the container size if it is not the body element.</b>
31749
31750 * @constructor
31751 * Create a new BorderLayout
31752 * @param {String/HTMLElement/Element} container The container this layout is bound to
31753 * @param {Object} config Configuration options
31754  */
31755 Roo.BorderLayout = function(container, config){
31756     config = config || {};
31757     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31758     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31759     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31760         var target = this.factory.validRegions[i];
31761         if(config[target]){
31762             this.addRegion(target, config[target]);
31763         }
31764     }
31765 };
31766
31767 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31768     /**
31769      * Creates and adds a new region if it doesn't already exist.
31770      * @param {String} target The target region key (north, south, east, west or center).
31771      * @param {Object} config The regions config object
31772      * @return {BorderLayoutRegion} The new region
31773      */
31774     addRegion : function(target, config){
31775         if(!this.regions[target]){
31776             var r = this.factory.create(target, this, config);
31777             this.bindRegion(target, r);
31778         }
31779         return this.regions[target];
31780     },
31781
31782     // private (kinda)
31783     bindRegion : function(name, r){
31784         this.regions[name] = r;
31785         r.on("visibilitychange", this.layout, this);
31786         r.on("paneladded", this.layout, this);
31787         r.on("panelremoved", this.layout, this);
31788         r.on("invalidated", this.layout, this);
31789         r.on("resized", this.onRegionResized, this);
31790         r.on("collapsed", this.onRegionCollapsed, this);
31791         r.on("expanded", this.onRegionExpanded, this);
31792     },
31793
31794     /**
31795      * Performs a layout update.
31796      */
31797     layout : function(){
31798         if(this.updating) return;
31799         var size = this.getViewSize();
31800         var w = size.width;
31801         var h = size.height;
31802         var centerW = w;
31803         var centerH = h;
31804         var centerY = 0;
31805         var centerX = 0;
31806         //var x = 0, y = 0;
31807
31808         var rs = this.regions;
31809         var north = rs["north"];
31810         var south = rs["south"]; 
31811         var west = rs["west"];
31812         var east = rs["east"];
31813         var center = rs["center"];
31814         //if(this.hideOnLayout){ // not supported anymore
31815             //c.el.setStyle("display", "none");
31816         //}
31817         if(north && north.isVisible()){
31818             var b = north.getBox();
31819             var m = north.getMargins();
31820             b.width = w - (m.left+m.right);
31821             b.x = m.left;
31822             b.y = m.top;
31823             centerY = b.height + b.y + m.bottom;
31824             centerH -= centerY;
31825             north.updateBox(this.safeBox(b));
31826         }
31827         if(south && south.isVisible()){
31828             var b = south.getBox();
31829             var m = south.getMargins();
31830             b.width = w - (m.left+m.right);
31831             b.x = m.left;
31832             var totalHeight = (b.height + m.top + m.bottom);
31833             b.y = h - totalHeight + m.top;
31834             centerH -= totalHeight;
31835             south.updateBox(this.safeBox(b));
31836         }
31837         if(west && west.isVisible()){
31838             var b = west.getBox();
31839             var m = west.getMargins();
31840             b.height = centerH - (m.top+m.bottom);
31841             b.x = m.left;
31842             b.y = centerY + m.top;
31843             var totalWidth = (b.width + m.left + m.right);
31844             centerX += totalWidth;
31845             centerW -= totalWidth;
31846             west.updateBox(this.safeBox(b));
31847         }
31848         if(east && east.isVisible()){
31849             var b = east.getBox();
31850             var m = east.getMargins();
31851             b.height = centerH - (m.top+m.bottom);
31852             var totalWidth = (b.width + m.left + m.right);
31853             b.x = w - totalWidth + m.left;
31854             b.y = centerY + m.top;
31855             centerW -= totalWidth;
31856             east.updateBox(this.safeBox(b));
31857         }
31858         if(center){
31859             var m = center.getMargins();
31860             var centerBox = {
31861                 x: centerX + m.left,
31862                 y: centerY + m.top,
31863                 width: centerW - (m.left+m.right),
31864                 height: centerH - (m.top+m.bottom)
31865             };
31866             //if(this.hideOnLayout){
31867                 //center.el.setStyle("display", "block");
31868             //}
31869             center.updateBox(this.safeBox(centerBox));
31870         }
31871         this.el.repaint();
31872         this.fireEvent("layout", this);
31873     },
31874
31875     // private
31876     safeBox : function(box){
31877         box.width = Math.max(0, box.width);
31878         box.height = Math.max(0, box.height);
31879         return box;
31880     },
31881
31882     /**
31883      * Adds a ContentPanel (or subclass) to this layout.
31884      * @param {String} target The target region key (north, south, east, west or center).
31885      * @param {Roo.ContentPanel} panel The panel to add
31886      * @return {Roo.ContentPanel} The added panel
31887      */
31888     add : function(target, panel){
31889          
31890         target = target.toLowerCase();
31891         return this.regions[target].add(panel);
31892     },
31893
31894     /**
31895      * Remove a ContentPanel (or subclass) to this layout.
31896      * @param {String} target The target region key (north, south, east, west or center).
31897      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31898      * @return {Roo.ContentPanel} The removed panel
31899      */
31900     remove : function(target, panel){
31901         target = target.toLowerCase();
31902         return this.regions[target].remove(panel);
31903     },
31904
31905     /**
31906      * Searches all regions for a panel with the specified id
31907      * @param {String} panelId
31908      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31909      */
31910     findPanel : function(panelId){
31911         var rs = this.regions;
31912         for(var target in rs){
31913             if(typeof rs[target] != "function"){
31914                 var p = rs[target].getPanel(panelId);
31915                 if(p){
31916                     return p;
31917                 }
31918             }
31919         }
31920         return null;
31921     },
31922
31923     /**
31924      * Searches all regions for a panel with the specified id and activates (shows) it.
31925      * @param {String/ContentPanel} panelId The panels id or the panel itself
31926      * @return {Roo.ContentPanel} The shown panel or null
31927      */
31928     showPanel : function(panelId) {
31929       var rs = this.regions;
31930       for(var target in rs){
31931          var r = rs[target];
31932          if(typeof r != "function"){
31933             if(r.hasPanel(panelId)){
31934                return r.showPanel(panelId);
31935             }
31936          }
31937       }
31938       return null;
31939    },
31940
31941    /**
31942      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31943      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31944      */
31945     restoreState : function(provider){
31946         if(!provider){
31947             provider = Roo.state.Manager;
31948         }
31949         var sm = new Roo.LayoutStateManager();
31950         sm.init(this, provider);
31951     },
31952
31953     /**
31954      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31955      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31956      * a valid ContentPanel config object.  Example:
31957      * <pre><code>
31958 // Create the main layout
31959 var layout = new Roo.BorderLayout('main-ct', {
31960     west: {
31961         split:true,
31962         minSize: 175,
31963         titlebar: true
31964     },
31965     center: {
31966         title:'Components'
31967     }
31968 }, 'main-ct');
31969
31970 // Create and add multiple ContentPanels at once via configs
31971 layout.batchAdd({
31972    west: {
31973        id: 'source-files',
31974        autoCreate:true,
31975        title:'Ext Source Files',
31976        autoScroll:true,
31977        fitToFrame:true
31978    },
31979    center : {
31980        el: cview,
31981        autoScroll:true,
31982        fitToFrame:true,
31983        toolbar: tb,
31984        resizeEl:'cbody'
31985    }
31986 });
31987 </code></pre>
31988      * @param {Object} regions An object containing ContentPanel configs by region name
31989      */
31990     batchAdd : function(regions){
31991         this.beginUpdate();
31992         for(var rname in regions){
31993             var lr = this.regions[rname];
31994             if(lr){
31995                 this.addTypedPanels(lr, regions[rname]);
31996             }
31997         }
31998         this.endUpdate();
31999     },
32000
32001     // private
32002     addTypedPanels : function(lr, ps){
32003         if(typeof ps == 'string'){
32004             lr.add(new Roo.ContentPanel(ps));
32005         }
32006         else if(ps instanceof Array){
32007             for(var i =0, len = ps.length; i < len; i++){
32008                 this.addTypedPanels(lr, ps[i]);
32009             }
32010         }
32011         else if(!ps.events){ // raw config?
32012             var el = ps.el;
32013             delete ps.el; // prevent conflict
32014             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
32015         }
32016         else {  // panel object assumed!
32017             lr.add(ps);
32018         }
32019     },
32020     /**
32021      * Adds a xtype elements to the layout.
32022      * <pre><code>
32023
32024 layout.addxtype({
32025        xtype : 'ContentPanel',
32026        region: 'west',
32027        items: [ .... ]
32028    }
32029 );
32030
32031 layout.addxtype({
32032         xtype : 'NestedLayoutPanel',
32033         region: 'west',
32034         layout: {
32035            center: { },
32036            west: { }   
32037         },
32038         items : [ ... list of content panels or nested layout panels.. ]
32039    }
32040 );
32041 </code></pre>
32042      * @param {Object} cfg Xtype definition of item to add.
32043      */
32044     addxtype : function(cfg)
32045     {
32046         // basically accepts a pannel...
32047         // can accept a layout region..!?!?
32048         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32049         
32050         if (!cfg.xtype.match(/Panel$/)) {
32051             return false;
32052         }
32053         var ret = false;
32054         
32055         if (typeof(cfg.region) == 'undefined') {
32056             Roo.log("Failed to add Panel, region was not set");
32057             Roo.log(cfg);
32058             return false;
32059         }
32060         var region = cfg.region;
32061         delete cfg.region;
32062         
32063           
32064         var xitems = [];
32065         if (cfg.items) {
32066             xitems = cfg.items;
32067             delete cfg.items;
32068         }
32069         var nb = false;
32070         
32071         switch(cfg.xtype) 
32072         {
32073             case 'ContentPanel':  // ContentPanel (el, cfg)
32074             case 'ScrollPanel':  // ContentPanel (el, cfg)
32075             case 'ViewPanel': 
32076                 if(cfg.autoCreate) {
32077                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32078                 } else {
32079                     var el = this.el.createChild();
32080                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
32081                 }
32082                 
32083                 this.add(region, ret);
32084                 break;
32085             
32086             
32087             case 'TreePanel': // our new panel!
32088                 cfg.el = this.el.createChild();
32089                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
32090                 this.add(region, ret);
32091                 break;
32092             
32093             case 'NestedLayoutPanel': 
32094                 // create a new Layout (which is  a Border Layout...
32095                 var el = this.el.createChild();
32096                 var clayout = cfg.layout;
32097                 delete cfg.layout;
32098                 clayout.items   = clayout.items  || [];
32099                 // replace this exitems with the clayout ones..
32100                 xitems = clayout.items;
32101                  
32102                 
32103                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
32104                     cfg.background = false;
32105                 }
32106                 var layout = new Roo.BorderLayout(el, clayout);
32107                 
32108                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
32109                 //console.log('adding nested layout panel '  + cfg.toSource());
32110                 this.add(region, ret);
32111                 nb = {}; /// find first...
32112                 break;
32113                 
32114             case 'GridPanel': 
32115             
32116                 // needs grid and region
32117                 
32118                 //var el = this.getRegion(region).el.createChild();
32119                 var el = this.el.createChild();
32120                 // create the grid first...
32121                 
32122                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
32123                 delete cfg.grid;
32124                 if (region == 'center' && this.active ) {
32125                     cfg.background = false;
32126                 }
32127                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
32128                 
32129                 this.add(region, ret);
32130                 if (cfg.background) {
32131                     ret.on('activate', function(gp) {
32132                         if (!gp.grid.rendered) {
32133                             gp.grid.render();
32134                         }
32135                     });
32136                 } else {
32137                     grid.render();
32138                 }
32139                 break;
32140            
32141            
32142            
32143                 
32144                 
32145                 
32146             default: 
32147                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
32148                 return null;
32149              // GridPanel (grid, cfg)
32150             
32151         }
32152         this.beginUpdate();
32153         // add children..
32154         var region = '';
32155         var abn = {};
32156         Roo.each(xitems, function(i)  {
32157             region = nb && i.region ? i.region : false;
32158             
32159             var add = ret.addxtype(i);
32160            
32161             if (region) {
32162                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
32163                 if (!i.background) {
32164                     abn[region] = nb[region] ;
32165                 }
32166             }
32167             
32168         });
32169         this.endUpdate();
32170
32171         // make the last non-background panel active..
32172         //if (nb) { Roo.log(abn); }
32173         if (nb) {
32174             
32175             for(var r in abn) {
32176                 region = this.getRegion(r);
32177                 if (region) {
32178                     // tried using nb[r], but it does not work..
32179                      
32180                     region.showPanel(abn[r]);
32181                    
32182                 }
32183             }
32184         }
32185         return ret;
32186         
32187     }
32188 });
32189
32190 /**
32191  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
32192  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
32193  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
32194  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
32195  * <pre><code>
32196 // shorthand
32197 var CP = Roo.ContentPanel;
32198
32199 var layout = Roo.BorderLayout.create({
32200     north: {
32201         initialSize: 25,
32202         titlebar: false,
32203         panels: [new CP("north", "North")]
32204     },
32205     west: {
32206         split:true,
32207         initialSize: 200,
32208         minSize: 175,
32209         maxSize: 400,
32210         titlebar: true,
32211         collapsible: true,
32212         panels: [new CP("west", {title: "West"})]
32213     },
32214     east: {
32215         split:true,
32216         initialSize: 202,
32217         minSize: 175,
32218         maxSize: 400,
32219         titlebar: true,
32220         collapsible: true,
32221         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
32222     },
32223     south: {
32224         split:true,
32225         initialSize: 100,
32226         minSize: 100,
32227         maxSize: 200,
32228         titlebar: true,
32229         collapsible: true,
32230         panels: [new CP("south", {title: "South", closable: true})]
32231     },
32232     center: {
32233         titlebar: true,
32234         autoScroll:true,
32235         resizeTabs: true,
32236         minTabWidth: 50,
32237         preferredTabWidth: 150,
32238         panels: [
32239             new CP("center1", {title: "Close Me", closable: true}),
32240             new CP("center2", {title: "Center Panel", closable: false})
32241         ]
32242     }
32243 }, document.body);
32244
32245 layout.getRegion("center").showPanel("center1");
32246 </code></pre>
32247  * @param config
32248  * @param targetEl
32249  */
32250 Roo.BorderLayout.create = function(config, targetEl){
32251     var layout = new Roo.BorderLayout(targetEl || document.body, config);
32252     layout.beginUpdate();
32253     var regions = Roo.BorderLayout.RegionFactory.validRegions;
32254     for(var j = 0, jlen = regions.length; j < jlen; j++){
32255         var lr = regions[j];
32256         if(layout.regions[lr] && config[lr].panels){
32257             var r = layout.regions[lr];
32258             var ps = config[lr].panels;
32259             layout.addTypedPanels(r, ps);
32260         }
32261     }
32262     layout.endUpdate();
32263     return layout;
32264 };
32265
32266 // private
32267 Roo.BorderLayout.RegionFactory = {
32268     // private
32269     validRegions : ["north","south","east","west","center"],
32270
32271     // private
32272     create : function(target, mgr, config){
32273         target = target.toLowerCase();
32274         if(config.lightweight || config.basic){
32275             return new Roo.BasicLayoutRegion(mgr, config, target);
32276         }
32277         switch(target){
32278             case "north":
32279                 return new Roo.NorthLayoutRegion(mgr, config);
32280             case "south":
32281                 return new Roo.SouthLayoutRegion(mgr, config);
32282             case "east":
32283                 return new Roo.EastLayoutRegion(mgr, config);
32284             case "west":
32285                 return new Roo.WestLayoutRegion(mgr, config);
32286             case "center":
32287                 return new Roo.CenterLayoutRegion(mgr, config);
32288         }
32289         throw 'Layout region "'+target+'" not supported.';
32290     }
32291 };/*
32292  * Based on:
32293  * Ext JS Library 1.1.1
32294  * Copyright(c) 2006-2007, Ext JS, LLC.
32295  *
32296  * Originally Released Under LGPL - original licence link has changed is not relivant.
32297  *
32298  * Fork - LGPL
32299  * <script type="text/javascript">
32300  */
32301  
32302 /**
32303  * @class Roo.BasicLayoutRegion
32304  * @extends Roo.util.Observable
32305  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
32306  * and does not have a titlebar, tabs or any other features. All it does is size and position 
32307  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
32308  */
32309 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
32310     this.mgr = mgr;
32311     this.position  = pos;
32312     this.events = {
32313         /**
32314          * @scope Roo.BasicLayoutRegion
32315          */
32316         
32317         /**
32318          * @event beforeremove
32319          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
32320          * @param {Roo.LayoutRegion} this
32321          * @param {Roo.ContentPanel} panel The panel
32322          * @param {Object} e The cancel event object
32323          */
32324         "beforeremove" : true,
32325         /**
32326          * @event invalidated
32327          * Fires when the layout for this region is changed.
32328          * @param {Roo.LayoutRegion} this
32329          */
32330         "invalidated" : true,
32331         /**
32332          * @event visibilitychange
32333          * Fires when this region is shown or hidden 
32334          * @param {Roo.LayoutRegion} this
32335          * @param {Boolean} visibility true or false
32336          */
32337         "visibilitychange" : true,
32338         /**
32339          * @event paneladded
32340          * Fires when a panel is added. 
32341          * @param {Roo.LayoutRegion} this
32342          * @param {Roo.ContentPanel} panel The panel
32343          */
32344         "paneladded" : true,
32345         /**
32346          * @event panelremoved
32347          * Fires when a panel is removed. 
32348          * @param {Roo.LayoutRegion} this
32349          * @param {Roo.ContentPanel} panel The panel
32350          */
32351         "panelremoved" : true,
32352         /**
32353          * @event collapsed
32354          * Fires when this region is collapsed.
32355          * @param {Roo.LayoutRegion} this
32356          */
32357         "collapsed" : true,
32358         /**
32359          * @event expanded
32360          * Fires when this region is expanded.
32361          * @param {Roo.LayoutRegion} this
32362          */
32363         "expanded" : true,
32364         /**
32365          * @event slideshow
32366          * Fires when this region is slid into view.
32367          * @param {Roo.LayoutRegion} this
32368          */
32369         "slideshow" : true,
32370         /**
32371          * @event slidehide
32372          * Fires when this region slides out of view. 
32373          * @param {Roo.LayoutRegion} this
32374          */
32375         "slidehide" : true,
32376         /**
32377          * @event panelactivated
32378          * Fires when a panel is activated. 
32379          * @param {Roo.LayoutRegion} this
32380          * @param {Roo.ContentPanel} panel The activated panel
32381          */
32382         "panelactivated" : true,
32383         /**
32384          * @event resized
32385          * Fires when the user resizes this region. 
32386          * @param {Roo.LayoutRegion} this
32387          * @param {Number} newSize The new size (width for east/west, height for north/south)
32388          */
32389         "resized" : true
32390     };
32391     /** A collection of panels in this region. @type Roo.util.MixedCollection */
32392     this.panels = new Roo.util.MixedCollection();
32393     this.panels.getKey = this.getPanelId.createDelegate(this);
32394     this.box = null;
32395     this.activePanel = null;
32396     // ensure listeners are added...
32397     
32398     if (config.listeners || config.events) {
32399         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
32400             listeners : config.listeners || {},
32401             events : config.events || {}
32402         });
32403     }
32404     
32405     if(skipConfig !== true){
32406         this.applyConfig(config);
32407     }
32408 };
32409
32410 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
32411     getPanelId : function(p){
32412         return p.getId();
32413     },
32414     
32415     applyConfig : function(config){
32416         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32417         this.config = config;
32418         
32419     },
32420     
32421     /**
32422      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
32423      * the width, for horizontal (north, south) the height.
32424      * @param {Number} newSize The new width or height
32425      */
32426     resizeTo : function(newSize){
32427         var el = this.el ? this.el :
32428                  (this.activePanel ? this.activePanel.getEl() : null);
32429         if(el){
32430             switch(this.position){
32431                 case "east":
32432                 case "west":
32433                     el.setWidth(newSize);
32434                     this.fireEvent("resized", this, newSize);
32435                 break;
32436                 case "north":
32437                 case "south":
32438                     el.setHeight(newSize);
32439                     this.fireEvent("resized", this, newSize);
32440                 break;                
32441             }
32442         }
32443     },
32444     
32445     getBox : function(){
32446         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
32447     },
32448     
32449     getMargins : function(){
32450         return this.margins;
32451     },
32452     
32453     updateBox : function(box){
32454         this.box = box;
32455         var el = this.activePanel.getEl();
32456         el.dom.style.left = box.x + "px";
32457         el.dom.style.top = box.y + "px";
32458         this.activePanel.setSize(box.width, box.height);
32459     },
32460     
32461     /**
32462      * Returns the container element for this region.
32463      * @return {Roo.Element}
32464      */
32465     getEl : function(){
32466         return this.activePanel;
32467     },
32468     
32469     /**
32470      * Returns true if this region is currently visible.
32471      * @return {Boolean}
32472      */
32473     isVisible : function(){
32474         return this.activePanel ? true : false;
32475     },
32476     
32477     setActivePanel : function(panel){
32478         panel = this.getPanel(panel);
32479         if(this.activePanel && this.activePanel != panel){
32480             this.activePanel.setActiveState(false);
32481             this.activePanel.getEl().setLeftTop(-10000,-10000);
32482         }
32483         this.activePanel = panel;
32484         panel.setActiveState(true);
32485         if(this.box){
32486             panel.setSize(this.box.width, this.box.height);
32487         }
32488         this.fireEvent("panelactivated", this, panel);
32489         this.fireEvent("invalidated");
32490     },
32491     
32492     /**
32493      * Show the specified panel.
32494      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32495      * @return {Roo.ContentPanel} The shown panel or null
32496      */
32497     showPanel : function(panel){
32498         if(panel = this.getPanel(panel)){
32499             this.setActivePanel(panel);
32500         }
32501         return panel;
32502     },
32503     
32504     /**
32505      * Get the active panel for this region.
32506      * @return {Roo.ContentPanel} The active panel or null
32507      */
32508     getActivePanel : function(){
32509         return this.activePanel;
32510     },
32511     
32512     /**
32513      * Add the passed ContentPanel(s)
32514      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32515      * @return {Roo.ContentPanel} The panel added (if only one was added)
32516      */
32517     add : function(panel){
32518         if(arguments.length > 1){
32519             for(var i = 0, len = arguments.length; i < len; i++) {
32520                 this.add(arguments[i]);
32521             }
32522             return null;
32523         }
32524         if(this.hasPanel(panel)){
32525             this.showPanel(panel);
32526             return panel;
32527         }
32528         var el = panel.getEl();
32529         if(el.dom.parentNode != this.mgr.el.dom){
32530             this.mgr.el.dom.appendChild(el.dom);
32531         }
32532         if(panel.setRegion){
32533             panel.setRegion(this);
32534         }
32535         this.panels.add(panel);
32536         el.setStyle("position", "absolute");
32537         if(!panel.background){
32538             this.setActivePanel(panel);
32539             if(this.config.initialSize && this.panels.getCount()==1){
32540                 this.resizeTo(this.config.initialSize);
32541             }
32542         }
32543         this.fireEvent("paneladded", this, panel);
32544         return panel;
32545     },
32546     
32547     /**
32548      * Returns true if the panel is in this region.
32549      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32550      * @return {Boolean}
32551      */
32552     hasPanel : function(panel){
32553         if(typeof panel == "object"){ // must be panel obj
32554             panel = panel.getId();
32555         }
32556         return this.getPanel(panel) ? true : false;
32557     },
32558     
32559     /**
32560      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32561      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32562      * @param {Boolean} preservePanel Overrides the config preservePanel option
32563      * @return {Roo.ContentPanel} The panel that was removed
32564      */
32565     remove : function(panel, preservePanel){
32566         panel = this.getPanel(panel);
32567         if(!panel){
32568             return null;
32569         }
32570         var e = {};
32571         this.fireEvent("beforeremove", this, panel, e);
32572         if(e.cancel === true){
32573             return null;
32574         }
32575         var panelId = panel.getId();
32576         this.panels.removeKey(panelId);
32577         return panel;
32578     },
32579     
32580     /**
32581      * Returns the panel specified or null if it's not in this region.
32582      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32583      * @return {Roo.ContentPanel}
32584      */
32585     getPanel : function(id){
32586         if(typeof id == "object"){ // must be panel obj
32587             return id;
32588         }
32589         return this.panels.get(id);
32590     },
32591     
32592     /**
32593      * Returns this regions position (north/south/east/west/center).
32594      * @return {String} 
32595      */
32596     getPosition: function(){
32597         return this.position;    
32598     }
32599 });/*
32600  * Based on:
32601  * Ext JS Library 1.1.1
32602  * Copyright(c) 2006-2007, Ext JS, LLC.
32603  *
32604  * Originally Released Under LGPL - original licence link has changed is not relivant.
32605  *
32606  * Fork - LGPL
32607  * <script type="text/javascript">
32608  */
32609  
32610 /**
32611  * @class Roo.LayoutRegion
32612  * @extends Roo.BasicLayoutRegion
32613  * This class represents a region in a layout manager.
32614  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
32615  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
32616  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
32617  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32618  * @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})
32619  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
32620  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
32621  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32622  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32623  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32624  * @cfg {String}    title           The title for the region (overrides panel titles)
32625  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32626  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32627  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32628  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32629  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32630  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32631  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32632  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32633  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32634  * @cfg {Boolean}   showPin         True to show a pin button
32635  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32636  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32637  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32638  * @cfg {Number}    width           For East/West panels
32639  * @cfg {Number}    height          For North/South panels
32640  * @cfg {Boolean}   split           To show the splitter
32641  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32642  */
32643 Roo.LayoutRegion = function(mgr, config, pos){
32644     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
32645     var dh = Roo.DomHelper;
32646     /** This region's container element 
32647     * @type Roo.Element */
32648     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
32649     /** This region's title element 
32650     * @type Roo.Element */
32651
32652     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
32653         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32654         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
32655     ]}, true);
32656     this.titleEl.enableDisplayMode();
32657     /** This region's title text element 
32658     * @type HTMLElement */
32659     this.titleTextEl = this.titleEl.dom.firstChild;
32660     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32661     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
32662     this.closeBtn.enableDisplayMode();
32663     this.closeBtn.on("click", this.closeClicked, this);
32664     this.closeBtn.hide();
32665
32666     this.createBody(config);
32667     this.visible = true;
32668     this.collapsed = false;
32669
32670     if(config.hideWhenEmpty){
32671         this.hide();
32672         this.on("paneladded", this.validateVisibility, this);
32673         this.on("panelremoved", this.validateVisibility, this);
32674     }
32675     this.applyConfig(config);
32676 };
32677
32678 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
32679
32680     createBody : function(){
32681         /** This region's body element 
32682         * @type Roo.Element */
32683         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
32684     },
32685
32686     applyConfig : function(c){
32687         if(c.collapsible && this.position != "center" && !this.collapsedEl){
32688             var dh = Roo.DomHelper;
32689             if(c.titlebar !== false){
32690                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
32691                 this.collapseBtn.on("click", this.collapse, this);
32692                 this.collapseBtn.enableDisplayMode();
32693
32694                 if(c.showPin === true || this.showPin){
32695                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
32696                     this.stickBtn.enableDisplayMode();
32697                     this.stickBtn.on("click", this.expand, this);
32698                     this.stickBtn.hide();
32699                 }
32700             }
32701             /** This region's collapsed element
32702             * @type Roo.Element */
32703             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32704                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32705             ]}, true);
32706             if(c.floatable !== false){
32707                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32708                this.collapsedEl.on("click", this.collapseClick, this);
32709             }
32710
32711             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32712                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32713                    id: "message", unselectable: "on", style:{"float":"left"}});
32714                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32715              }
32716             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32717             this.expandBtn.on("click", this.expand, this);
32718         }
32719         if(this.collapseBtn){
32720             this.collapseBtn.setVisible(c.collapsible == true);
32721         }
32722         this.cmargins = c.cmargins || this.cmargins ||
32723                          (this.position == "west" || this.position == "east" ?
32724                              {top: 0, left: 2, right:2, bottom: 0} :
32725                              {top: 2, left: 0, right:0, bottom: 2});
32726         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32727         this.bottomTabs = c.tabPosition != "top";
32728         this.autoScroll = c.autoScroll || false;
32729         if(this.autoScroll){
32730             this.bodyEl.setStyle("overflow", "auto");
32731         }else{
32732             this.bodyEl.setStyle("overflow", "hidden");
32733         }
32734         //if(c.titlebar !== false){
32735             if((!c.titlebar && !c.title) || c.titlebar === false){
32736                 this.titleEl.hide();
32737             }else{
32738                 this.titleEl.show();
32739                 if(c.title){
32740                     this.titleTextEl.innerHTML = c.title;
32741                 }
32742             }
32743         //}
32744         this.duration = c.duration || .30;
32745         this.slideDuration = c.slideDuration || .45;
32746         this.config = c;
32747         if(c.collapsed){
32748             this.collapse(true);
32749         }
32750         if(c.hidden){
32751             this.hide();
32752         }
32753     },
32754     /**
32755      * Returns true if this region is currently visible.
32756      * @return {Boolean}
32757      */
32758     isVisible : function(){
32759         return this.visible;
32760     },
32761
32762     /**
32763      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32764      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32765      */
32766     setCollapsedTitle : function(title){
32767         title = title || "&#160;";
32768         if(this.collapsedTitleTextEl){
32769             this.collapsedTitleTextEl.innerHTML = title;
32770         }
32771     },
32772
32773     getBox : function(){
32774         var b;
32775         if(!this.collapsed){
32776             b = this.el.getBox(false, true);
32777         }else{
32778             b = this.collapsedEl.getBox(false, true);
32779         }
32780         return b;
32781     },
32782
32783     getMargins : function(){
32784         return this.collapsed ? this.cmargins : this.margins;
32785     },
32786
32787     highlight : function(){
32788         this.el.addClass("x-layout-panel-dragover");
32789     },
32790
32791     unhighlight : function(){
32792         this.el.removeClass("x-layout-panel-dragover");
32793     },
32794
32795     updateBox : function(box){
32796         this.box = box;
32797         if(!this.collapsed){
32798             this.el.dom.style.left = box.x + "px";
32799             this.el.dom.style.top = box.y + "px";
32800             this.updateBody(box.width, box.height);
32801         }else{
32802             this.collapsedEl.dom.style.left = box.x + "px";
32803             this.collapsedEl.dom.style.top = box.y + "px";
32804             this.collapsedEl.setSize(box.width, box.height);
32805         }
32806         if(this.tabs){
32807             this.tabs.autoSizeTabs();
32808         }
32809     },
32810
32811     updateBody : function(w, h){
32812         if(w !== null){
32813             this.el.setWidth(w);
32814             w -= this.el.getBorderWidth("rl");
32815             if(this.config.adjustments){
32816                 w += this.config.adjustments[0];
32817             }
32818         }
32819         if(h !== null){
32820             this.el.setHeight(h);
32821             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32822             h -= this.el.getBorderWidth("tb");
32823             if(this.config.adjustments){
32824                 h += this.config.adjustments[1];
32825             }
32826             this.bodyEl.setHeight(h);
32827             if(this.tabs){
32828                 h = this.tabs.syncHeight(h);
32829             }
32830         }
32831         if(this.panelSize){
32832             w = w !== null ? w : this.panelSize.width;
32833             h = h !== null ? h : this.panelSize.height;
32834         }
32835         if(this.activePanel){
32836             var el = this.activePanel.getEl();
32837             w = w !== null ? w : el.getWidth();
32838             h = h !== null ? h : el.getHeight();
32839             this.panelSize = {width: w, height: h};
32840             this.activePanel.setSize(w, h);
32841         }
32842         if(Roo.isIE && this.tabs){
32843             this.tabs.el.repaint();
32844         }
32845     },
32846
32847     /**
32848      * Returns the container element for this region.
32849      * @return {Roo.Element}
32850      */
32851     getEl : function(){
32852         return this.el;
32853     },
32854
32855     /**
32856      * Hides this region.
32857      */
32858     hide : function(){
32859         if(!this.collapsed){
32860             this.el.dom.style.left = "-2000px";
32861             this.el.hide();
32862         }else{
32863             this.collapsedEl.dom.style.left = "-2000px";
32864             this.collapsedEl.hide();
32865         }
32866         this.visible = false;
32867         this.fireEvent("visibilitychange", this, false);
32868     },
32869
32870     /**
32871      * Shows this region if it was previously hidden.
32872      */
32873     show : function(){
32874         if(!this.collapsed){
32875             this.el.show();
32876         }else{
32877             this.collapsedEl.show();
32878         }
32879         this.visible = true;
32880         this.fireEvent("visibilitychange", this, true);
32881     },
32882
32883     closeClicked : function(){
32884         if(this.activePanel){
32885             this.remove(this.activePanel);
32886         }
32887     },
32888
32889     collapseClick : function(e){
32890         if(this.isSlid){
32891            e.stopPropagation();
32892            this.slideIn();
32893         }else{
32894            e.stopPropagation();
32895            this.slideOut();
32896         }
32897     },
32898
32899     /**
32900      * Collapses this region.
32901      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32902      */
32903     collapse : function(skipAnim){
32904         if(this.collapsed) return;
32905         this.collapsed = true;
32906         if(this.split){
32907             this.split.el.hide();
32908         }
32909         if(this.config.animate && skipAnim !== true){
32910             this.fireEvent("invalidated", this);
32911             this.animateCollapse();
32912         }else{
32913             this.el.setLocation(-20000,-20000);
32914             this.el.hide();
32915             this.collapsedEl.show();
32916             this.fireEvent("collapsed", this);
32917             this.fireEvent("invalidated", this);
32918         }
32919     },
32920
32921     animateCollapse : function(){
32922         // overridden
32923     },
32924
32925     /**
32926      * Expands this region if it was previously collapsed.
32927      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32928      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32929      */
32930     expand : function(e, skipAnim){
32931         if(e) e.stopPropagation();
32932         if(!this.collapsed || this.el.hasActiveFx()) return;
32933         if(this.isSlid){
32934             this.afterSlideIn();
32935             skipAnim = true;
32936         }
32937         this.collapsed = false;
32938         if(this.config.animate && skipAnim !== true){
32939             this.animateExpand();
32940         }else{
32941             this.el.show();
32942             if(this.split){
32943                 this.split.el.show();
32944             }
32945             this.collapsedEl.setLocation(-2000,-2000);
32946             this.collapsedEl.hide();
32947             this.fireEvent("invalidated", this);
32948             this.fireEvent("expanded", this);
32949         }
32950     },
32951
32952     animateExpand : function(){
32953         // overridden
32954     },
32955
32956     initTabs : function()
32957     {
32958         this.bodyEl.setStyle("overflow", "hidden");
32959         var ts = new Roo.TabPanel(
32960                 this.bodyEl.dom,
32961                 {
32962                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32963                     disableTooltips: this.config.disableTabTips,
32964                     toolbar : this.config.toolbar
32965                 }
32966         );
32967         if(this.config.hideTabs){
32968             ts.stripWrap.setDisplayed(false);
32969         }
32970         this.tabs = ts;
32971         ts.resizeTabs = this.config.resizeTabs === true;
32972         ts.minTabWidth = this.config.minTabWidth || 40;
32973         ts.maxTabWidth = this.config.maxTabWidth || 250;
32974         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32975         ts.monitorResize = false;
32976         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32977         ts.bodyEl.addClass('x-layout-tabs-body');
32978         this.panels.each(this.initPanelAsTab, this);
32979     },
32980
32981     initPanelAsTab : function(panel){
32982         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32983                     this.config.closeOnTab && panel.isClosable());
32984         if(panel.tabTip !== undefined){
32985             ti.setTooltip(panel.tabTip);
32986         }
32987         ti.on("activate", function(){
32988               this.setActivePanel(panel);
32989         }, this);
32990         if(this.config.closeOnTab){
32991             ti.on("beforeclose", function(t, e){
32992                 e.cancel = true;
32993                 this.remove(panel);
32994             }, this);
32995         }
32996         return ti;
32997     },
32998
32999     updatePanelTitle : function(panel, title){
33000         if(this.activePanel == panel){
33001             this.updateTitle(title);
33002         }
33003         if(this.tabs){
33004             var ti = this.tabs.getTab(panel.getEl().id);
33005             ti.setText(title);
33006             if(panel.tabTip !== undefined){
33007                 ti.setTooltip(panel.tabTip);
33008             }
33009         }
33010     },
33011
33012     updateTitle : function(title){
33013         if(this.titleTextEl && !this.config.title){
33014             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
33015         }
33016     },
33017
33018     setActivePanel : function(panel){
33019         panel = this.getPanel(panel);
33020         if(this.activePanel && this.activePanel != panel){
33021             this.activePanel.setActiveState(false);
33022         }
33023         this.activePanel = panel;
33024         panel.setActiveState(true);
33025         if(this.panelSize){
33026             panel.setSize(this.panelSize.width, this.panelSize.height);
33027         }
33028         if(this.closeBtn){
33029             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
33030         }
33031         this.updateTitle(panel.getTitle());
33032         if(this.tabs){
33033             this.fireEvent("invalidated", this);
33034         }
33035         this.fireEvent("panelactivated", this, panel);
33036     },
33037
33038     /**
33039      * Shows the specified panel.
33040      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
33041      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
33042      */
33043     showPanel : function(panel){
33044         if(panel = this.getPanel(panel)){
33045             if(this.tabs){
33046                 var tab = this.tabs.getTab(panel.getEl().id);
33047                 if(tab.isHidden()){
33048                     this.tabs.unhideTab(tab.id);
33049                 }
33050                 tab.activate();
33051             }else{
33052                 this.setActivePanel(panel);
33053             }
33054         }
33055         return panel;
33056     },
33057
33058     /**
33059      * Get the active panel for this region.
33060      * @return {Roo.ContentPanel} The active panel or null
33061      */
33062     getActivePanel : function(){
33063         return this.activePanel;
33064     },
33065
33066     validateVisibility : function(){
33067         if(this.panels.getCount() < 1){
33068             this.updateTitle("&#160;");
33069             this.closeBtn.hide();
33070             this.hide();
33071         }else{
33072             if(!this.isVisible()){
33073                 this.show();
33074             }
33075         }
33076     },
33077
33078     /**
33079      * Adds the passed ContentPanel(s) to this region.
33080      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33081      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
33082      */
33083     add : function(panel){
33084         if(arguments.length > 1){
33085             for(var i = 0, len = arguments.length; i < len; i++) {
33086                 this.add(arguments[i]);
33087             }
33088             return null;
33089         }
33090         if(this.hasPanel(panel)){
33091             this.showPanel(panel);
33092             return panel;
33093         }
33094         panel.setRegion(this);
33095         this.panels.add(panel);
33096         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
33097             this.bodyEl.dom.appendChild(panel.getEl().dom);
33098             if(panel.background !== true){
33099                 this.setActivePanel(panel);
33100             }
33101             this.fireEvent("paneladded", this, panel);
33102             return panel;
33103         }
33104         if(!this.tabs){
33105             this.initTabs();
33106         }else{
33107             this.initPanelAsTab(panel);
33108         }
33109         if(panel.background !== true){
33110             this.tabs.activate(panel.getEl().id);
33111         }
33112         this.fireEvent("paneladded", this, panel);
33113         return panel;
33114     },
33115
33116     /**
33117      * Hides the tab for the specified panel.
33118      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33119      */
33120     hidePanel : function(panel){
33121         if(this.tabs && (panel = this.getPanel(panel))){
33122             this.tabs.hideTab(panel.getEl().id);
33123         }
33124     },
33125
33126     /**
33127      * Unhides the tab for a previously hidden panel.
33128      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33129      */
33130     unhidePanel : function(panel){
33131         if(this.tabs && (panel = this.getPanel(panel))){
33132             this.tabs.unhideTab(panel.getEl().id);
33133         }
33134     },
33135
33136     clearPanels : function(){
33137         while(this.panels.getCount() > 0){
33138              this.remove(this.panels.first());
33139         }
33140     },
33141
33142     /**
33143      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33144      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
33145      * @param {Boolean} preservePanel Overrides the config preservePanel option
33146      * @return {Roo.ContentPanel} The panel that was removed
33147      */
33148     remove : function(panel, preservePanel){
33149         panel = this.getPanel(panel);
33150         if(!panel){
33151             return null;
33152         }
33153         var e = {};
33154         this.fireEvent("beforeremove", this, panel, e);
33155         if(e.cancel === true){
33156             return null;
33157         }
33158         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
33159         var panelId = panel.getId();
33160         this.panels.removeKey(panelId);
33161         if(preservePanel){
33162             document.body.appendChild(panel.getEl().dom);
33163         }
33164         if(this.tabs){
33165             this.tabs.removeTab(panel.getEl().id);
33166         }else if (!preservePanel){
33167             this.bodyEl.dom.removeChild(panel.getEl().dom);
33168         }
33169         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
33170             var p = this.panels.first();
33171             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
33172             tempEl.appendChild(p.getEl().dom);
33173             this.bodyEl.update("");
33174             this.bodyEl.dom.appendChild(p.getEl().dom);
33175             tempEl = null;
33176             this.updateTitle(p.getTitle());
33177             this.tabs = null;
33178             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
33179             this.setActivePanel(p);
33180         }
33181         panel.setRegion(null);
33182         if(this.activePanel == panel){
33183             this.activePanel = null;
33184         }
33185         if(this.config.autoDestroy !== false && preservePanel !== true){
33186             try{panel.destroy();}catch(e){}
33187         }
33188         this.fireEvent("panelremoved", this, panel);
33189         return panel;
33190     },
33191
33192     /**
33193      * Returns the TabPanel component used by this region
33194      * @return {Roo.TabPanel}
33195      */
33196     getTabs : function(){
33197         return this.tabs;
33198     },
33199
33200     createTool : function(parentEl, className){
33201         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
33202             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
33203         btn.addClassOnOver("x-layout-tools-button-over");
33204         return btn;
33205     }
33206 });/*
33207  * Based on:
33208  * Ext JS Library 1.1.1
33209  * Copyright(c) 2006-2007, Ext JS, LLC.
33210  *
33211  * Originally Released Under LGPL - original licence link has changed is not relivant.
33212  *
33213  * Fork - LGPL
33214  * <script type="text/javascript">
33215  */
33216  
33217
33218
33219 /**
33220  * @class Roo.SplitLayoutRegion
33221  * @extends Roo.LayoutRegion
33222  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
33223  */
33224 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
33225     this.cursor = cursor;
33226     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
33227 };
33228
33229 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
33230     splitTip : "Drag to resize.",
33231     collapsibleSplitTip : "Drag to resize. Double click to hide.",
33232     useSplitTips : false,
33233
33234     applyConfig : function(config){
33235         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
33236         if(config.split){
33237             if(!this.split){
33238                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
33239                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
33240                 /** The SplitBar for this region 
33241                 * @type Roo.SplitBar */
33242                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
33243                 this.split.on("moved", this.onSplitMove, this);
33244                 this.split.useShim = config.useShim === true;
33245                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
33246                 if(this.useSplitTips){
33247                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
33248                 }
33249                 if(config.collapsible){
33250                     this.split.el.on("dblclick", this.collapse,  this);
33251                 }
33252             }
33253             if(typeof config.minSize != "undefined"){
33254                 this.split.minSize = config.minSize;
33255             }
33256             if(typeof config.maxSize != "undefined"){
33257                 this.split.maxSize = config.maxSize;
33258             }
33259             if(config.hideWhenEmpty || config.hidden || config.collapsed){
33260                 this.hideSplitter();
33261             }
33262         }
33263     },
33264
33265     getHMaxSize : function(){
33266          var cmax = this.config.maxSize || 10000;
33267          var center = this.mgr.getRegion("center");
33268          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
33269     },
33270
33271     getVMaxSize : function(){
33272          var cmax = this.config.maxSize || 10000;
33273          var center = this.mgr.getRegion("center");
33274          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
33275     },
33276
33277     onSplitMove : function(split, newSize){
33278         this.fireEvent("resized", this, newSize);
33279     },
33280     
33281     /** 
33282      * Returns the {@link Roo.SplitBar} for this region.
33283      * @return {Roo.SplitBar}
33284      */
33285     getSplitBar : function(){
33286         return this.split;
33287     },
33288     
33289     hide : function(){
33290         this.hideSplitter();
33291         Roo.SplitLayoutRegion.superclass.hide.call(this);
33292     },
33293
33294     hideSplitter : function(){
33295         if(this.split){
33296             this.split.el.setLocation(-2000,-2000);
33297             this.split.el.hide();
33298         }
33299     },
33300
33301     show : function(){
33302         if(this.split){
33303             this.split.el.show();
33304         }
33305         Roo.SplitLayoutRegion.superclass.show.call(this);
33306     },
33307     
33308     beforeSlide: function(){
33309         if(Roo.isGecko){// firefox overflow auto bug workaround
33310             this.bodyEl.clip();
33311             if(this.tabs) this.tabs.bodyEl.clip();
33312             if(this.activePanel){
33313                 this.activePanel.getEl().clip();
33314                 
33315                 if(this.activePanel.beforeSlide){
33316                     this.activePanel.beforeSlide();
33317                 }
33318             }
33319         }
33320     },
33321     
33322     afterSlide : function(){
33323         if(Roo.isGecko){// firefox overflow auto bug workaround
33324             this.bodyEl.unclip();
33325             if(this.tabs) this.tabs.bodyEl.unclip();
33326             if(this.activePanel){
33327                 this.activePanel.getEl().unclip();
33328                 if(this.activePanel.afterSlide){
33329                     this.activePanel.afterSlide();
33330                 }
33331             }
33332         }
33333     },
33334
33335     initAutoHide : function(){
33336         if(this.autoHide !== false){
33337             if(!this.autoHideHd){
33338                 var st = new Roo.util.DelayedTask(this.slideIn, this);
33339                 this.autoHideHd = {
33340                     "mouseout": function(e){
33341                         if(!e.within(this.el, true)){
33342                             st.delay(500);
33343                         }
33344                     },
33345                     "mouseover" : function(e){
33346                         st.cancel();
33347                     },
33348                     scope : this
33349                 };
33350             }
33351             this.el.on(this.autoHideHd);
33352         }
33353     },
33354
33355     clearAutoHide : function(){
33356         if(this.autoHide !== false){
33357             this.el.un("mouseout", this.autoHideHd.mouseout);
33358             this.el.un("mouseover", this.autoHideHd.mouseover);
33359         }
33360     },
33361
33362     clearMonitor : function(){
33363         Roo.get(document).un("click", this.slideInIf, this);
33364     },
33365
33366     // these names are backwards but not changed for compat
33367     slideOut : function(){
33368         if(this.isSlid || this.el.hasActiveFx()){
33369             return;
33370         }
33371         this.isSlid = true;
33372         if(this.collapseBtn){
33373             this.collapseBtn.hide();
33374         }
33375         this.closeBtnState = this.closeBtn.getStyle('display');
33376         this.closeBtn.hide();
33377         if(this.stickBtn){
33378             this.stickBtn.show();
33379         }
33380         this.el.show();
33381         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33382         this.beforeSlide();
33383         this.el.setStyle("z-index", 10001);
33384         this.el.slideIn(this.getSlideAnchor(), {
33385             callback: function(){
33386                 this.afterSlide();
33387                 this.initAutoHide();
33388                 Roo.get(document).on("click", this.slideInIf, this);
33389                 this.fireEvent("slideshow", this);
33390             },
33391             scope: this,
33392             block: true
33393         });
33394     },
33395
33396     afterSlideIn : function(){
33397         this.clearAutoHide();
33398         this.isSlid = false;
33399         this.clearMonitor();
33400         this.el.setStyle("z-index", "");
33401         if(this.collapseBtn){
33402             this.collapseBtn.show();
33403         }
33404         this.closeBtn.setStyle('display', this.closeBtnState);
33405         if(this.stickBtn){
33406             this.stickBtn.hide();
33407         }
33408         this.fireEvent("slidehide", this);
33409     },
33410
33411     slideIn : function(cb){
33412         if(!this.isSlid || this.el.hasActiveFx()){
33413             Roo.callback(cb);
33414             return;
33415         }
33416         this.isSlid = false;
33417         this.beforeSlide();
33418         this.el.slideOut(this.getSlideAnchor(), {
33419             callback: function(){
33420                 this.el.setLeftTop(-10000, -10000);
33421                 this.afterSlide();
33422                 this.afterSlideIn();
33423                 Roo.callback(cb);
33424             },
33425             scope: this,
33426             block: true
33427         });
33428     },
33429     
33430     slideInIf : function(e){
33431         if(!e.within(this.el)){
33432             this.slideIn();
33433         }
33434     },
33435
33436     animateCollapse : function(){
33437         this.beforeSlide();
33438         this.el.setStyle("z-index", 20000);
33439         var anchor = this.getSlideAnchor();
33440         this.el.slideOut(anchor, {
33441             callback : function(){
33442                 this.el.setStyle("z-index", "");
33443                 this.collapsedEl.slideIn(anchor, {duration:.3});
33444                 this.afterSlide();
33445                 this.el.setLocation(-10000,-10000);
33446                 this.el.hide();
33447                 this.fireEvent("collapsed", this);
33448             },
33449             scope: this,
33450             block: true
33451         });
33452     },
33453
33454     animateExpand : function(){
33455         this.beforeSlide();
33456         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33457         this.el.setStyle("z-index", 20000);
33458         this.collapsedEl.hide({
33459             duration:.1
33460         });
33461         this.el.slideIn(this.getSlideAnchor(), {
33462             callback : function(){
33463                 this.el.setStyle("z-index", "");
33464                 this.afterSlide();
33465                 if(this.split){
33466                     this.split.el.show();
33467                 }
33468                 this.fireEvent("invalidated", this);
33469                 this.fireEvent("expanded", this);
33470             },
33471             scope: this,
33472             block: true
33473         });
33474     },
33475
33476     anchors : {
33477         "west" : "left",
33478         "east" : "right",
33479         "north" : "top",
33480         "south" : "bottom"
33481     },
33482
33483     sanchors : {
33484         "west" : "l",
33485         "east" : "r",
33486         "north" : "t",
33487         "south" : "b"
33488     },
33489
33490     canchors : {
33491         "west" : "tl-tr",
33492         "east" : "tr-tl",
33493         "north" : "tl-bl",
33494         "south" : "bl-tl"
33495     },
33496
33497     getAnchor : function(){
33498         return this.anchors[this.position];
33499     },
33500
33501     getCollapseAnchor : function(){
33502         return this.canchors[this.position];
33503     },
33504
33505     getSlideAnchor : function(){
33506         return this.sanchors[this.position];
33507     },
33508
33509     getAlignAdj : function(){
33510         var cm = this.cmargins;
33511         switch(this.position){
33512             case "west":
33513                 return [0, 0];
33514             break;
33515             case "east":
33516                 return [0, 0];
33517             break;
33518             case "north":
33519                 return [0, 0];
33520             break;
33521             case "south":
33522                 return [0, 0];
33523             break;
33524         }
33525     },
33526
33527     getExpandAdj : function(){
33528         var c = this.collapsedEl, cm = this.cmargins;
33529         switch(this.position){
33530             case "west":
33531                 return [-(cm.right+c.getWidth()+cm.left), 0];
33532             break;
33533             case "east":
33534                 return [cm.right+c.getWidth()+cm.left, 0];
33535             break;
33536             case "north":
33537                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33538             break;
33539             case "south":
33540                 return [0, cm.top+cm.bottom+c.getHeight()];
33541             break;
33542         }
33543     }
33544 });/*
33545  * Based on:
33546  * Ext JS Library 1.1.1
33547  * Copyright(c) 2006-2007, Ext JS, LLC.
33548  *
33549  * Originally Released Under LGPL - original licence link has changed is not relivant.
33550  *
33551  * Fork - LGPL
33552  * <script type="text/javascript">
33553  */
33554 /*
33555  * These classes are private internal classes
33556  */
33557 Roo.CenterLayoutRegion = function(mgr, config){
33558     Roo.LayoutRegion.call(this, mgr, config, "center");
33559     this.visible = true;
33560     this.minWidth = config.minWidth || 20;
33561     this.minHeight = config.minHeight || 20;
33562 };
33563
33564 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
33565     hide : function(){
33566         // center panel can't be hidden
33567     },
33568     
33569     show : function(){
33570         // center panel can't be hidden
33571     },
33572     
33573     getMinWidth: function(){
33574         return this.minWidth;
33575     },
33576     
33577     getMinHeight: function(){
33578         return this.minHeight;
33579     }
33580 });
33581
33582
33583 Roo.NorthLayoutRegion = function(mgr, config){
33584     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
33585     if(this.split){
33586         this.split.placement = Roo.SplitBar.TOP;
33587         this.split.orientation = Roo.SplitBar.VERTICAL;
33588         this.split.el.addClass("x-layout-split-v");
33589     }
33590     var size = config.initialSize || config.height;
33591     if(typeof size != "undefined"){
33592         this.el.setHeight(size);
33593     }
33594 };
33595 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
33596     orientation: Roo.SplitBar.VERTICAL,
33597     getBox : function(){
33598         if(this.collapsed){
33599             return this.collapsedEl.getBox();
33600         }
33601         var box = this.el.getBox();
33602         if(this.split){
33603             box.height += this.split.el.getHeight();
33604         }
33605         return box;
33606     },
33607     
33608     updateBox : function(box){
33609         if(this.split && !this.collapsed){
33610             box.height -= this.split.el.getHeight();
33611             this.split.el.setLeft(box.x);
33612             this.split.el.setTop(box.y+box.height);
33613             this.split.el.setWidth(box.width);
33614         }
33615         if(this.collapsed){
33616             this.updateBody(box.width, null);
33617         }
33618         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33619     }
33620 });
33621
33622 Roo.SouthLayoutRegion = function(mgr, config){
33623     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
33624     if(this.split){
33625         this.split.placement = Roo.SplitBar.BOTTOM;
33626         this.split.orientation = Roo.SplitBar.VERTICAL;
33627         this.split.el.addClass("x-layout-split-v");
33628     }
33629     var size = config.initialSize || config.height;
33630     if(typeof size != "undefined"){
33631         this.el.setHeight(size);
33632     }
33633 };
33634 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
33635     orientation: Roo.SplitBar.VERTICAL,
33636     getBox : function(){
33637         if(this.collapsed){
33638             return this.collapsedEl.getBox();
33639         }
33640         var box = this.el.getBox();
33641         if(this.split){
33642             var sh = this.split.el.getHeight();
33643             box.height += sh;
33644             box.y -= sh;
33645         }
33646         return box;
33647     },
33648     
33649     updateBox : function(box){
33650         if(this.split && !this.collapsed){
33651             var sh = this.split.el.getHeight();
33652             box.height -= sh;
33653             box.y += sh;
33654             this.split.el.setLeft(box.x);
33655             this.split.el.setTop(box.y-sh);
33656             this.split.el.setWidth(box.width);
33657         }
33658         if(this.collapsed){
33659             this.updateBody(box.width, null);
33660         }
33661         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33662     }
33663 });
33664
33665 Roo.EastLayoutRegion = function(mgr, config){
33666     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
33667     if(this.split){
33668         this.split.placement = Roo.SplitBar.RIGHT;
33669         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33670         this.split.el.addClass("x-layout-split-h");
33671     }
33672     var size = config.initialSize || config.width;
33673     if(typeof size != "undefined"){
33674         this.el.setWidth(size);
33675     }
33676 };
33677 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
33678     orientation: Roo.SplitBar.HORIZONTAL,
33679     getBox : function(){
33680         if(this.collapsed){
33681             return this.collapsedEl.getBox();
33682         }
33683         var box = this.el.getBox();
33684         if(this.split){
33685             var sw = this.split.el.getWidth();
33686             box.width += sw;
33687             box.x -= sw;
33688         }
33689         return box;
33690     },
33691
33692     updateBox : function(box){
33693         if(this.split && !this.collapsed){
33694             var sw = this.split.el.getWidth();
33695             box.width -= sw;
33696             this.split.el.setLeft(box.x);
33697             this.split.el.setTop(box.y);
33698             this.split.el.setHeight(box.height);
33699             box.x += sw;
33700         }
33701         if(this.collapsed){
33702             this.updateBody(null, box.height);
33703         }
33704         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33705     }
33706 });
33707
33708 Roo.WestLayoutRegion = function(mgr, config){
33709     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
33710     if(this.split){
33711         this.split.placement = Roo.SplitBar.LEFT;
33712         this.split.orientation = Roo.SplitBar.HORIZONTAL;
33713         this.split.el.addClass("x-layout-split-h");
33714     }
33715     var size = config.initialSize || config.width;
33716     if(typeof size != "undefined"){
33717         this.el.setWidth(size);
33718     }
33719 };
33720 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
33721     orientation: Roo.SplitBar.HORIZONTAL,
33722     getBox : function(){
33723         if(this.collapsed){
33724             return this.collapsedEl.getBox();
33725         }
33726         var box = this.el.getBox();
33727         if(this.split){
33728             box.width += this.split.el.getWidth();
33729         }
33730         return box;
33731     },
33732     
33733     updateBox : function(box){
33734         if(this.split && !this.collapsed){
33735             var sw = this.split.el.getWidth();
33736             box.width -= sw;
33737             this.split.el.setLeft(box.x+box.width);
33738             this.split.el.setTop(box.y);
33739             this.split.el.setHeight(box.height);
33740         }
33741         if(this.collapsed){
33742             this.updateBody(null, box.height);
33743         }
33744         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33745     }
33746 });
33747 /*
33748  * Based on:
33749  * Ext JS Library 1.1.1
33750  * Copyright(c) 2006-2007, Ext JS, LLC.
33751  *
33752  * Originally Released Under LGPL - original licence link has changed is not relivant.
33753  *
33754  * Fork - LGPL
33755  * <script type="text/javascript">
33756  */
33757  
33758  
33759 /*
33760  * Private internal class for reading and applying state
33761  */
33762 Roo.LayoutStateManager = function(layout){
33763      // default empty state
33764      this.state = {
33765         north: {},
33766         south: {},
33767         east: {},
33768         west: {}       
33769     };
33770 };
33771
33772 Roo.LayoutStateManager.prototype = {
33773     init : function(layout, provider){
33774         this.provider = provider;
33775         var state = provider.get(layout.id+"-layout-state");
33776         if(state){
33777             var wasUpdating = layout.isUpdating();
33778             if(!wasUpdating){
33779                 layout.beginUpdate();
33780             }
33781             for(var key in state){
33782                 if(typeof state[key] != "function"){
33783                     var rstate = state[key];
33784                     var r = layout.getRegion(key);
33785                     if(r && rstate){
33786                         if(rstate.size){
33787                             r.resizeTo(rstate.size);
33788                         }
33789                         if(rstate.collapsed == true){
33790                             r.collapse(true);
33791                         }else{
33792                             r.expand(null, true);
33793                         }
33794                     }
33795                 }
33796             }
33797             if(!wasUpdating){
33798                 layout.endUpdate();
33799             }
33800             this.state = state; 
33801         }
33802         this.layout = layout;
33803         layout.on("regionresized", this.onRegionResized, this);
33804         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33805         layout.on("regionexpanded", this.onRegionExpanded, this);
33806     },
33807     
33808     storeState : function(){
33809         this.provider.set(this.layout.id+"-layout-state", this.state);
33810     },
33811     
33812     onRegionResized : function(region, newSize){
33813         this.state[region.getPosition()].size = newSize;
33814         this.storeState();
33815     },
33816     
33817     onRegionCollapsed : function(region){
33818         this.state[region.getPosition()].collapsed = true;
33819         this.storeState();
33820     },
33821     
33822     onRegionExpanded : function(region){
33823         this.state[region.getPosition()].collapsed = false;
33824         this.storeState();
33825     }
33826 };/*
33827  * Based on:
33828  * Ext JS Library 1.1.1
33829  * Copyright(c) 2006-2007, Ext JS, LLC.
33830  *
33831  * Originally Released Under LGPL - original licence link has changed is not relivant.
33832  *
33833  * Fork - LGPL
33834  * <script type="text/javascript">
33835  */
33836 /**
33837  * @class Roo.ContentPanel
33838  * @extends Roo.util.Observable
33839  * A basic ContentPanel element.
33840  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33841  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33842  * @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
33843  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33844  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33845  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33846  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33847  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33848  * @cfg {String} title          The title for this panel
33849  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33850  * @cfg {String} url            Calls {@link #setUrl} with this value
33851  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33852  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33853  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33854  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33855
33856  * @constructor
33857  * Create a new ContentPanel.
33858  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33859  * @param {String/Object} config A string to set only the title or a config object
33860  * @param {String} content (optional) Set the HTML content for this panel
33861  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33862  */
33863 Roo.ContentPanel = function(el, config, content){
33864     
33865      
33866     /*
33867     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33868         config = el;
33869         el = Roo.id();
33870     }
33871     if (config && config.parentLayout) { 
33872         el = config.parentLayout.el.createChild(); 
33873     }
33874     */
33875     if(el.autoCreate){ // xtype is available if this is called from factory
33876         config = el;
33877         el = Roo.id();
33878     }
33879     this.el = Roo.get(el);
33880     if(!this.el && config && config.autoCreate){
33881         if(typeof config.autoCreate == "object"){
33882             if(!config.autoCreate.id){
33883                 config.autoCreate.id = config.id||el;
33884             }
33885             this.el = Roo.DomHelper.append(document.body,
33886                         config.autoCreate, true);
33887         }else{
33888             this.el = Roo.DomHelper.append(document.body,
33889                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33890         }
33891     }
33892     this.closable = false;
33893     this.loaded = false;
33894     this.active = false;
33895     if(typeof config == "string"){
33896         this.title = config;
33897     }else{
33898         Roo.apply(this, config);
33899     }
33900     
33901     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33902         this.wrapEl = this.el.wrap();
33903         this.toolbar.container = this.el.insertSibling(false, 'before');
33904         this.toolbar = new Roo.Toolbar(this.toolbar);
33905     }
33906     
33907     // xtype created footer. - not sure if will work as we normally have to render first..
33908     if (this.footer && !this.footer.el && this.footer.xtype) {
33909         if (!this.wrapEl) {
33910             this.wrapEl = this.el.wrap();
33911         }
33912     
33913         this.footer.container = this.wrapEl.createChild();
33914          
33915         this.footer = Roo.factory(this.footer, Roo);
33916         
33917     }
33918     
33919     if(this.resizeEl){
33920         this.resizeEl = Roo.get(this.resizeEl, true);
33921     }else{
33922         this.resizeEl = this.el;
33923     }
33924     // handle view.xtype
33925     
33926  
33927     
33928     
33929     this.addEvents({
33930         /**
33931          * @event activate
33932          * Fires when this panel is activated. 
33933          * @param {Roo.ContentPanel} this
33934          */
33935         "activate" : true,
33936         /**
33937          * @event deactivate
33938          * Fires when this panel is activated. 
33939          * @param {Roo.ContentPanel} this
33940          */
33941         "deactivate" : true,
33942
33943         /**
33944          * @event resize
33945          * Fires when this panel is resized if fitToFrame is true.
33946          * @param {Roo.ContentPanel} this
33947          * @param {Number} width The width after any component adjustments
33948          * @param {Number} height The height after any component adjustments
33949          */
33950         "resize" : true,
33951         
33952          /**
33953          * @event render
33954          * Fires when this tab is created
33955          * @param {Roo.ContentPanel} this
33956          */
33957         "render" : true
33958         
33959         
33960         
33961     });
33962     
33963
33964     
33965     
33966     if(this.autoScroll){
33967         this.resizeEl.setStyle("overflow", "auto");
33968     } else {
33969         // fix randome scrolling
33970         this.el.on('scroll', function() {
33971             Roo.log('fix random scolling');
33972             this.scrollTo('top',0); 
33973         });
33974     }
33975     content = content || this.content;
33976     if(content){
33977         this.setContent(content);
33978     }
33979     if(config && config.url){
33980         this.setUrl(this.url, this.params, this.loadOnce);
33981     }
33982     
33983     
33984     
33985     Roo.ContentPanel.superclass.constructor.call(this);
33986     
33987     if (this.view && typeof(this.view.xtype) != 'undefined') {
33988         this.view.el = this.el.appendChild(document.createElement("div"));
33989         this.view = Roo.factory(this.view); 
33990         this.view.render  &&  this.view.render(false, '');  
33991     }
33992     
33993     
33994     this.fireEvent('render', this);
33995 };
33996
33997 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33998     tabTip:'',
33999     setRegion : function(region){
34000         this.region = region;
34001         if(region){
34002            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
34003         }else{
34004            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
34005         } 
34006     },
34007     
34008     /**
34009      * Returns the toolbar for this Panel if one was configured. 
34010      * @return {Roo.Toolbar} 
34011      */
34012     getToolbar : function(){
34013         return this.toolbar;
34014     },
34015     
34016     setActiveState : function(active){
34017         this.active = active;
34018         if(!active){
34019             this.fireEvent("deactivate", this);
34020         }else{
34021             this.fireEvent("activate", this);
34022         }
34023     },
34024     /**
34025      * Updates this panel's element
34026      * @param {String} content The new content
34027      * @param {Boolean} loadScripts (optional) true to look for and process scripts
34028     */
34029     setContent : function(content, loadScripts){
34030         this.el.update(content, loadScripts);
34031     },
34032
34033     ignoreResize : function(w, h){
34034         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
34035             return true;
34036         }else{
34037             this.lastSize = {width: w, height: h};
34038             return false;
34039         }
34040     },
34041     /**
34042      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
34043      * @return {Roo.UpdateManager} The UpdateManager
34044      */
34045     getUpdateManager : function(){
34046         return this.el.getUpdateManager();
34047     },
34048      /**
34049      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
34050      * @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:
34051 <pre><code>
34052 panel.load({
34053     url: "your-url.php",
34054     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
34055     callback: yourFunction,
34056     scope: yourObject, //(optional scope)
34057     discardUrl: false,
34058     nocache: false,
34059     text: "Loading...",
34060     timeout: 30,
34061     scripts: false
34062 });
34063 </code></pre>
34064      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
34065      * 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.
34066      * @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}
34067      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
34068      * @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.
34069      * @return {Roo.ContentPanel} this
34070      */
34071     load : function(){
34072         var um = this.el.getUpdateManager();
34073         um.update.apply(um, arguments);
34074         return this;
34075     },
34076
34077
34078     /**
34079      * 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.
34080      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
34081      * @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)
34082      * @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)
34083      * @return {Roo.UpdateManager} The UpdateManager
34084      */
34085     setUrl : function(url, params, loadOnce){
34086         if(this.refreshDelegate){
34087             this.removeListener("activate", this.refreshDelegate);
34088         }
34089         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34090         this.on("activate", this.refreshDelegate);
34091         return this.el.getUpdateManager();
34092     },
34093     
34094     _handleRefresh : function(url, params, loadOnce){
34095         if(!loadOnce || !this.loaded){
34096             var updater = this.el.getUpdateManager();
34097             updater.update(url, params, this._setLoaded.createDelegate(this));
34098         }
34099     },
34100     
34101     _setLoaded : function(){
34102         this.loaded = true;
34103     }, 
34104     
34105     /**
34106      * Returns this panel's id
34107      * @return {String} 
34108      */
34109     getId : function(){
34110         return this.el.id;
34111     },
34112     
34113     /** 
34114      * Returns this panel's element - used by regiosn to add.
34115      * @return {Roo.Element} 
34116      */
34117     getEl : function(){
34118         return this.wrapEl || this.el;
34119     },
34120     
34121     adjustForComponents : function(width, height)
34122     {
34123         //Roo.log('adjustForComponents ');
34124         if(this.resizeEl != this.el){
34125             width -= this.el.getFrameWidth('lr');
34126             height -= this.el.getFrameWidth('tb');
34127         }
34128         if(this.toolbar){
34129             var te = this.toolbar.getEl();
34130             height -= te.getHeight();
34131             te.setWidth(width);
34132         }
34133         if(this.footer){
34134             var te = this.footer.getEl();
34135             Roo.log("footer:" + te.getHeight());
34136             
34137             height -= te.getHeight();
34138             te.setWidth(width);
34139         }
34140         
34141         
34142         if(this.adjustments){
34143             width += this.adjustments[0];
34144             height += this.adjustments[1];
34145         }
34146         return {"width": width, "height": height};
34147     },
34148     
34149     setSize : function(width, height){
34150         if(this.fitToFrame && !this.ignoreResize(width, height)){
34151             if(this.fitContainer && this.resizeEl != this.el){
34152                 this.el.setSize(width, height);
34153             }
34154             var size = this.adjustForComponents(width, height);
34155             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
34156             this.fireEvent('resize', this, size.width, size.height);
34157         }
34158     },
34159     
34160     /**
34161      * Returns this panel's title
34162      * @return {String} 
34163      */
34164     getTitle : function(){
34165         return this.title;
34166     },
34167     
34168     /**
34169      * Set this panel's title
34170      * @param {String} title
34171      */
34172     setTitle : function(title){
34173         this.title = title;
34174         if(this.region){
34175             this.region.updatePanelTitle(this, title);
34176         }
34177     },
34178     
34179     /**
34180      * Returns true is this panel was configured to be closable
34181      * @return {Boolean} 
34182      */
34183     isClosable : function(){
34184         return this.closable;
34185     },
34186     
34187     beforeSlide : function(){
34188         this.el.clip();
34189         this.resizeEl.clip();
34190     },
34191     
34192     afterSlide : function(){
34193         this.el.unclip();
34194         this.resizeEl.unclip();
34195     },
34196     
34197     /**
34198      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
34199      *   Will fail silently if the {@link #setUrl} method has not been called.
34200      *   This does not activate the panel, just updates its content.
34201      */
34202     refresh : function(){
34203         if(this.refreshDelegate){
34204            this.loaded = false;
34205            this.refreshDelegate();
34206         }
34207     },
34208     
34209     /**
34210      * Destroys this panel
34211      */
34212     destroy : function(){
34213         this.el.removeAllListeners();
34214         var tempEl = document.createElement("span");
34215         tempEl.appendChild(this.el.dom);
34216         tempEl.innerHTML = "";
34217         this.el.remove();
34218         this.el = null;
34219     },
34220     
34221     /**
34222      * form - if the content panel contains a form - this is a reference to it.
34223      * @type {Roo.form.Form}
34224      */
34225     form : false,
34226     /**
34227      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
34228      *    This contains a reference to it.
34229      * @type {Roo.View}
34230      */
34231     view : false,
34232     
34233       /**
34234      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
34235      * <pre><code>
34236
34237 layout.addxtype({
34238        xtype : 'Form',
34239        items: [ .... ]
34240    }
34241 );
34242
34243 </code></pre>
34244      * @param {Object} cfg Xtype definition of item to add.
34245      */
34246     
34247     addxtype : function(cfg) {
34248         // add form..
34249         if (cfg.xtype.match(/^Form$/)) {
34250             
34251             var el;
34252             //if (this.footer) {
34253             //    el = this.footer.container.insertSibling(false, 'before');
34254             //} else {
34255                 el = this.el.createChild();
34256             //}
34257
34258             this.form = new  Roo.form.Form(cfg);
34259             
34260             
34261             if ( this.form.allItems.length) this.form.render(el.dom);
34262             return this.form;
34263         }
34264         // should only have one of theses..
34265         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
34266             // views.. should not be just added - used named prop 'view''
34267             
34268             cfg.el = this.el.appendChild(document.createElement("div"));
34269             // factory?
34270             
34271             var ret = new Roo.factory(cfg);
34272              
34273              ret.render && ret.render(false, ''); // render blank..
34274             this.view = ret;
34275             return ret;
34276         }
34277         return false;
34278     }
34279 });
34280
34281 /**
34282  * @class Roo.GridPanel
34283  * @extends Roo.ContentPanel
34284  * @constructor
34285  * Create a new GridPanel.
34286  * @param {Roo.grid.Grid} grid The grid for this panel
34287  * @param {String/Object} config A string to set only the panel's title, or a config object
34288  */
34289 Roo.GridPanel = function(grid, config){
34290     
34291   
34292     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
34293         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
34294         
34295     this.wrapper.dom.appendChild(grid.getGridEl().dom);
34296     
34297     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
34298     
34299     if(this.toolbar){
34300         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
34301     }
34302     // xtype created footer. - not sure if will work as we normally have to render first..
34303     if (this.footer && !this.footer.el && this.footer.xtype) {
34304         
34305         this.footer.container = this.grid.getView().getFooterPanel(true);
34306         this.footer.dataSource = this.grid.dataSource;
34307         this.footer = Roo.factory(this.footer, Roo);
34308         
34309     }
34310     
34311     grid.monitorWindowResize = false; // turn off autosizing
34312     grid.autoHeight = false;
34313     grid.autoWidth = false;
34314     this.grid = grid;
34315     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
34316 };
34317
34318 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
34319     getId : function(){
34320         return this.grid.id;
34321     },
34322     
34323     /**
34324      * Returns the grid for this panel
34325      * @return {Roo.grid.Grid} 
34326      */
34327     getGrid : function(){
34328         return this.grid;    
34329     },
34330     
34331     setSize : function(width, height){
34332         if(!this.ignoreResize(width, height)){
34333             var grid = this.grid;
34334             var size = this.adjustForComponents(width, height);
34335             grid.getGridEl().setSize(size.width, size.height);
34336             grid.autoSize();
34337         }
34338     },
34339     
34340     beforeSlide : function(){
34341         this.grid.getView().scroller.clip();
34342     },
34343     
34344     afterSlide : function(){
34345         this.grid.getView().scroller.unclip();
34346     },
34347     
34348     destroy : function(){
34349         this.grid.destroy();
34350         delete this.grid;
34351         Roo.GridPanel.superclass.destroy.call(this); 
34352     }
34353 });
34354
34355
34356 /**
34357  * @class Roo.NestedLayoutPanel
34358  * @extends Roo.ContentPanel
34359  * @constructor
34360  * Create a new NestedLayoutPanel.
34361  * 
34362  * 
34363  * @param {Roo.BorderLayout} layout The layout for this panel
34364  * @param {String/Object} config A string to set only the title or a config object
34365  */
34366 Roo.NestedLayoutPanel = function(layout, config)
34367 {
34368     // construct with only one argument..
34369     /* FIXME - implement nicer consturctors
34370     if (layout.layout) {
34371         config = layout;
34372         layout = config.layout;
34373         delete config.layout;
34374     }
34375     if (layout.xtype && !layout.getEl) {
34376         // then layout needs constructing..
34377         layout = Roo.factory(layout, Roo);
34378     }
34379     */
34380     
34381     
34382     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
34383     
34384     layout.monitorWindowResize = false; // turn off autosizing
34385     this.layout = layout;
34386     this.layout.getEl().addClass("x-layout-nested-layout");
34387     
34388     
34389     
34390     
34391 };
34392
34393 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
34394
34395     setSize : function(width, height){
34396         if(!this.ignoreResize(width, height)){
34397             var size = this.adjustForComponents(width, height);
34398             var el = this.layout.getEl();
34399             el.setSize(size.width, size.height);
34400             var touch = el.dom.offsetWidth;
34401             this.layout.layout();
34402             // ie requires a double layout on the first pass
34403             if(Roo.isIE && !this.initialized){
34404                 this.initialized = true;
34405                 this.layout.layout();
34406             }
34407         }
34408     },
34409     
34410     // activate all subpanels if not currently active..
34411     
34412     setActiveState : function(active){
34413         this.active = active;
34414         if(!active){
34415             this.fireEvent("deactivate", this);
34416             return;
34417         }
34418         
34419         this.fireEvent("activate", this);
34420         // not sure if this should happen before or after..
34421         if (!this.layout) {
34422             return; // should not happen..
34423         }
34424         var reg = false;
34425         for (var r in this.layout.regions) {
34426             reg = this.layout.getRegion(r);
34427             if (reg.getActivePanel()) {
34428                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34429                 reg.setActivePanel(reg.getActivePanel());
34430                 continue;
34431             }
34432             if (!reg.panels.length) {
34433                 continue;
34434             }
34435             reg.showPanel(reg.getPanel(0));
34436         }
34437         
34438         
34439         
34440         
34441     },
34442     
34443     /**
34444      * Returns the nested BorderLayout for this panel
34445      * @return {Roo.BorderLayout} 
34446      */
34447     getLayout : function(){
34448         return this.layout;
34449     },
34450     
34451      /**
34452      * Adds a xtype elements to the layout of the nested panel
34453      * <pre><code>
34454
34455 panel.addxtype({
34456        xtype : 'ContentPanel',
34457        region: 'west',
34458        items: [ .... ]
34459    }
34460 );
34461
34462 panel.addxtype({
34463         xtype : 'NestedLayoutPanel',
34464         region: 'west',
34465         layout: {
34466            center: { },
34467            west: { }   
34468         },
34469         items : [ ... list of content panels or nested layout panels.. ]
34470    }
34471 );
34472 </code></pre>
34473      * @param {Object} cfg Xtype definition of item to add.
34474      */
34475     addxtype : function(cfg) {
34476         return this.layout.addxtype(cfg);
34477     
34478     }
34479 });
34480
34481 Roo.ScrollPanel = function(el, config, content){
34482     config = config || {};
34483     config.fitToFrame = true;
34484     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
34485     
34486     this.el.dom.style.overflow = "hidden";
34487     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
34488     this.el.removeClass("x-layout-inactive-content");
34489     this.el.on("mousewheel", this.onWheel, this);
34490
34491     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
34492     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
34493     up.unselectable(); down.unselectable();
34494     up.on("click", this.scrollUp, this);
34495     down.on("click", this.scrollDown, this);
34496     up.addClassOnOver("x-scroller-btn-over");
34497     down.addClassOnOver("x-scroller-btn-over");
34498     up.addClassOnClick("x-scroller-btn-click");
34499     down.addClassOnClick("x-scroller-btn-click");
34500     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
34501
34502     this.resizeEl = this.el;
34503     this.el = wrap; this.up = up; this.down = down;
34504 };
34505
34506 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
34507     increment : 100,
34508     wheelIncrement : 5,
34509     scrollUp : function(){
34510         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
34511     },
34512
34513     scrollDown : function(){
34514         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
34515     },
34516
34517     afterScroll : function(){
34518         var el = this.resizeEl;
34519         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
34520         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34521         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
34522     },
34523
34524     setSize : function(){
34525         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
34526         this.afterScroll();
34527     },
34528
34529     onWheel : function(e){
34530         var d = e.getWheelDelta();
34531         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
34532         this.afterScroll();
34533         e.stopEvent();
34534     },
34535
34536     setContent : function(content, loadScripts){
34537         this.resizeEl.update(content, loadScripts);
34538     }
34539
34540 });
34541
34542
34543
34544
34545
34546
34547
34548
34549
34550 /**
34551  * @class Roo.TreePanel
34552  * @extends Roo.ContentPanel
34553  * @constructor
34554  * Create a new TreePanel. - defaults to fit/scoll contents.
34555  * @param {String/Object} config A string to set only the panel's title, or a config object
34556  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
34557  */
34558 Roo.TreePanel = function(config){
34559     var el = config.el;
34560     var tree = config.tree;
34561     delete config.tree; 
34562     delete config.el; // hopefull!
34563     
34564     // wrapper for IE7 strict & safari scroll issue
34565     
34566     var treeEl = el.createChild();
34567     config.resizeEl = treeEl;
34568     
34569     
34570     
34571     Roo.TreePanel.superclass.constructor.call(this, el, config);
34572  
34573  
34574     this.tree = new Roo.tree.TreePanel(treeEl , tree);
34575     //console.log(tree);
34576     this.on('activate', function()
34577     {
34578         if (this.tree.rendered) {
34579             return;
34580         }
34581         //console.log('render tree');
34582         this.tree.render();
34583     });
34584     // this should not be needed.. - it's actually the 'el' that resizes?
34585     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
34586     
34587     //this.on('resize',  function (cp, w, h) {
34588     //        this.tree.innerCt.setWidth(w);
34589     //        this.tree.innerCt.setHeight(h);
34590     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
34591     //});
34592
34593         
34594     
34595 };
34596
34597 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
34598     fitToFrame : true,
34599     autoScroll : true
34600 });
34601
34602
34603
34604
34605
34606
34607
34608
34609
34610
34611
34612 /*
34613  * Based on:
34614  * Ext JS Library 1.1.1
34615  * Copyright(c) 2006-2007, Ext JS, LLC.
34616  *
34617  * Originally Released Under LGPL - original licence link has changed is not relivant.
34618  *
34619  * Fork - LGPL
34620  * <script type="text/javascript">
34621  */
34622  
34623
34624 /**
34625  * @class Roo.ReaderLayout
34626  * @extends Roo.BorderLayout
34627  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
34628  * center region containing two nested regions (a top one for a list view and one for item preview below),
34629  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
34630  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
34631  * expedites the setup of the overall layout and regions for this common application style.
34632  * Example:
34633  <pre><code>
34634 var reader = new Roo.ReaderLayout();
34635 var CP = Roo.ContentPanel;  // shortcut for adding
34636
34637 reader.beginUpdate();
34638 reader.add("north", new CP("north", "North"));
34639 reader.add("west", new CP("west", {title: "West"}));
34640 reader.add("east", new CP("east", {title: "East"}));
34641
34642 reader.regions.listView.add(new CP("listView", "List"));
34643 reader.regions.preview.add(new CP("preview", "Preview"));
34644 reader.endUpdate();
34645 </code></pre>
34646 * @constructor
34647 * Create a new ReaderLayout
34648 * @param {Object} config Configuration options
34649 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
34650 * document.body if omitted)
34651 */
34652 Roo.ReaderLayout = function(config, renderTo){
34653     var c = config || {size:{}};
34654     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
34655         north: c.north !== false ? Roo.apply({
34656             split:false,
34657             initialSize: 32,
34658             titlebar: false
34659         }, c.north) : false,
34660         west: c.west !== false ? Roo.apply({
34661             split:true,
34662             initialSize: 200,
34663             minSize: 175,
34664             maxSize: 400,
34665             titlebar: true,
34666             collapsible: true,
34667             animate: true,
34668             margins:{left:5,right:0,bottom:5,top:5},
34669             cmargins:{left:5,right:5,bottom:5,top:5}
34670         }, c.west) : false,
34671         east: c.east !== false ? Roo.apply({
34672             split:true,
34673             initialSize: 200,
34674             minSize: 175,
34675             maxSize: 400,
34676             titlebar: true,
34677             collapsible: true,
34678             animate: true,
34679             margins:{left:0,right:5,bottom:5,top:5},
34680             cmargins:{left:5,right:5,bottom:5,top:5}
34681         }, c.east) : false,
34682         center: Roo.apply({
34683             tabPosition: 'top',
34684             autoScroll:false,
34685             closeOnTab: true,
34686             titlebar:false,
34687             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
34688         }, c.center)
34689     });
34690
34691     this.el.addClass('x-reader');
34692
34693     this.beginUpdate();
34694
34695     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
34696         south: c.preview !== false ? Roo.apply({
34697             split:true,
34698             initialSize: 200,
34699             minSize: 100,
34700             autoScroll:true,
34701             collapsible:true,
34702             titlebar: true,
34703             cmargins:{top:5,left:0, right:0, bottom:0}
34704         }, c.preview) : false,
34705         center: Roo.apply({
34706             autoScroll:false,
34707             titlebar:false,
34708             minHeight:200
34709         }, c.listView)
34710     });
34711     this.add('center', new Roo.NestedLayoutPanel(inner,
34712             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
34713
34714     this.endUpdate();
34715
34716     this.regions.preview = inner.getRegion('south');
34717     this.regions.listView = inner.getRegion('center');
34718 };
34719
34720 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
34721  * Based on:
34722  * Ext JS Library 1.1.1
34723  * Copyright(c) 2006-2007, Ext JS, LLC.
34724  *
34725  * Originally Released Under LGPL - original licence link has changed is not relivant.
34726  *
34727  * Fork - LGPL
34728  * <script type="text/javascript">
34729  */
34730  
34731 /**
34732  * @class Roo.grid.Grid
34733  * @extends Roo.util.Observable
34734  * This class represents the primary interface of a component based grid control.
34735  * <br><br>Usage:<pre><code>
34736  var grid = new Roo.grid.Grid("my-container-id", {
34737      ds: myDataStore,
34738      cm: myColModel,
34739      selModel: mySelectionModel,
34740      autoSizeColumns: true,
34741      monitorWindowResize: false,
34742      trackMouseOver: true
34743  });
34744  // set any options
34745  grid.render();
34746  * </code></pre>
34747  * <b>Common Problems:</b><br/>
34748  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
34749  * element will correct this<br/>
34750  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
34751  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
34752  * are unpredictable.<br/>
34753  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
34754  * grid to calculate dimensions/offsets.<br/>
34755   * @constructor
34756  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
34757  * The container MUST have some type of size defined for the grid to fill. The container will be
34758  * automatically set to position relative if it isn't already.
34759  * @param {Object} config A config object that sets properties on this grid.
34760  */
34761 Roo.grid.Grid = function(container, config){
34762         // initialize the container
34763         this.container = Roo.get(container);
34764         this.container.update("");
34765         this.container.setStyle("overflow", "hidden");
34766     this.container.addClass('x-grid-container');
34767
34768     this.id = this.container.id;
34769
34770     Roo.apply(this, config);
34771     // check and correct shorthanded configs
34772     if(this.ds){
34773         this.dataSource = this.ds;
34774         delete this.ds;
34775     }
34776     if(this.cm){
34777         this.colModel = this.cm;
34778         delete this.cm;
34779     }
34780     if(this.sm){
34781         this.selModel = this.sm;
34782         delete this.sm;
34783     }
34784
34785     if (this.selModel) {
34786         this.selModel = Roo.factory(this.selModel, Roo.grid);
34787         this.sm = this.selModel;
34788         this.sm.xmodule = this.xmodule || false;
34789     }
34790     if (typeof(this.colModel.config) == 'undefined') {
34791         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34792         this.cm = this.colModel;
34793         this.cm.xmodule = this.xmodule || false;
34794     }
34795     if (this.dataSource) {
34796         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34797         this.ds = this.dataSource;
34798         this.ds.xmodule = this.xmodule || false;
34799          
34800     }
34801     
34802     
34803     
34804     if(this.width){
34805         this.container.setWidth(this.width);
34806     }
34807
34808     if(this.height){
34809         this.container.setHeight(this.height);
34810     }
34811     /** @private */
34812         this.addEvents({
34813         // raw events
34814         /**
34815          * @event click
34816          * The raw click event for the entire grid.
34817          * @param {Roo.EventObject} e
34818          */
34819         "click" : true,
34820         /**
34821          * @event dblclick
34822          * The raw dblclick event for the entire grid.
34823          * @param {Roo.EventObject} e
34824          */
34825         "dblclick" : true,
34826         /**
34827          * @event contextmenu
34828          * The raw contextmenu event for the entire grid.
34829          * @param {Roo.EventObject} e
34830          */
34831         "contextmenu" : true,
34832         /**
34833          * @event mousedown
34834          * The raw mousedown event for the entire grid.
34835          * @param {Roo.EventObject} e
34836          */
34837         "mousedown" : true,
34838         /**
34839          * @event mouseup
34840          * The raw mouseup event for the entire grid.
34841          * @param {Roo.EventObject} e
34842          */
34843         "mouseup" : true,
34844         /**
34845          * @event mouseover
34846          * The raw mouseover event for the entire grid.
34847          * @param {Roo.EventObject} e
34848          */
34849         "mouseover" : true,
34850         /**
34851          * @event mouseout
34852          * The raw mouseout event for the entire grid.
34853          * @param {Roo.EventObject} e
34854          */
34855         "mouseout" : true,
34856         /**
34857          * @event keypress
34858          * The raw keypress event for the entire grid.
34859          * @param {Roo.EventObject} e
34860          */
34861         "keypress" : true,
34862         /**
34863          * @event keydown
34864          * The raw keydown event for the entire grid.
34865          * @param {Roo.EventObject} e
34866          */
34867         "keydown" : true,
34868
34869         // custom events
34870
34871         /**
34872          * @event cellclick
34873          * Fires when a cell is clicked
34874          * @param {Grid} this
34875          * @param {Number} rowIndex
34876          * @param {Number} columnIndex
34877          * @param {Roo.EventObject} e
34878          */
34879         "cellclick" : true,
34880         /**
34881          * @event celldblclick
34882          * Fires when a cell is double clicked
34883          * @param {Grid} this
34884          * @param {Number} rowIndex
34885          * @param {Number} columnIndex
34886          * @param {Roo.EventObject} e
34887          */
34888         "celldblclick" : true,
34889         /**
34890          * @event rowclick
34891          * Fires when a row is clicked
34892          * @param {Grid} this
34893          * @param {Number} rowIndex
34894          * @param {Roo.EventObject} e
34895          */
34896         "rowclick" : true,
34897         /**
34898          * @event rowdblclick
34899          * Fires when a row is double clicked
34900          * @param {Grid} this
34901          * @param {Number} rowIndex
34902          * @param {Roo.EventObject} e
34903          */
34904         "rowdblclick" : true,
34905         /**
34906          * @event headerclick
34907          * Fires when a header is clicked
34908          * @param {Grid} this
34909          * @param {Number} columnIndex
34910          * @param {Roo.EventObject} e
34911          */
34912         "headerclick" : true,
34913         /**
34914          * @event headerdblclick
34915          * Fires when a header cell is double clicked
34916          * @param {Grid} this
34917          * @param {Number} columnIndex
34918          * @param {Roo.EventObject} e
34919          */
34920         "headerdblclick" : true,
34921         /**
34922          * @event rowcontextmenu
34923          * Fires when a row is right clicked
34924          * @param {Grid} this
34925          * @param {Number} rowIndex
34926          * @param {Roo.EventObject} e
34927          */
34928         "rowcontextmenu" : true,
34929         /**
34930          * @event cellcontextmenu
34931          * Fires when a cell is right clicked
34932          * @param {Grid} this
34933          * @param {Number} rowIndex
34934          * @param {Number} cellIndex
34935          * @param {Roo.EventObject} e
34936          */
34937          "cellcontextmenu" : true,
34938         /**
34939          * @event headercontextmenu
34940          * Fires when a header is right clicked
34941          * @param {Grid} this
34942          * @param {Number} columnIndex
34943          * @param {Roo.EventObject} e
34944          */
34945         "headercontextmenu" : true,
34946         /**
34947          * @event bodyscroll
34948          * Fires when the body element is scrolled
34949          * @param {Number} scrollLeft
34950          * @param {Number} scrollTop
34951          */
34952         "bodyscroll" : true,
34953         /**
34954          * @event columnresize
34955          * Fires when the user resizes a column
34956          * @param {Number} columnIndex
34957          * @param {Number} newSize
34958          */
34959         "columnresize" : true,
34960         /**
34961          * @event columnmove
34962          * Fires when the user moves a column
34963          * @param {Number} oldIndex
34964          * @param {Number} newIndex
34965          */
34966         "columnmove" : true,
34967         /**
34968          * @event startdrag
34969          * Fires when row(s) start being dragged
34970          * @param {Grid} this
34971          * @param {Roo.GridDD} dd The drag drop object
34972          * @param {event} e The raw browser event
34973          */
34974         "startdrag" : true,
34975         /**
34976          * @event enddrag
34977          * Fires when a drag operation is complete
34978          * @param {Grid} this
34979          * @param {Roo.GridDD} dd The drag drop object
34980          * @param {event} e The raw browser event
34981          */
34982         "enddrag" : true,
34983         /**
34984          * @event dragdrop
34985          * Fires when dragged row(s) are dropped on a valid DD target
34986          * @param {Grid} this
34987          * @param {Roo.GridDD} dd The drag drop object
34988          * @param {String} targetId The target drag drop object
34989          * @param {event} e The raw browser event
34990          */
34991         "dragdrop" : true,
34992         /**
34993          * @event dragover
34994          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34995          * @param {Grid} this
34996          * @param {Roo.GridDD} dd The drag drop object
34997          * @param {String} targetId The target drag drop object
34998          * @param {event} e The raw browser event
34999          */
35000         "dragover" : true,
35001         /**
35002          * @event dragenter
35003          *  Fires when the dragged row(s) first cross another DD target while being dragged
35004          * @param {Grid} this
35005          * @param {Roo.GridDD} dd The drag drop object
35006          * @param {String} targetId The target drag drop object
35007          * @param {event} e The raw browser event
35008          */
35009         "dragenter" : true,
35010         /**
35011          * @event dragout
35012          * Fires when the dragged row(s) leave another DD target while being dragged
35013          * @param {Grid} this
35014          * @param {Roo.GridDD} dd The drag drop object
35015          * @param {String} targetId The target drag drop object
35016          * @param {event} e The raw browser event
35017          */
35018         "dragout" : true,
35019         /**
35020          * @event rowclass
35021          * Fires when a row is rendered, so you can change add a style to it.
35022          * @param {GridView} gridview   The grid view
35023          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
35024          */
35025         'rowclass' : true,
35026
35027         /**
35028          * @event render
35029          * Fires when the grid is rendered
35030          * @param {Grid} grid
35031          */
35032         'render' : true
35033     });
35034
35035     Roo.grid.Grid.superclass.constructor.call(this);
35036 };
35037 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
35038     
35039     /**
35040      * @cfg {String} ddGroup - drag drop group.
35041      */
35042
35043     /**
35044      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
35045      */
35046     minColumnWidth : 25,
35047
35048     /**
35049      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
35050      * <b>on initial render.</b> It is more efficient to explicitly size the columns
35051      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
35052      */
35053     autoSizeColumns : false,
35054
35055     /**
35056      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
35057      */
35058     autoSizeHeaders : true,
35059
35060     /**
35061      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
35062      */
35063     monitorWindowResize : true,
35064
35065     /**
35066      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
35067      * rows measured to get a columns size. Default is 0 (all rows).
35068      */
35069     maxRowsToMeasure : 0,
35070
35071     /**
35072      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
35073      */
35074     trackMouseOver : true,
35075
35076     /**
35077     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
35078     */
35079     
35080     /**
35081     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
35082     */
35083     enableDragDrop : false,
35084     
35085     /**
35086     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
35087     */
35088     enableColumnMove : true,
35089     
35090     /**
35091     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
35092     */
35093     enableColumnHide : true,
35094     
35095     /**
35096     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
35097     */
35098     enableRowHeightSync : false,
35099     
35100     /**
35101     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
35102     */
35103     stripeRows : true,
35104     
35105     /**
35106     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
35107     */
35108     autoHeight : false,
35109
35110     /**
35111      * @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.
35112      */
35113     autoExpandColumn : false,
35114
35115     /**
35116     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
35117     * Default is 50.
35118     */
35119     autoExpandMin : 50,
35120
35121     /**
35122     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
35123     */
35124     autoExpandMax : 1000,
35125
35126     /**
35127     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
35128     */
35129     view : null,
35130
35131     /**
35132     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
35133     */
35134     loadMask : false,
35135     /**
35136     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
35137     */
35138     dropTarget: false,
35139     
35140    
35141     
35142     // private
35143     rendered : false,
35144
35145     /**
35146     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
35147     * of a fixed width. Default is false.
35148     */
35149     /**
35150     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
35151     */
35152     /**
35153      * Called once after all setup has been completed and the grid is ready to be rendered.
35154      * @return {Roo.grid.Grid} this
35155      */
35156     render : function()
35157     {
35158         var c = this.container;
35159         // try to detect autoHeight/width mode
35160         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
35161             this.autoHeight = true;
35162         }
35163         var view = this.getView();
35164         view.init(this);
35165
35166         c.on("click", this.onClick, this);
35167         c.on("dblclick", this.onDblClick, this);
35168         c.on("contextmenu", this.onContextMenu, this);
35169         c.on("keydown", this.onKeyDown, this);
35170
35171         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
35172
35173         this.getSelectionModel().init(this);
35174
35175         view.render();
35176
35177         if(this.loadMask){
35178             this.loadMask = new Roo.LoadMask(this.container,
35179                     Roo.apply({store:this.dataSource}, this.loadMask));
35180         }
35181         
35182         
35183         if (this.toolbar && this.toolbar.xtype) {
35184             this.toolbar.container = this.getView().getHeaderPanel(true);
35185             this.toolbar = new Roo.Toolbar(this.toolbar);
35186         }
35187         if (this.footer && this.footer.xtype) {
35188             this.footer.dataSource = this.getDataSource();
35189             this.footer.container = this.getView().getFooterPanel(true);
35190             this.footer = Roo.factory(this.footer, Roo);
35191         }
35192         if (this.dropTarget && this.dropTarget.xtype) {
35193             delete this.dropTarget.xtype;
35194             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
35195         }
35196         
35197         
35198         this.rendered = true;
35199         this.fireEvent('render', this);
35200         return this;
35201     },
35202
35203         /**
35204          * Reconfigures the grid to use a different Store and Column Model.
35205          * The View will be bound to the new objects and refreshed.
35206          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
35207          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
35208          */
35209     reconfigure : function(dataSource, colModel){
35210         if(this.loadMask){
35211             this.loadMask.destroy();
35212             this.loadMask = new Roo.LoadMask(this.container,
35213                     Roo.apply({store:dataSource}, this.loadMask));
35214         }
35215         this.view.bind(dataSource, colModel);
35216         this.dataSource = dataSource;
35217         this.colModel = colModel;
35218         this.view.refresh(true);
35219     },
35220
35221     // private
35222     onKeyDown : function(e){
35223         this.fireEvent("keydown", e);
35224     },
35225
35226     /**
35227      * Destroy this grid.
35228      * @param {Boolean} removeEl True to remove the element
35229      */
35230     destroy : function(removeEl, keepListeners){
35231         if(this.loadMask){
35232             this.loadMask.destroy();
35233         }
35234         var c = this.container;
35235         c.removeAllListeners();
35236         this.view.destroy();
35237         this.colModel.purgeListeners();
35238         if(!keepListeners){
35239             this.purgeListeners();
35240         }
35241         c.update("");
35242         if(removeEl === true){
35243             c.remove();
35244         }
35245     },
35246
35247     // private
35248     processEvent : function(name, e){
35249         this.fireEvent(name, e);
35250         var t = e.getTarget();
35251         var v = this.view;
35252         var header = v.findHeaderIndex(t);
35253         if(header !== false){
35254             this.fireEvent("header" + name, this, header, e);
35255         }else{
35256             var row = v.findRowIndex(t);
35257             var cell = v.findCellIndex(t);
35258             if(row !== false){
35259                 this.fireEvent("row" + name, this, row, e);
35260                 if(cell !== false){
35261                     this.fireEvent("cell" + name, this, row, cell, e);
35262                 }
35263             }
35264         }
35265     },
35266
35267     // private
35268     onClick : function(e){
35269         this.processEvent("click", e);
35270     },
35271
35272     // private
35273     onContextMenu : function(e, t){
35274         this.processEvent("contextmenu", e);
35275     },
35276
35277     // private
35278     onDblClick : function(e){
35279         this.processEvent("dblclick", e);
35280     },
35281
35282     // private
35283     walkCells : function(row, col, step, fn, scope){
35284         var cm = this.colModel, clen = cm.getColumnCount();
35285         var ds = this.dataSource, rlen = ds.getCount(), first = true;
35286         if(step < 0){
35287             if(col < 0){
35288                 row--;
35289                 first = false;
35290             }
35291             while(row >= 0){
35292                 if(!first){
35293                     col = clen-1;
35294                 }
35295                 first = false;
35296                 while(col >= 0){
35297                     if(fn.call(scope || this, row, col, cm) === true){
35298                         return [row, col];
35299                     }
35300                     col--;
35301                 }
35302                 row--;
35303             }
35304         } else {
35305             if(col >= clen){
35306                 row++;
35307                 first = false;
35308             }
35309             while(row < rlen){
35310                 if(!first){
35311                     col = 0;
35312                 }
35313                 first = false;
35314                 while(col < clen){
35315                     if(fn.call(scope || this, row, col, cm) === true){
35316                         return [row, col];
35317                     }
35318                     col++;
35319                 }
35320                 row++;
35321             }
35322         }
35323         return null;
35324     },
35325
35326     // private
35327     getSelections : function(){
35328         return this.selModel.getSelections();
35329     },
35330
35331     /**
35332      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
35333      * but if manual update is required this method will initiate it.
35334      */
35335     autoSize : function(){
35336         if(this.rendered){
35337             this.view.layout();
35338             if(this.view.adjustForScroll){
35339                 this.view.adjustForScroll();
35340             }
35341         }
35342     },
35343
35344     /**
35345      * Returns the grid's underlying element.
35346      * @return {Element} The element
35347      */
35348     getGridEl : function(){
35349         return this.container;
35350     },
35351
35352     // private for compatibility, overridden by editor grid
35353     stopEditing : function(){},
35354
35355     /**
35356      * Returns the grid's SelectionModel.
35357      * @return {SelectionModel}
35358      */
35359     getSelectionModel : function(){
35360         if(!this.selModel){
35361             this.selModel = new Roo.grid.RowSelectionModel();
35362         }
35363         return this.selModel;
35364     },
35365
35366     /**
35367      * Returns the grid's DataSource.
35368      * @return {DataSource}
35369      */
35370     getDataSource : function(){
35371         return this.dataSource;
35372     },
35373
35374     /**
35375      * Returns the grid's ColumnModel.
35376      * @return {ColumnModel}
35377      */
35378     getColumnModel : function(){
35379         return this.colModel;
35380     },
35381
35382     /**
35383      * Returns the grid's GridView object.
35384      * @return {GridView}
35385      */
35386     getView : function(){
35387         if(!this.view){
35388             this.view = new Roo.grid.GridView(this.viewConfig);
35389         }
35390         return this.view;
35391     },
35392     /**
35393      * Called to get grid's drag proxy text, by default returns this.ddText.
35394      * @return {String}
35395      */
35396     getDragDropText : function(){
35397         var count = this.selModel.getCount();
35398         return String.format(this.ddText, count, count == 1 ? '' : 's');
35399     }
35400 });
35401 /**
35402  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
35403  * %0 is replaced with the number of selected rows.
35404  * @type String
35405  */
35406 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
35407  * Based on:
35408  * Ext JS Library 1.1.1
35409  * Copyright(c) 2006-2007, Ext JS, LLC.
35410  *
35411  * Originally Released Under LGPL - original licence link has changed is not relivant.
35412  *
35413  * Fork - LGPL
35414  * <script type="text/javascript">
35415  */
35416  
35417 Roo.grid.AbstractGridView = function(){
35418         this.grid = null;
35419         
35420         this.events = {
35421             "beforerowremoved" : true,
35422             "beforerowsinserted" : true,
35423             "beforerefresh" : true,
35424             "rowremoved" : true,
35425             "rowsinserted" : true,
35426             "rowupdated" : true,
35427             "refresh" : true
35428         };
35429     Roo.grid.AbstractGridView.superclass.constructor.call(this);
35430 };
35431
35432 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
35433     rowClass : "x-grid-row",
35434     cellClass : "x-grid-cell",
35435     tdClass : "x-grid-td",
35436     hdClass : "x-grid-hd",
35437     splitClass : "x-grid-hd-split",
35438     
35439         init: function(grid){
35440         this.grid = grid;
35441                 var cid = this.grid.getGridEl().id;
35442         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
35443         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
35444         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
35445         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
35446         },
35447         
35448         getColumnRenderers : function(){
35449         var renderers = [];
35450         var cm = this.grid.colModel;
35451         var colCount = cm.getColumnCount();
35452         for(var i = 0; i < colCount; i++){
35453             renderers[i] = cm.getRenderer(i);
35454         }
35455         return renderers;
35456     },
35457     
35458     getColumnIds : function(){
35459         var ids = [];
35460         var cm = this.grid.colModel;
35461         var colCount = cm.getColumnCount();
35462         for(var i = 0; i < colCount; i++){
35463             ids[i] = cm.getColumnId(i);
35464         }
35465         return ids;
35466     },
35467     
35468     getDataIndexes : function(){
35469         if(!this.indexMap){
35470             this.indexMap = this.buildIndexMap();
35471         }
35472         return this.indexMap.colToData;
35473     },
35474     
35475     getColumnIndexByDataIndex : function(dataIndex){
35476         if(!this.indexMap){
35477             this.indexMap = this.buildIndexMap();
35478         }
35479         return this.indexMap.dataToCol[dataIndex];
35480     },
35481     
35482     /**
35483      * Set a css style for a column dynamically. 
35484      * @param {Number} colIndex The index of the column
35485      * @param {String} name The css property name
35486      * @param {String} value The css value
35487      */
35488     setCSSStyle : function(colIndex, name, value){
35489         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
35490         Roo.util.CSS.updateRule(selector, name, value);
35491     },
35492     
35493     generateRules : function(cm){
35494         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
35495         Roo.util.CSS.removeStyleSheet(rulesId);
35496         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35497             var cid = cm.getColumnId(i);
35498             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
35499                          this.tdSelector, cid, " {\n}\n",
35500                          this.hdSelector, cid, " {\n}\n",
35501                          this.splitSelector, cid, " {\n}\n");
35502         }
35503         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35504     }
35505 });/*
35506  * Based on:
35507  * Ext JS Library 1.1.1
35508  * Copyright(c) 2006-2007, Ext JS, LLC.
35509  *
35510  * Originally Released Under LGPL - original licence link has changed is not relivant.
35511  *
35512  * Fork - LGPL
35513  * <script type="text/javascript">
35514  */
35515
35516 // private
35517 // This is a support class used internally by the Grid components
35518 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
35519     this.grid = grid;
35520     this.view = grid.getView();
35521     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35522     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
35523     if(hd2){
35524         this.setHandleElId(Roo.id(hd));
35525         this.setOuterHandleElId(Roo.id(hd2));
35526     }
35527     this.scroll = false;
35528 };
35529 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
35530     maxDragWidth: 120,
35531     getDragData : function(e){
35532         var t = Roo.lib.Event.getTarget(e);
35533         var h = this.view.findHeaderCell(t);
35534         if(h){
35535             return {ddel: h.firstChild, header:h};
35536         }
35537         return false;
35538     },
35539
35540     onInitDrag : function(e){
35541         this.view.headersDisabled = true;
35542         var clone = this.dragData.ddel.cloneNode(true);
35543         clone.id = Roo.id();
35544         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
35545         this.proxy.update(clone);
35546         return true;
35547     },
35548
35549     afterValidDrop : function(){
35550         var v = this.view;
35551         setTimeout(function(){
35552             v.headersDisabled = false;
35553         }, 50);
35554     },
35555
35556     afterInvalidDrop : function(){
35557         var v = this.view;
35558         setTimeout(function(){
35559             v.headersDisabled = false;
35560         }, 50);
35561     }
35562 });
35563 /*
35564  * Based on:
35565  * Ext JS Library 1.1.1
35566  * Copyright(c) 2006-2007, Ext JS, LLC.
35567  *
35568  * Originally Released Under LGPL - original licence link has changed is not relivant.
35569  *
35570  * Fork - LGPL
35571  * <script type="text/javascript">
35572  */
35573 // private
35574 // This is a support class used internally by the Grid components
35575 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
35576     this.grid = grid;
35577     this.view = grid.getView();
35578     // split the proxies so they don't interfere with mouse events
35579     this.proxyTop = Roo.DomHelper.append(document.body, {
35580         cls:"col-move-top", html:"&#160;"
35581     }, true);
35582     this.proxyBottom = Roo.DomHelper.append(document.body, {
35583         cls:"col-move-bottom", html:"&#160;"
35584     }, true);
35585     this.proxyTop.hide = this.proxyBottom.hide = function(){
35586         this.setLeftTop(-100,-100);
35587         this.setStyle("visibility", "hidden");
35588     };
35589     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
35590     // temporarily disabled
35591     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
35592     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
35593 };
35594 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
35595     proxyOffsets : [-4, -9],
35596     fly: Roo.Element.fly,
35597
35598     getTargetFromEvent : function(e){
35599         var t = Roo.lib.Event.getTarget(e);
35600         var cindex = this.view.findCellIndex(t);
35601         if(cindex !== false){
35602             return this.view.getHeaderCell(cindex);
35603         }
35604         return null;
35605     },
35606
35607     nextVisible : function(h){
35608         var v = this.view, cm = this.grid.colModel;
35609         h = h.nextSibling;
35610         while(h){
35611             if(!cm.isHidden(v.getCellIndex(h))){
35612                 return h;
35613             }
35614             h = h.nextSibling;
35615         }
35616         return null;
35617     },
35618
35619     prevVisible : function(h){
35620         var v = this.view, cm = this.grid.colModel;
35621         h = h.prevSibling;
35622         while(h){
35623             if(!cm.isHidden(v.getCellIndex(h))){
35624                 return h;
35625             }
35626             h = h.prevSibling;
35627         }
35628         return null;
35629     },
35630
35631     positionIndicator : function(h, n, e){
35632         var x = Roo.lib.Event.getPageX(e);
35633         var r = Roo.lib.Dom.getRegion(n.firstChild);
35634         var px, pt, py = r.top + this.proxyOffsets[1];
35635         if((r.right - x) <= (r.right-r.left)/2){
35636             px = r.right+this.view.borderWidth;
35637             pt = "after";
35638         }else{
35639             px = r.left;
35640             pt = "before";
35641         }
35642         var oldIndex = this.view.getCellIndex(h);
35643         var newIndex = this.view.getCellIndex(n);
35644
35645         if(this.grid.colModel.isFixed(newIndex)){
35646             return false;
35647         }
35648
35649         var locked = this.grid.colModel.isLocked(newIndex);
35650
35651         if(pt == "after"){
35652             newIndex++;
35653         }
35654         if(oldIndex < newIndex){
35655             newIndex--;
35656         }
35657         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
35658             return false;
35659         }
35660         px +=  this.proxyOffsets[0];
35661         this.proxyTop.setLeftTop(px, py);
35662         this.proxyTop.show();
35663         if(!this.bottomOffset){
35664             this.bottomOffset = this.view.mainHd.getHeight();
35665         }
35666         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
35667         this.proxyBottom.show();
35668         return pt;
35669     },
35670
35671     onNodeEnter : function(n, dd, e, data){
35672         if(data.header != n){
35673             this.positionIndicator(data.header, n, e);
35674         }
35675     },
35676
35677     onNodeOver : function(n, dd, e, data){
35678         var result = false;
35679         if(data.header != n){
35680             result = this.positionIndicator(data.header, n, e);
35681         }
35682         if(!result){
35683             this.proxyTop.hide();
35684             this.proxyBottom.hide();
35685         }
35686         return result ? this.dropAllowed : this.dropNotAllowed;
35687     },
35688
35689     onNodeOut : function(n, dd, e, data){
35690         this.proxyTop.hide();
35691         this.proxyBottom.hide();
35692     },
35693
35694     onNodeDrop : function(n, dd, e, data){
35695         var h = data.header;
35696         if(h != n){
35697             var cm = this.grid.colModel;
35698             var x = Roo.lib.Event.getPageX(e);
35699             var r = Roo.lib.Dom.getRegion(n.firstChild);
35700             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
35701             var oldIndex = this.view.getCellIndex(h);
35702             var newIndex = this.view.getCellIndex(n);
35703             var locked = cm.isLocked(newIndex);
35704             if(pt == "after"){
35705                 newIndex++;
35706             }
35707             if(oldIndex < newIndex){
35708                 newIndex--;
35709             }
35710             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
35711                 return false;
35712             }
35713             cm.setLocked(oldIndex, locked, true);
35714             cm.moveColumn(oldIndex, newIndex);
35715             this.grid.fireEvent("columnmove", oldIndex, newIndex);
35716             return true;
35717         }
35718         return false;
35719     }
35720 });
35721 /*
35722  * Based on:
35723  * Ext JS Library 1.1.1
35724  * Copyright(c) 2006-2007, Ext JS, LLC.
35725  *
35726  * Originally Released Under LGPL - original licence link has changed is not relivant.
35727  *
35728  * Fork - LGPL
35729  * <script type="text/javascript">
35730  */
35731   
35732 /**
35733  * @class Roo.grid.GridView
35734  * @extends Roo.util.Observable
35735  *
35736  * @constructor
35737  * @param {Object} config
35738  */
35739 Roo.grid.GridView = function(config){
35740     Roo.grid.GridView.superclass.constructor.call(this);
35741     this.el = null;
35742
35743     Roo.apply(this, config);
35744 };
35745
35746 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
35747
35748     unselectable :  'unselectable="on"',
35749     unselectableCls :  'x-unselectable',
35750     
35751     
35752     rowClass : "x-grid-row",
35753
35754     cellClass : "x-grid-col",
35755
35756     tdClass : "x-grid-td",
35757
35758     hdClass : "x-grid-hd",
35759
35760     splitClass : "x-grid-split",
35761
35762     sortClasses : ["sort-asc", "sort-desc"],
35763
35764     enableMoveAnim : false,
35765
35766     hlColor: "C3DAF9",
35767
35768     dh : Roo.DomHelper,
35769
35770     fly : Roo.Element.fly,
35771
35772     css : Roo.util.CSS,
35773
35774     borderWidth: 1,
35775
35776     splitOffset: 3,
35777
35778     scrollIncrement : 22,
35779
35780     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35781
35782     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35783
35784     bind : function(ds, cm){
35785         if(this.ds){
35786             this.ds.un("load", this.onLoad, this);
35787             this.ds.un("datachanged", this.onDataChange, this);
35788             this.ds.un("add", this.onAdd, this);
35789             this.ds.un("remove", this.onRemove, this);
35790             this.ds.un("update", this.onUpdate, this);
35791             this.ds.un("clear", this.onClear, this);
35792         }
35793         if(ds){
35794             ds.on("load", this.onLoad, this);
35795             ds.on("datachanged", this.onDataChange, this);
35796             ds.on("add", this.onAdd, this);
35797             ds.on("remove", this.onRemove, this);
35798             ds.on("update", this.onUpdate, this);
35799             ds.on("clear", this.onClear, this);
35800         }
35801         this.ds = ds;
35802
35803         if(this.cm){
35804             this.cm.un("widthchange", this.onColWidthChange, this);
35805             this.cm.un("headerchange", this.onHeaderChange, this);
35806             this.cm.un("hiddenchange", this.onHiddenChange, this);
35807             this.cm.un("columnmoved", this.onColumnMove, this);
35808             this.cm.un("columnlockchange", this.onColumnLock, this);
35809         }
35810         if(cm){
35811             this.generateRules(cm);
35812             cm.on("widthchange", this.onColWidthChange, this);
35813             cm.on("headerchange", this.onHeaderChange, this);
35814             cm.on("hiddenchange", this.onHiddenChange, this);
35815             cm.on("columnmoved", this.onColumnMove, this);
35816             cm.on("columnlockchange", this.onColumnLock, this);
35817         }
35818         this.cm = cm;
35819     },
35820
35821     init: function(grid){
35822         Roo.grid.GridView.superclass.init.call(this, grid);
35823
35824         this.bind(grid.dataSource, grid.colModel);
35825
35826         grid.on("headerclick", this.handleHeaderClick, this);
35827
35828         if(grid.trackMouseOver){
35829             grid.on("mouseover", this.onRowOver, this);
35830             grid.on("mouseout", this.onRowOut, this);
35831         }
35832         grid.cancelTextSelection = function(){};
35833         this.gridId = grid.id;
35834
35835         var tpls = this.templates || {};
35836
35837         if(!tpls.master){
35838             tpls.master = new Roo.Template(
35839                '<div class="x-grid" hidefocus="true">',
35840                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35841                   '<div class="x-grid-topbar"></div>',
35842                   '<div class="x-grid-scroller"><div></div></div>',
35843                   '<div class="x-grid-locked">',
35844                       '<div class="x-grid-header">{lockedHeader}</div>',
35845                       '<div class="x-grid-body">{lockedBody}</div>',
35846                   "</div>",
35847                   '<div class="x-grid-viewport">',
35848                       '<div class="x-grid-header">{header}</div>',
35849                       '<div class="x-grid-body">{body}</div>',
35850                   "</div>",
35851                   '<div class="x-grid-bottombar"></div>',
35852                  
35853                   '<div class="x-grid-resize-proxy">&#160;</div>',
35854                "</div>"
35855             );
35856             tpls.master.disableformats = true;
35857         }
35858
35859         if(!tpls.header){
35860             tpls.header = new Roo.Template(
35861                '<table border="0" cellspacing="0" cellpadding="0">',
35862                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35863                "</table>{splits}"
35864             );
35865             tpls.header.disableformats = true;
35866         }
35867         tpls.header.compile();
35868
35869         if(!tpls.hcell){
35870             tpls.hcell = new Roo.Template(
35871                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35872                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35873                 "</div></td>"
35874              );
35875              tpls.hcell.disableFormats = true;
35876         }
35877         tpls.hcell.compile();
35878
35879         if(!tpls.hsplit){
35880             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
35881                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
35882             tpls.hsplit.disableFormats = true;
35883         }
35884         tpls.hsplit.compile();
35885
35886         if(!tpls.body){
35887             tpls.body = new Roo.Template(
35888                '<table border="0" cellspacing="0" cellpadding="0">',
35889                "<tbody>{rows}</tbody>",
35890                "</table>"
35891             );
35892             tpls.body.disableFormats = true;
35893         }
35894         tpls.body.compile();
35895
35896         if(!tpls.row){
35897             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35898             tpls.row.disableFormats = true;
35899         }
35900         tpls.row.compile();
35901
35902         if(!tpls.cell){
35903             tpls.cell = new Roo.Template(
35904                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35905                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
35906                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
35907                 "</td>"
35908             );
35909             tpls.cell.disableFormats = true;
35910         }
35911         tpls.cell.compile();
35912
35913         this.templates = tpls;
35914     },
35915
35916     // remap these for backwards compat
35917     onColWidthChange : function(){
35918         this.updateColumns.apply(this, arguments);
35919     },
35920     onHeaderChange : function(){
35921         this.updateHeaders.apply(this, arguments);
35922     }, 
35923     onHiddenChange : function(){
35924         this.handleHiddenChange.apply(this, arguments);
35925     },
35926     onColumnMove : function(){
35927         this.handleColumnMove.apply(this, arguments);
35928     },
35929     onColumnLock : function(){
35930         this.handleLockChange.apply(this, arguments);
35931     },
35932
35933     onDataChange : function(){
35934         this.refresh();
35935         this.updateHeaderSortState();
35936     },
35937
35938     onClear : function(){
35939         this.refresh();
35940     },
35941
35942     onUpdate : function(ds, record){
35943         this.refreshRow(record);
35944     },
35945
35946     refreshRow : function(record){
35947         var ds = this.ds, index;
35948         if(typeof record == 'number'){
35949             index = record;
35950             record = ds.getAt(index);
35951         }else{
35952             index = ds.indexOf(record);
35953         }
35954         this.insertRows(ds, index, index, true);
35955         this.onRemove(ds, record, index+1, true);
35956         this.syncRowHeights(index, index);
35957         this.layout();
35958         this.fireEvent("rowupdated", this, index, record);
35959     },
35960
35961     onAdd : function(ds, records, index){
35962         this.insertRows(ds, index, index + (records.length-1));
35963     },
35964
35965     onRemove : function(ds, record, index, isUpdate){
35966         if(isUpdate !== true){
35967             this.fireEvent("beforerowremoved", this, index, record);
35968         }
35969         var bt = this.getBodyTable(), lt = this.getLockedTable();
35970         if(bt.rows[index]){
35971             bt.firstChild.removeChild(bt.rows[index]);
35972         }
35973         if(lt.rows[index]){
35974             lt.firstChild.removeChild(lt.rows[index]);
35975         }
35976         if(isUpdate !== true){
35977             this.stripeRows(index);
35978             this.syncRowHeights(index, index);
35979             this.layout();
35980             this.fireEvent("rowremoved", this, index, record);
35981         }
35982     },
35983
35984     onLoad : function(){
35985         this.scrollToTop();
35986     },
35987
35988     /**
35989      * Scrolls the grid to the top
35990      */
35991     scrollToTop : function(){
35992         if(this.scroller){
35993             this.scroller.dom.scrollTop = 0;
35994             this.syncScroll();
35995         }
35996     },
35997
35998     /**
35999      * Gets a panel in the header of the grid that can be used for toolbars etc.
36000      * After modifying the contents of this panel a call to grid.autoSize() may be
36001      * required to register any changes in size.
36002      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
36003      * @return Roo.Element
36004      */
36005     getHeaderPanel : function(doShow){
36006         if(doShow){
36007             this.headerPanel.show();
36008         }
36009         return this.headerPanel;
36010     },
36011
36012     /**
36013      * Gets a panel in the footer of the grid that can be used for toolbars etc.
36014      * After modifying the contents of this panel a call to grid.autoSize() may be
36015      * required to register any changes in size.
36016      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
36017      * @return Roo.Element
36018      */
36019     getFooterPanel : function(doShow){
36020         if(doShow){
36021             this.footerPanel.show();
36022         }
36023         return this.footerPanel;
36024     },
36025
36026     initElements : function(){
36027         var E = Roo.Element;
36028         var el = this.grid.getGridEl().dom.firstChild;
36029         var cs = el.childNodes;
36030
36031         this.el = new E(el);
36032         
36033          this.focusEl = new E(el.firstChild);
36034         this.focusEl.swallowEvent("click", true);
36035         
36036         this.headerPanel = new E(cs[1]);
36037         this.headerPanel.enableDisplayMode("block");
36038
36039         this.scroller = new E(cs[2]);
36040         this.scrollSizer = new E(this.scroller.dom.firstChild);
36041
36042         this.lockedWrap = new E(cs[3]);
36043         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
36044         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
36045
36046         this.mainWrap = new E(cs[4]);
36047         this.mainHd = new E(this.mainWrap.dom.firstChild);
36048         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
36049
36050         this.footerPanel = new E(cs[5]);
36051         this.footerPanel.enableDisplayMode("block");
36052
36053         this.resizeProxy = new E(cs[6]);
36054
36055         this.headerSelector = String.format(
36056            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
36057            this.lockedHd.id, this.mainHd.id
36058         );
36059
36060         this.splitterSelector = String.format(
36061            '#{0} div.x-grid-split, #{1} div.x-grid-split',
36062            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
36063         );
36064     },
36065     idToCssName : function(s)
36066     {
36067         return s.replace(/[^a-z0-9]+/ig, '-');
36068     },
36069
36070     getHeaderCell : function(index){
36071         return Roo.DomQuery.select(this.headerSelector)[index];
36072     },
36073
36074     getHeaderCellMeasure : function(index){
36075         return this.getHeaderCell(index).firstChild;
36076     },
36077
36078     getHeaderCellText : function(index){
36079         return this.getHeaderCell(index).firstChild.firstChild;
36080     },
36081
36082     getLockedTable : function(){
36083         return this.lockedBody.dom.firstChild;
36084     },
36085
36086     getBodyTable : function(){
36087         return this.mainBody.dom.firstChild;
36088     },
36089
36090     getLockedRow : function(index){
36091         return this.getLockedTable().rows[index];
36092     },
36093
36094     getRow : function(index){
36095         return this.getBodyTable().rows[index];
36096     },
36097
36098     getRowComposite : function(index){
36099         if(!this.rowEl){
36100             this.rowEl = new Roo.CompositeElementLite();
36101         }
36102         var els = [], lrow, mrow;
36103         if(lrow = this.getLockedRow(index)){
36104             els.push(lrow);
36105         }
36106         if(mrow = this.getRow(index)){
36107             els.push(mrow);
36108         }
36109         this.rowEl.elements = els;
36110         return this.rowEl;
36111     },
36112     /**
36113      * Gets the 'td' of the cell
36114      * 
36115      * @param {Integer} rowIndex row to select
36116      * @param {Integer} colIndex column to select
36117      * 
36118      * @return {Object} 
36119      */
36120     getCell : function(rowIndex, colIndex){
36121         var locked = this.cm.getLockedCount();
36122         var source;
36123         if(colIndex < locked){
36124             source = this.lockedBody.dom.firstChild;
36125         }else{
36126             source = this.mainBody.dom.firstChild;
36127             colIndex -= locked;
36128         }
36129         return source.rows[rowIndex].childNodes[colIndex];
36130     },
36131
36132     getCellText : function(rowIndex, colIndex){
36133         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
36134     },
36135
36136     getCellBox : function(cell){
36137         var b = this.fly(cell).getBox();
36138         if(Roo.isOpera){ // opera fails to report the Y
36139             b.y = cell.offsetTop + this.mainBody.getY();
36140         }
36141         return b;
36142     },
36143
36144     getCellIndex : function(cell){
36145         var id = String(cell.className).match(this.cellRE);
36146         if(id){
36147             return parseInt(id[1], 10);
36148         }
36149         return 0;
36150     },
36151
36152     findHeaderIndex : function(n){
36153         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36154         return r ? this.getCellIndex(r) : false;
36155     },
36156
36157     findHeaderCell : function(n){
36158         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
36159         return r ? r : false;
36160     },
36161
36162     findRowIndex : function(n){
36163         if(!n){
36164             return false;
36165         }
36166         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
36167         return r ? r.rowIndex : false;
36168     },
36169
36170     findCellIndex : function(node){
36171         var stop = this.el.dom;
36172         while(node && node != stop){
36173             if(this.findRE.test(node.className)){
36174                 return this.getCellIndex(node);
36175             }
36176             node = node.parentNode;
36177         }
36178         return false;
36179     },
36180
36181     getColumnId : function(index){
36182         return this.cm.getColumnId(index);
36183     },
36184
36185     getSplitters : function()
36186     {
36187         if(this.splitterSelector){
36188            return Roo.DomQuery.select(this.splitterSelector);
36189         }else{
36190             return null;
36191       }
36192     },
36193
36194     getSplitter : function(index){
36195         return this.getSplitters()[index];
36196     },
36197
36198     onRowOver : function(e, t){
36199         var row;
36200         if((row = this.findRowIndex(t)) !== false){
36201             this.getRowComposite(row).addClass("x-grid-row-over");
36202         }
36203     },
36204
36205     onRowOut : function(e, t){
36206         var row;
36207         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
36208             this.getRowComposite(row).removeClass("x-grid-row-over");
36209         }
36210     },
36211
36212     renderHeaders : function(){
36213         var cm = this.cm;
36214         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
36215         var cb = [], lb = [], sb = [], lsb = [], p = {};
36216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36217             p.cellId = "x-grid-hd-0-" + i;
36218             p.splitId = "x-grid-csplit-0-" + i;
36219             p.id = cm.getColumnId(i);
36220             p.title = cm.getColumnTooltip(i) || "";
36221             p.value = cm.getColumnHeader(i) || "";
36222             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
36223             if(!cm.isLocked(i)){
36224                 cb[cb.length] = ct.apply(p);
36225                 sb[sb.length] = st.apply(p);
36226             }else{
36227                 lb[lb.length] = ct.apply(p);
36228                 lsb[lsb.length] = st.apply(p);
36229             }
36230         }
36231         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
36232                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
36233     },
36234
36235     updateHeaders : function(){
36236         var html = this.renderHeaders();
36237         this.lockedHd.update(html[0]);
36238         this.mainHd.update(html[1]);
36239     },
36240
36241     /**
36242      * Focuses the specified row.
36243      * @param {Number} row The row index
36244      */
36245     focusRow : function(row)
36246     {
36247         //Roo.log('GridView.focusRow');
36248         var x = this.scroller.dom.scrollLeft;
36249         this.focusCell(row, 0, false);
36250         this.scroller.dom.scrollLeft = x;
36251     },
36252
36253     /**
36254      * Focuses the specified cell.
36255      * @param {Number} row The row index
36256      * @param {Number} col The column index
36257      * @param {Boolean} hscroll false to disable horizontal scrolling
36258      */
36259     focusCell : function(row, col, hscroll)
36260     {
36261         //Roo.log('GridView.focusCell');
36262         var el = this.ensureVisible(row, col, hscroll);
36263         this.focusEl.alignTo(el, "tl-tl");
36264         if(Roo.isGecko){
36265             this.focusEl.focus();
36266         }else{
36267             this.focusEl.focus.defer(1, this.focusEl);
36268         }
36269     },
36270
36271     /**
36272      * Scrolls the specified cell into view
36273      * @param {Number} row The row index
36274      * @param {Number} col The column index
36275      * @param {Boolean} hscroll false to disable horizontal scrolling
36276      */
36277     ensureVisible : function(row, col, hscroll)
36278     {
36279         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
36280         //return null; //disable for testing.
36281         if(typeof row != "number"){
36282             row = row.rowIndex;
36283         }
36284         if(row < 0 && row >= this.ds.getCount()){
36285             return  null;
36286         }
36287         col = (col !== undefined ? col : 0);
36288         var cm = this.grid.colModel;
36289         while(cm.isHidden(col)){
36290             col++;
36291         }
36292
36293         var el = this.getCell(row, col);
36294         if(!el){
36295             return null;
36296         }
36297         var c = this.scroller.dom;
36298
36299         var ctop = parseInt(el.offsetTop, 10);
36300         var cleft = parseInt(el.offsetLeft, 10);
36301         var cbot = ctop + el.offsetHeight;
36302         var cright = cleft + el.offsetWidth;
36303         
36304         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
36305         var stop = parseInt(c.scrollTop, 10);
36306         var sleft = parseInt(c.scrollLeft, 10);
36307         var sbot = stop + ch;
36308         var sright = sleft + c.clientWidth;
36309         /*
36310         Roo.log('GridView.ensureVisible:' +
36311                 ' ctop:' + ctop +
36312                 ' c.clientHeight:' + c.clientHeight +
36313                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
36314                 ' stop:' + stop +
36315                 ' cbot:' + cbot +
36316                 ' sbot:' + sbot +
36317                 ' ch:' + ch  
36318                 );
36319         */
36320         if(ctop < stop){
36321              c.scrollTop = ctop;
36322             //Roo.log("set scrolltop to ctop DISABLE?");
36323         }else if(cbot > sbot){
36324             //Roo.log("set scrolltop to cbot-ch");
36325             c.scrollTop = cbot-ch;
36326         }
36327         
36328         if(hscroll !== false){
36329             if(cleft < sleft){
36330                 c.scrollLeft = cleft;
36331             }else if(cright > sright){
36332                 c.scrollLeft = cright-c.clientWidth;
36333             }
36334         }
36335          
36336         return el;
36337     },
36338
36339     updateColumns : function(){
36340         this.grid.stopEditing();
36341         var cm = this.grid.colModel, colIds = this.getColumnIds();
36342         //var totalWidth = cm.getTotalWidth();
36343         var pos = 0;
36344         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36345             //if(cm.isHidden(i)) continue;
36346             var w = cm.getColumnWidth(i);
36347             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36348             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
36349         }
36350         this.updateSplitters();
36351     },
36352
36353     generateRules : function(cm){
36354         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
36355         Roo.util.CSS.removeStyleSheet(rulesId);
36356         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36357             var cid = cm.getColumnId(i);
36358             var align = '';
36359             if(cm.config[i].align){
36360                 align = 'text-align:'+cm.config[i].align+';';
36361             }
36362             var hidden = '';
36363             if(cm.isHidden(i)){
36364                 hidden = 'display:none;';
36365             }
36366             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
36367             ruleBuf.push(
36368                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
36369                     this.hdSelector, cid, " {\n", align, width, "}\n",
36370                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
36371                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
36372         }
36373         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
36374     },
36375
36376     updateSplitters : function(){
36377         var cm = this.cm, s = this.getSplitters();
36378         if(s){ // splitters not created yet
36379             var pos = 0, locked = true;
36380             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
36381                 if(cm.isHidden(i)) continue;
36382                 var w = cm.getColumnWidth(i); // make sure it's a number
36383                 if(!cm.isLocked(i) && locked){
36384                     pos = 0;
36385                     locked = false;
36386                 }
36387                 pos += w;
36388                 s[i].style.left = (pos-this.splitOffset) + "px";
36389             }
36390         }
36391     },
36392
36393     handleHiddenChange : function(colModel, colIndex, hidden){
36394         if(hidden){
36395             this.hideColumn(colIndex);
36396         }else{
36397             this.unhideColumn(colIndex);
36398         }
36399     },
36400
36401     hideColumn : function(colIndex){
36402         var cid = this.getColumnId(colIndex);
36403         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
36404         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
36405         if(Roo.isSafari){
36406             this.updateHeaders();
36407         }
36408         this.updateSplitters();
36409         this.layout();
36410     },
36411
36412     unhideColumn : function(colIndex){
36413         var cid = this.getColumnId(colIndex);
36414         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
36415         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
36416
36417         if(Roo.isSafari){
36418             this.updateHeaders();
36419         }
36420         this.updateSplitters();
36421         this.layout();
36422     },
36423
36424     insertRows : function(dm, firstRow, lastRow, isUpdate){
36425         if(firstRow == 0 && lastRow == dm.getCount()-1){
36426             this.refresh();
36427         }else{
36428             if(!isUpdate){
36429                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
36430             }
36431             var s = this.getScrollState();
36432             var markup = this.renderRows(firstRow, lastRow);
36433             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
36434             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
36435             this.restoreScroll(s);
36436             if(!isUpdate){
36437                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
36438                 this.syncRowHeights(firstRow, lastRow);
36439                 this.stripeRows(firstRow);
36440                 this.layout();
36441             }
36442         }
36443     },
36444
36445     bufferRows : function(markup, target, index){
36446         var before = null, trows = target.rows, tbody = target.tBodies[0];
36447         if(index < trows.length){
36448             before = trows[index];
36449         }
36450         var b = document.createElement("div");
36451         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
36452         var rows = b.firstChild.rows;
36453         for(var i = 0, len = rows.length; i < len; i++){
36454             if(before){
36455                 tbody.insertBefore(rows[0], before);
36456             }else{
36457                 tbody.appendChild(rows[0]);
36458             }
36459         }
36460         b.innerHTML = "";
36461         b = null;
36462     },
36463
36464     deleteRows : function(dm, firstRow, lastRow){
36465         if(dm.getRowCount()<1){
36466             this.fireEvent("beforerefresh", this);
36467             this.mainBody.update("");
36468             this.lockedBody.update("");
36469             this.fireEvent("refresh", this);
36470         }else{
36471             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
36472             var bt = this.getBodyTable();
36473             var tbody = bt.firstChild;
36474             var rows = bt.rows;
36475             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
36476                 tbody.removeChild(rows[firstRow]);
36477             }
36478             this.stripeRows(firstRow);
36479             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
36480         }
36481     },
36482
36483     updateRows : function(dataSource, firstRow, lastRow){
36484         var s = this.getScrollState();
36485         this.refresh();
36486         this.restoreScroll(s);
36487     },
36488
36489     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
36490         if(!noRefresh){
36491            this.refresh();
36492         }
36493         this.updateHeaderSortState();
36494     },
36495
36496     getScrollState : function(){
36497         
36498         var sb = this.scroller.dom;
36499         return {left: sb.scrollLeft, top: sb.scrollTop};
36500     },
36501
36502     stripeRows : function(startRow){
36503         if(!this.grid.stripeRows || this.ds.getCount() < 1){
36504             return;
36505         }
36506         startRow = startRow || 0;
36507         var rows = this.getBodyTable().rows;
36508         var lrows = this.getLockedTable().rows;
36509         var cls = ' x-grid-row-alt ';
36510         for(var i = startRow, len = rows.length; i < len; i++){
36511             var row = rows[i], lrow = lrows[i];
36512             var isAlt = ((i+1) % 2 == 0);
36513             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
36514             if(isAlt == hasAlt){
36515                 continue;
36516             }
36517             if(isAlt){
36518                 row.className += " x-grid-row-alt";
36519             }else{
36520                 row.className = row.className.replace("x-grid-row-alt", "");
36521             }
36522             if(lrow){
36523                 lrow.className = row.className;
36524             }
36525         }
36526     },
36527
36528     restoreScroll : function(state){
36529         //Roo.log('GridView.restoreScroll');
36530         var sb = this.scroller.dom;
36531         sb.scrollLeft = state.left;
36532         sb.scrollTop = state.top;
36533         this.syncScroll();
36534     },
36535
36536     syncScroll : function(){
36537         //Roo.log('GridView.syncScroll');
36538         var sb = this.scroller.dom;
36539         var sh = this.mainHd.dom;
36540         var bs = this.mainBody.dom;
36541         var lv = this.lockedBody.dom;
36542         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
36543         lv.scrollTop = bs.scrollTop = sb.scrollTop;
36544     },
36545
36546     handleScroll : function(e){
36547         this.syncScroll();
36548         var sb = this.scroller.dom;
36549         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
36550         e.stopEvent();
36551     },
36552
36553     handleWheel : function(e){
36554         var d = e.getWheelDelta();
36555         this.scroller.dom.scrollTop -= d*22;
36556         // set this here to prevent jumpy scrolling on large tables
36557         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
36558         e.stopEvent();
36559     },
36560
36561     renderRows : function(startRow, endRow){
36562         // pull in all the crap needed to render rows
36563         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
36564         var colCount = cm.getColumnCount();
36565
36566         if(ds.getCount() < 1){
36567             return ["", ""];
36568         }
36569
36570         // build a map for all the columns
36571         var cs = [];
36572         for(var i = 0; i < colCount; i++){
36573             var name = cm.getDataIndex(i);
36574             cs[i] = {
36575                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
36576                 renderer : cm.getRenderer(i),
36577                 id : cm.getColumnId(i),
36578                 locked : cm.isLocked(i)
36579             };
36580         }
36581
36582         startRow = startRow || 0;
36583         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
36584
36585         // records to render
36586         var rs = ds.getRange(startRow, endRow);
36587
36588         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
36589     },
36590
36591     // As much as I hate to duplicate code, this was branched because FireFox really hates
36592     // [].join("") on strings. The performance difference was substantial enough to
36593     // branch this function
36594     doRender : Roo.isGecko ?
36595             function(cs, rs, ds, startRow, colCount, stripe){
36596                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36597                 // buffers
36598                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36599                 
36600                 var hasListener = this.grid.hasListener('rowclass');
36601                 var rowcfg = {};
36602                 for(var j = 0, len = rs.length; j < len; j++){
36603                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
36604                     for(var i = 0; i < colCount; i++){
36605                         c = cs[i];
36606                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36607                         p.id = c.id;
36608                         p.css = p.attr = "";
36609                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36610                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36611                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36612                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36613                         }
36614                         var markup = ct.apply(p);
36615                         if(!c.locked){
36616                             cb+= markup;
36617                         }else{
36618                             lcb+= markup;
36619                         }
36620                     }
36621                     var alt = [];
36622                     if(stripe && ((rowIndex+1) % 2 == 0)){
36623                         alt.push("x-grid-row-alt")
36624                     }
36625                     if(r.dirty){
36626                         alt.push(  " x-grid-dirty-row");
36627                     }
36628                     rp.cells = lcb;
36629                     if(this.getRowClass){
36630                         alt.push(this.getRowClass(r, rowIndex));
36631                     }
36632                     if (hasListener) {
36633                         rowcfg = {
36634                              
36635                             record: r,
36636                             rowIndex : rowIndex,
36637                             rowClass : ''
36638                         }
36639                         this.grid.fireEvent('rowclass', this, rowcfg);
36640                         alt.push(rowcfg.rowClass);
36641                     }
36642                     rp.alt = alt.join(" ");
36643                     lbuf+= rt.apply(rp);
36644                     rp.cells = cb;
36645                     buf+=  rt.apply(rp);
36646                 }
36647                 return [lbuf, buf];
36648             } :
36649             function(cs, rs, ds, startRow, colCount, stripe){
36650                 var ts = this.templates, ct = ts.cell, rt = ts.row;
36651                 // buffers
36652                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
36653                 var hasListener = this.grid.hasListener('rowclass');
36654  
36655                 var rowcfg = {};
36656                 for(var j = 0, len = rs.length; j < len; j++){
36657                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
36658                     for(var i = 0; i < colCount; i++){
36659                         c = cs[i];
36660                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
36661                         p.id = c.id;
36662                         p.css = p.attr = "";
36663                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
36664                         if(p.value == undefined || p.value === "") p.value = "&#160;";
36665                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
36666                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
36667                         }
36668                         
36669                         var markup = ct.apply(p);
36670                         if(!c.locked){
36671                             cb[cb.length] = markup;
36672                         }else{
36673                             lcb[lcb.length] = markup;
36674                         }
36675                     }
36676                     var alt = [];
36677                     if(stripe && ((rowIndex+1) % 2 == 0)){
36678                         alt.push( "x-grid-row-alt");
36679                     }
36680                     if(r.dirty){
36681                         alt.push(" x-grid-dirty-row");
36682                     }
36683                     rp.cells = lcb;
36684                     if(this.getRowClass){
36685                         alt.push( this.getRowClass(r, rowIndex));
36686                     }
36687                     if (hasListener) {
36688                         rowcfg = {
36689                              
36690                             record: r,
36691                             rowIndex : rowIndex,
36692                             rowClass : ''
36693                         }
36694                         this.grid.fireEvent('rowclass', this, rowcfg);
36695                         alt.push(rowcfg.rowClass);
36696                     }
36697                     rp.alt = alt.join(" ");
36698                     rp.cells = lcb.join("");
36699                     lbuf[lbuf.length] = rt.apply(rp);
36700                     rp.cells = cb.join("");
36701                     buf[buf.length] =  rt.apply(rp);
36702                 }
36703                 return [lbuf.join(""), buf.join("")];
36704             },
36705
36706     renderBody : function(){
36707         var markup = this.renderRows();
36708         var bt = this.templates.body;
36709         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
36710     },
36711
36712     /**
36713      * Refreshes the grid
36714      * @param {Boolean} headersToo
36715      */
36716     refresh : function(headersToo){
36717         this.fireEvent("beforerefresh", this);
36718         this.grid.stopEditing();
36719         var result = this.renderBody();
36720         this.lockedBody.update(result[0]);
36721         this.mainBody.update(result[1]);
36722         if(headersToo === true){
36723             this.updateHeaders();
36724             this.updateColumns();
36725             this.updateSplitters();
36726             this.updateHeaderSortState();
36727         }
36728         this.syncRowHeights();
36729         this.layout();
36730         this.fireEvent("refresh", this);
36731     },
36732
36733     handleColumnMove : function(cm, oldIndex, newIndex){
36734         this.indexMap = null;
36735         var s = this.getScrollState();
36736         this.refresh(true);
36737         this.restoreScroll(s);
36738         this.afterMove(newIndex);
36739     },
36740
36741     afterMove : function(colIndex){
36742         if(this.enableMoveAnim && Roo.enableFx){
36743             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
36744         }
36745         // if multisort - fix sortOrder, and reload..
36746         if (this.grid.dataSource.multiSort) {
36747             // the we can call sort again..
36748             var dm = this.grid.dataSource;
36749             var cm = this.grid.colModel;
36750             var so = [];
36751             for(var i = 0; i < cm.config.length; i++ ) {
36752                 
36753                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
36754                     continue; // dont' bother, it's not in sort list or being set.
36755                 }
36756                 
36757                 so.push(cm.config[i].dataIndex);
36758             };
36759             dm.sortOrder = so;
36760             dm.load(dm.lastOptions);
36761             
36762             
36763         }
36764         
36765     },
36766
36767     updateCell : function(dm, rowIndex, dataIndex){
36768         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36769         if(typeof colIndex == "undefined"){ // not present in grid
36770             return;
36771         }
36772         var cm = this.grid.colModel;
36773         var cell = this.getCell(rowIndex, colIndex);
36774         var cellText = this.getCellText(rowIndex, colIndex);
36775
36776         var p = {
36777             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36778             id : cm.getColumnId(colIndex),
36779             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36780         };
36781         var renderer = cm.getRenderer(colIndex);
36782         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36783         if(typeof val == "undefined" || val === "") val = "&#160;";
36784         cellText.innerHTML = val;
36785         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36786         this.syncRowHeights(rowIndex, rowIndex);
36787     },
36788
36789     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36790         var maxWidth = 0;
36791         if(this.grid.autoSizeHeaders){
36792             var h = this.getHeaderCellMeasure(colIndex);
36793             maxWidth = Math.max(maxWidth, h.scrollWidth);
36794         }
36795         var tb, index;
36796         if(this.cm.isLocked(colIndex)){
36797             tb = this.getLockedTable();
36798             index = colIndex;
36799         }else{
36800             tb = this.getBodyTable();
36801             index = colIndex - this.cm.getLockedCount();
36802         }
36803         if(tb && tb.rows){
36804             var rows = tb.rows;
36805             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36806             for(var i = 0; i < stopIndex; i++){
36807                 var cell = rows[i].childNodes[index].firstChild;
36808                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36809             }
36810         }
36811         return maxWidth + /*margin for error in IE*/ 5;
36812     },
36813     /**
36814      * Autofit a column to its content.
36815      * @param {Number} colIndex
36816      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36817      */
36818      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36819          if(this.cm.isHidden(colIndex)){
36820              return; // can't calc a hidden column
36821          }
36822         if(forceMinSize){
36823             var cid = this.cm.getColumnId(colIndex);
36824             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36825            if(this.grid.autoSizeHeaders){
36826                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36827            }
36828         }
36829         var newWidth = this.calcColumnWidth(colIndex);
36830         this.cm.setColumnWidth(colIndex,
36831             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36832         if(!suppressEvent){
36833             this.grid.fireEvent("columnresize", colIndex, newWidth);
36834         }
36835     },
36836
36837     /**
36838      * Autofits all columns to their content and then expands to fit any extra space in the grid
36839      */
36840      autoSizeColumns : function(){
36841         var cm = this.grid.colModel;
36842         var colCount = cm.getColumnCount();
36843         for(var i = 0; i < colCount; i++){
36844             this.autoSizeColumn(i, true, true);
36845         }
36846         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36847             this.fitColumns();
36848         }else{
36849             this.updateColumns();
36850             this.layout();
36851         }
36852     },
36853
36854     /**
36855      * Autofits all columns to the grid's width proportionate with their current size
36856      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36857      */
36858     fitColumns : function(reserveScrollSpace){
36859         var cm = this.grid.colModel;
36860         var colCount = cm.getColumnCount();
36861         var cols = [];
36862         var width = 0;
36863         var i, w;
36864         for (i = 0; i < colCount; i++){
36865             if(!cm.isHidden(i) && !cm.isFixed(i)){
36866                 w = cm.getColumnWidth(i);
36867                 cols.push(i);
36868                 cols.push(w);
36869                 width += w;
36870             }
36871         }
36872         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36873         if(reserveScrollSpace){
36874             avail -= 17;
36875         }
36876         var frac = (avail - cm.getTotalWidth())/width;
36877         while (cols.length){
36878             w = cols.pop();
36879             i = cols.pop();
36880             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36881         }
36882         this.updateColumns();
36883         this.layout();
36884     },
36885
36886     onRowSelect : function(rowIndex){
36887         var row = this.getRowComposite(rowIndex);
36888         row.addClass("x-grid-row-selected");
36889     },
36890
36891     onRowDeselect : function(rowIndex){
36892         var row = this.getRowComposite(rowIndex);
36893         row.removeClass("x-grid-row-selected");
36894     },
36895
36896     onCellSelect : function(row, col){
36897         var cell = this.getCell(row, col);
36898         if(cell){
36899             Roo.fly(cell).addClass("x-grid-cell-selected");
36900         }
36901     },
36902
36903     onCellDeselect : function(row, col){
36904         var cell = this.getCell(row, col);
36905         if(cell){
36906             Roo.fly(cell).removeClass("x-grid-cell-selected");
36907         }
36908     },
36909
36910     updateHeaderSortState : function(){
36911         
36912         // sort state can be single { field: xxx, direction : yyy}
36913         // or   { xxx=>ASC , yyy : DESC ..... }
36914         
36915         var mstate = {};
36916         if (!this.ds.multiSort) { 
36917             var state = this.ds.getSortState();
36918             if(!state){
36919                 return;
36920             }
36921             mstate[state.field] = state.direction;
36922             // FIXME... - this is not used here.. but might be elsewhere..
36923             this.sortState = state;
36924             
36925         } else {
36926             mstate = this.ds.sortToggle;
36927         }
36928         //remove existing sort classes..
36929         
36930         var sc = this.sortClasses;
36931         var hds = this.el.select(this.headerSelector).removeClass(sc);
36932         
36933         for(var f in mstate) {
36934         
36935             var sortColumn = this.cm.findColumnIndex(f);
36936             
36937             if(sortColumn != -1){
36938                 var sortDir = mstate[f];        
36939                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36940             }
36941         }
36942         
36943          
36944         
36945     },
36946
36947
36948     handleHeaderClick : function(g, index){
36949         if(this.headersDisabled){
36950             return;
36951         }
36952         var dm = g.dataSource, cm = g.colModel;
36953         if(!cm.isSortable(index)){
36954             return;
36955         }
36956         g.stopEditing();
36957         
36958         if (dm.multiSort) {
36959             // update the sortOrder
36960             var so = [];
36961             for(var i = 0; i < cm.config.length; i++ ) {
36962                 
36963                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36964                     continue; // dont' bother, it's not in sort list or being set.
36965                 }
36966                 
36967                 so.push(cm.config[i].dataIndex);
36968             };
36969             dm.sortOrder = so;
36970         }
36971         
36972         
36973         dm.sort(cm.getDataIndex(index));
36974     },
36975
36976
36977     destroy : function(){
36978         if(this.colMenu){
36979             this.colMenu.removeAll();
36980             Roo.menu.MenuMgr.unregister(this.colMenu);
36981             this.colMenu.getEl().remove();
36982             delete this.colMenu;
36983         }
36984         if(this.hmenu){
36985             this.hmenu.removeAll();
36986             Roo.menu.MenuMgr.unregister(this.hmenu);
36987             this.hmenu.getEl().remove();
36988             delete this.hmenu;
36989         }
36990         if(this.grid.enableColumnMove){
36991             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36992             if(dds){
36993                 for(var dd in dds){
36994                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36995                         var elid = dds[dd].dragElId;
36996                         dds[dd].unreg();
36997                         Roo.get(elid).remove();
36998                     } else if(dds[dd].config.isTarget){
36999                         dds[dd].proxyTop.remove();
37000                         dds[dd].proxyBottom.remove();
37001                         dds[dd].unreg();
37002                     }
37003                     if(Roo.dd.DDM.locationCache[dd]){
37004                         delete Roo.dd.DDM.locationCache[dd];
37005                     }
37006                 }
37007                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
37008             }
37009         }
37010         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
37011         this.bind(null, null);
37012         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
37013     },
37014
37015     handleLockChange : function(){
37016         this.refresh(true);
37017     },
37018
37019     onDenyColumnLock : function(){
37020
37021     },
37022
37023     onDenyColumnHide : function(){
37024
37025     },
37026
37027     handleHdMenuClick : function(item){
37028         var index = this.hdCtxIndex;
37029         var cm = this.cm, ds = this.ds;
37030         switch(item.id){
37031             case "asc":
37032                 ds.sort(cm.getDataIndex(index), "ASC");
37033                 break;
37034             case "desc":
37035                 ds.sort(cm.getDataIndex(index), "DESC");
37036                 break;
37037             case "lock":
37038                 var lc = cm.getLockedCount();
37039                 if(cm.getColumnCount(true) <= lc+1){
37040                     this.onDenyColumnLock();
37041                     return;
37042                 }
37043                 if(lc != index){
37044                     cm.setLocked(index, true, true);
37045                     cm.moveColumn(index, lc);
37046                     this.grid.fireEvent("columnmove", index, lc);
37047                 }else{
37048                     cm.setLocked(index, true);
37049                 }
37050             break;
37051             case "unlock":
37052                 var lc = cm.getLockedCount();
37053                 if((lc-1) != index){
37054                     cm.setLocked(index, false, true);
37055                     cm.moveColumn(index, lc-1);
37056                     this.grid.fireEvent("columnmove", index, lc-1);
37057                 }else{
37058                     cm.setLocked(index, false);
37059                 }
37060             break;
37061             default:
37062                 index = cm.getIndexById(item.id.substr(4));
37063                 if(index != -1){
37064                     if(item.checked && cm.getColumnCount(true) <= 1){
37065                         this.onDenyColumnHide();
37066                         return false;
37067                     }
37068                     cm.setHidden(index, item.checked);
37069                 }
37070         }
37071         return true;
37072     },
37073
37074     beforeColMenuShow : function(){
37075         var cm = this.cm,  colCount = cm.getColumnCount();
37076         this.colMenu.removeAll();
37077         for(var i = 0; i < colCount; i++){
37078             this.colMenu.add(new Roo.menu.CheckItem({
37079                 id: "col-"+cm.getColumnId(i),
37080                 text: cm.getColumnHeader(i),
37081                 checked: !cm.isHidden(i),
37082                 hideOnClick:false
37083             }));
37084         }
37085     },
37086
37087     handleHdCtx : function(g, index, e){
37088         e.stopEvent();
37089         var hd = this.getHeaderCell(index);
37090         this.hdCtxIndex = index;
37091         var ms = this.hmenu.items, cm = this.cm;
37092         ms.get("asc").setDisabled(!cm.isSortable(index));
37093         ms.get("desc").setDisabled(!cm.isSortable(index));
37094         if(this.grid.enableColLock !== false){
37095             ms.get("lock").setDisabled(cm.isLocked(index));
37096             ms.get("unlock").setDisabled(!cm.isLocked(index));
37097         }
37098         this.hmenu.show(hd, "tl-bl");
37099     },
37100
37101     handleHdOver : function(e){
37102         var hd = this.findHeaderCell(e.getTarget());
37103         if(hd && !this.headersDisabled){
37104             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
37105                this.fly(hd).addClass("x-grid-hd-over");
37106             }
37107         }
37108     },
37109
37110     handleHdOut : function(e){
37111         var hd = this.findHeaderCell(e.getTarget());
37112         if(hd){
37113             this.fly(hd).removeClass("x-grid-hd-over");
37114         }
37115     },
37116
37117     handleSplitDblClick : function(e, t){
37118         var i = this.getCellIndex(t);
37119         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
37120             this.autoSizeColumn(i, true);
37121             this.layout();
37122         }
37123     },
37124
37125     render : function(){
37126
37127         var cm = this.cm;
37128         var colCount = cm.getColumnCount();
37129
37130         if(this.grid.monitorWindowResize === true){
37131             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37132         }
37133         var header = this.renderHeaders();
37134         var body = this.templates.body.apply({rows:""});
37135         var html = this.templates.master.apply({
37136             lockedBody: body,
37137             body: body,
37138             lockedHeader: header[0],
37139             header: header[1]
37140         });
37141
37142         //this.updateColumns();
37143
37144         this.grid.getGridEl().dom.innerHTML = html;
37145
37146         this.initElements();
37147         
37148         // a kludge to fix the random scolling effect in webkit
37149         this.el.on("scroll", function() {
37150             this.el.dom.scrollTop=0; // hopefully not recursive..
37151         },this);
37152
37153         this.scroller.on("scroll", this.handleScroll, this);
37154         this.lockedBody.on("mousewheel", this.handleWheel, this);
37155         this.mainBody.on("mousewheel", this.handleWheel, this);
37156
37157         this.mainHd.on("mouseover", this.handleHdOver, this);
37158         this.mainHd.on("mouseout", this.handleHdOut, this);
37159         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
37160                 {delegate: "."+this.splitClass});
37161
37162         this.lockedHd.on("mouseover", this.handleHdOver, this);
37163         this.lockedHd.on("mouseout", this.handleHdOut, this);
37164         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
37165                 {delegate: "."+this.splitClass});
37166
37167         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
37168             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37169         }
37170
37171         this.updateSplitters();
37172
37173         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
37174             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37175             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
37176         }
37177
37178         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
37179             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
37180             this.hmenu.add(
37181                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
37182                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
37183             );
37184             if(this.grid.enableColLock !== false){
37185                 this.hmenu.add('-',
37186                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
37187                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
37188                 );
37189             }
37190             if(this.grid.enableColumnHide !== false){
37191
37192                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
37193                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
37194                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
37195
37196                 this.hmenu.add('-',
37197                     {id:"columns", text: this.columnsText, menu: this.colMenu}
37198                 );
37199             }
37200             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
37201
37202             this.grid.on("headercontextmenu", this.handleHdCtx, this);
37203         }
37204
37205         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
37206             this.dd = new Roo.grid.GridDragZone(this.grid, {
37207                 ddGroup : this.grid.ddGroup || 'GridDD'
37208             });
37209             
37210         }
37211
37212         /*
37213         for(var i = 0; i < colCount; i++){
37214             if(cm.isHidden(i)){
37215                 this.hideColumn(i);
37216             }
37217             if(cm.config[i].align){
37218                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
37219                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
37220             }
37221         }*/
37222         
37223         this.updateHeaderSortState();
37224
37225         this.beforeInitialResize();
37226         this.layout(true);
37227
37228         // two part rendering gives faster view to the user
37229         this.renderPhase2.defer(1, this);
37230     },
37231
37232     renderPhase2 : function(){
37233         // render the rows now
37234         this.refresh();
37235         if(this.grid.autoSizeColumns){
37236             this.autoSizeColumns();
37237         }
37238     },
37239
37240     beforeInitialResize : function(){
37241
37242     },
37243
37244     onColumnSplitterMoved : function(i, w){
37245         this.userResized = true;
37246         var cm = this.grid.colModel;
37247         cm.setColumnWidth(i, w, true);
37248         var cid = cm.getColumnId(i);
37249         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37250         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
37251         this.updateSplitters();
37252         this.layout();
37253         this.grid.fireEvent("columnresize", i, w);
37254     },
37255
37256     syncRowHeights : function(startIndex, endIndex){
37257         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
37258             startIndex = startIndex || 0;
37259             var mrows = this.getBodyTable().rows;
37260             var lrows = this.getLockedTable().rows;
37261             var len = mrows.length-1;
37262             endIndex = Math.min(endIndex || len, len);
37263             for(var i = startIndex; i <= endIndex; i++){
37264                 var m = mrows[i], l = lrows[i];
37265                 var h = Math.max(m.offsetHeight, l.offsetHeight);
37266                 m.style.height = l.style.height = h + "px";
37267             }
37268         }
37269     },
37270
37271     layout : function(initialRender, is2ndPass){
37272         var g = this.grid;
37273         var auto = g.autoHeight;
37274         var scrollOffset = 16;
37275         var c = g.getGridEl(), cm = this.cm,
37276                 expandCol = g.autoExpandColumn,
37277                 gv = this;
37278         //c.beginMeasure();
37279
37280         if(!c.dom.offsetWidth){ // display:none?
37281             if(initialRender){
37282                 this.lockedWrap.show();
37283                 this.mainWrap.show();
37284             }
37285             return;
37286         }
37287
37288         var hasLock = this.cm.isLocked(0);
37289
37290         var tbh = this.headerPanel.getHeight();
37291         var bbh = this.footerPanel.getHeight();
37292
37293         if(auto){
37294             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
37295             var newHeight = ch + c.getBorderWidth("tb");
37296             if(g.maxHeight){
37297                 newHeight = Math.min(g.maxHeight, newHeight);
37298             }
37299             c.setHeight(newHeight);
37300         }
37301
37302         if(g.autoWidth){
37303             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
37304         }
37305
37306         var s = this.scroller;
37307
37308         var csize = c.getSize(true);
37309
37310         this.el.setSize(csize.width, csize.height);
37311
37312         this.headerPanel.setWidth(csize.width);
37313         this.footerPanel.setWidth(csize.width);
37314
37315         var hdHeight = this.mainHd.getHeight();
37316         var vw = csize.width;
37317         var vh = csize.height - (tbh + bbh);
37318
37319         s.setSize(vw, vh);
37320
37321         var bt = this.getBodyTable();
37322         var ltWidth = hasLock ?
37323                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
37324
37325         var scrollHeight = bt.offsetHeight;
37326         var scrollWidth = ltWidth + bt.offsetWidth;
37327         var vscroll = false, hscroll = false;
37328
37329         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
37330
37331         var lw = this.lockedWrap, mw = this.mainWrap;
37332         var lb = this.lockedBody, mb = this.mainBody;
37333
37334         setTimeout(function(){
37335             var t = s.dom.offsetTop;
37336             var w = s.dom.clientWidth,
37337                 h = s.dom.clientHeight;
37338
37339             lw.setTop(t);
37340             lw.setSize(ltWidth, h);
37341
37342             mw.setLeftTop(ltWidth, t);
37343             mw.setSize(w-ltWidth, h);
37344
37345             lb.setHeight(h-hdHeight);
37346             mb.setHeight(h-hdHeight);
37347
37348             if(is2ndPass !== true && !gv.userResized && expandCol){
37349                 // high speed resize without full column calculation
37350                 
37351                 var ci = cm.getIndexById(expandCol);
37352                 if (ci < 0) {
37353                     ci = cm.findColumnIndex(expandCol);
37354                 }
37355                 ci = Math.max(0, ci); // make sure it's got at least the first col.
37356                 var expandId = cm.getColumnId(ci);
37357                 var  tw = cm.getTotalWidth(false);
37358                 var currentWidth = cm.getColumnWidth(ci);
37359                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
37360                 if(currentWidth != cw){
37361                     cm.setColumnWidth(ci, cw, true);
37362                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37363                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
37364                     gv.updateSplitters();
37365                     gv.layout(false, true);
37366                 }
37367             }
37368
37369             if(initialRender){
37370                 lw.show();
37371                 mw.show();
37372             }
37373             //c.endMeasure();
37374         }, 10);
37375     },
37376
37377     onWindowResize : function(){
37378         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
37379             return;
37380         }
37381         this.layout();
37382     },
37383
37384     appendFooter : function(parentEl){
37385         return null;
37386     },
37387
37388     sortAscText : "Sort Ascending",
37389     sortDescText : "Sort Descending",
37390     lockText : "Lock Column",
37391     unlockText : "Unlock Column",
37392     columnsText : "Columns"
37393 });
37394
37395
37396 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
37397     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
37398     this.proxy.el.addClass('x-grid3-col-dd');
37399 };
37400
37401 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
37402     handleMouseDown : function(e){
37403
37404     },
37405
37406     callHandleMouseDown : function(e){
37407         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
37408     }
37409 });
37410 /*
37411  * Based on:
37412  * Ext JS Library 1.1.1
37413  * Copyright(c) 2006-2007, Ext JS, LLC.
37414  *
37415  * Originally Released Under LGPL - original licence link has changed is not relivant.
37416  *
37417  * Fork - LGPL
37418  * <script type="text/javascript">
37419  */
37420  
37421 // private
37422 // This is a support class used internally by the Grid components
37423 Roo.grid.SplitDragZone = function(grid, hd, hd2){
37424     this.grid = grid;
37425     this.view = grid.getView();
37426     this.proxy = this.view.resizeProxy;
37427     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
37428         "gridSplitters" + this.grid.getGridEl().id, {
37429         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
37430     });
37431     this.setHandleElId(Roo.id(hd));
37432     this.setOuterHandleElId(Roo.id(hd2));
37433     this.scroll = false;
37434 };
37435 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
37436     fly: Roo.Element.fly,
37437
37438     b4StartDrag : function(x, y){
37439         this.view.headersDisabled = true;
37440         this.proxy.setHeight(this.view.mainWrap.getHeight());
37441         var w = this.cm.getColumnWidth(this.cellIndex);
37442         var minw = Math.max(w-this.grid.minColumnWidth, 0);
37443         this.resetConstraints();
37444         this.setXConstraint(minw, 1000);
37445         this.setYConstraint(0, 0);
37446         this.minX = x - minw;
37447         this.maxX = x + 1000;
37448         this.startPos = x;
37449         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
37450     },
37451
37452
37453     handleMouseDown : function(e){
37454         ev = Roo.EventObject.setEvent(e);
37455         var t = this.fly(ev.getTarget());
37456         if(t.hasClass("x-grid-split")){
37457             this.cellIndex = this.view.getCellIndex(t.dom);
37458             this.split = t.dom;
37459             this.cm = this.grid.colModel;
37460             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
37461                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
37462             }
37463         }
37464     },
37465
37466     endDrag : function(e){
37467         this.view.headersDisabled = false;
37468         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
37469         var diff = endX - this.startPos;
37470         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
37471     },
37472
37473     autoOffset : function(){
37474         this.setDelta(0,0);
37475     }
37476 });/*
37477  * Based on:
37478  * Ext JS Library 1.1.1
37479  * Copyright(c) 2006-2007, Ext JS, LLC.
37480  *
37481  * Originally Released Under LGPL - original licence link has changed is not relivant.
37482  *
37483  * Fork - LGPL
37484  * <script type="text/javascript">
37485  */
37486  
37487 // private
37488 // This is a support class used internally by the Grid components
37489 Roo.grid.GridDragZone = function(grid, config){
37490     this.view = grid.getView();
37491     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
37492     if(this.view.lockedBody){
37493         this.setHandleElId(Roo.id(this.view.mainBody.dom));
37494         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
37495     }
37496     this.scroll = false;
37497     this.grid = grid;
37498     this.ddel = document.createElement('div');
37499     this.ddel.className = 'x-grid-dd-wrap';
37500 };
37501
37502 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
37503     ddGroup : "GridDD",
37504
37505     getDragData : function(e){
37506         var t = Roo.lib.Event.getTarget(e);
37507         var rowIndex = this.view.findRowIndex(t);
37508         var sm = this.grid.selModel;
37509             
37510         //Roo.log(rowIndex);
37511         
37512         if (sm.getSelectedCell) {
37513             // cell selection..
37514             if (!sm.getSelectedCell()) {
37515                 return false;
37516             }
37517             if (rowIndex != sm.getSelectedCell()[0]) {
37518                 return false;
37519             }
37520         
37521         }
37522         
37523         if(rowIndex !== false){
37524             
37525             // if editorgrid.. 
37526             
37527             
37528             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
37529                
37530             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
37531               //  
37532             //}
37533             if (e.hasModifier()){
37534                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
37535             }
37536             
37537             Roo.log("getDragData");
37538             
37539             return {
37540                 grid: this.grid,
37541                 ddel: this.ddel,
37542                 rowIndex: rowIndex,
37543                 selections:sm.getSelections ? sm.getSelections() : (
37544                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
37545                 )
37546             };
37547         }
37548         return false;
37549     },
37550
37551     onInitDrag : function(e){
37552         var data = this.dragData;
37553         this.ddel.innerHTML = this.grid.getDragDropText();
37554         this.proxy.update(this.ddel);
37555         // fire start drag?
37556     },
37557
37558     afterRepair : function(){
37559         this.dragging = false;
37560     },
37561
37562     getRepairXY : function(e, data){
37563         return false;
37564     },
37565
37566     onEndDrag : function(data, e){
37567         // fire end drag?
37568     },
37569
37570     onValidDrop : function(dd, e, id){
37571         // fire drag drop?
37572         this.hideProxy();
37573     },
37574
37575     beforeInvalidDrop : function(e, id){
37576
37577     }
37578 });/*
37579  * Based on:
37580  * Ext JS Library 1.1.1
37581  * Copyright(c) 2006-2007, Ext JS, LLC.
37582  *
37583  * Originally Released Under LGPL - original licence link has changed is not relivant.
37584  *
37585  * Fork - LGPL
37586  * <script type="text/javascript">
37587  */
37588  
37589
37590 /**
37591  * @class Roo.grid.ColumnModel
37592  * @extends Roo.util.Observable
37593  * This is the default implementation of a ColumnModel used by the Grid. It defines
37594  * the columns in the grid.
37595  * <br>Usage:<br>
37596  <pre><code>
37597  var colModel = new Roo.grid.ColumnModel([
37598         {header: "Ticker", width: 60, sortable: true, locked: true},
37599         {header: "Company Name", width: 150, sortable: true},
37600         {header: "Market Cap.", width: 100, sortable: true},
37601         {header: "$ Sales", width: 100, sortable: true, renderer: money},
37602         {header: "Employees", width: 100, sortable: true, resizable: false}
37603  ]);
37604  </code></pre>
37605  * <p>
37606  
37607  * The config options listed for this class are options which may appear in each
37608  * individual column definition.
37609  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
37610  * @constructor
37611  * @param {Object} config An Array of column config objects. See this class's
37612  * config objects for details.
37613 */
37614 Roo.grid.ColumnModel = function(config){
37615         /**
37616      * The config passed into the constructor
37617      */
37618     this.config = config;
37619     this.lookup = {};
37620
37621     // if no id, create one
37622     // if the column does not have a dataIndex mapping,
37623     // map it to the order it is in the config
37624     for(var i = 0, len = config.length; i < len; i++){
37625         var c = config[i];
37626         if(typeof c.dataIndex == "undefined"){
37627             c.dataIndex = i;
37628         }
37629         if(typeof c.renderer == "string"){
37630             c.renderer = Roo.util.Format[c.renderer];
37631         }
37632         if(typeof c.id == "undefined"){
37633             c.id = Roo.id();
37634         }
37635         if(c.editor && c.editor.xtype){
37636             c.editor  = Roo.factory(c.editor, Roo.grid);
37637         }
37638         if(c.editor && c.editor.isFormField){
37639             c.editor = new Roo.grid.GridEditor(c.editor);
37640         }
37641         this.lookup[c.id] = c;
37642     }
37643
37644     /**
37645      * The width of columns which have no width specified (defaults to 100)
37646      * @type Number
37647      */
37648     this.defaultWidth = 100;
37649
37650     /**
37651      * Default sortable of columns which have no sortable specified (defaults to false)
37652      * @type Boolean
37653      */
37654     this.defaultSortable = false;
37655
37656     this.addEvents({
37657         /**
37658              * @event widthchange
37659              * Fires when the width of a column changes.
37660              * @param {ColumnModel} this
37661              * @param {Number} columnIndex The column index
37662              * @param {Number} newWidth The new width
37663              */
37664             "widthchange": true,
37665         /**
37666              * @event headerchange
37667              * Fires when the text of a header changes.
37668              * @param {ColumnModel} this
37669              * @param {Number} columnIndex The column index
37670              * @param {Number} newText The new header text
37671              */
37672             "headerchange": true,
37673         /**
37674              * @event hiddenchange
37675              * Fires when a column is hidden or "unhidden".
37676              * @param {ColumnModel} this
37677              * @param {Number} columnIndex The column index
37678              * @param {Boolean} hidden true if hidden, false otherwise
37679              */
37680             "hiddenchange": true,
37681             /**
37682          * @event columnmoved
37683          * Fires when a column is moved.
37684          * @param {ColumnModel} this
37685          * @param {Number} oldIndex
37686          * @param {Number} newIndex
37687          */
37688         "columnmoved" : true,
37689         /**
37690          * @event columlockchange
37691          * Fires when a column's locked state is changed
37692          * @param {ColumnModel} this
37693          * @param {Number} colIndex
37694          * @param {Boolean} locked true if locked
37695          */
37696         "columnlockchange" : true
37697     });
37698     Roo.grid.ColumnModel.superclass.constructor.call(this);
37699 };
37700 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
37701     /**
37702      * @cfg {String} header The header text to display in the Grid view.
37703      */
37704     /**
37705      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
37706      * {@link Roo.data.Record} definition from which to draw the column's value. If not
37707      * specified, the column's index is used as an index into the Record's data Array.
37708      */
37709     /**
37710      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
37711      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
37712      */
37713     /**
37714      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
37715      * Defaults to the value of the {@link #defaultSortable} property.
37716      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
37717      */
37718     /**
37719      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
37720      */
37721     /**
37722      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
37723      */
37724     /**
37725      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
37726      */
37727     /**
37728      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
37729      */
37730     /**
37731      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
37732      * given the cell's data value. See {@link #setRenderer}. If not specified, the
37733      * default renderer uses the raw data value.
37734      */
37735        /**
37736      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
37737      */
37738     /**
37739      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
37740      */
37741
37742     /**
37743      * Returns the id of the column at the specified index.
37744      * @param {Number} index The column index
37745      * @return {String} the id
37746      */
37747     getColumnId : function(index){
37748         return this.config[index].id;
37749     },
37750
37751     /**
37752      * Returns the column for a specified id.
37753      * @param {String} id The column id
37754      * @return {Object} the column
37755      */
37756     getColumnById : function(id){
37757         return this.lookup[id];
37758     },
37759
37760     
37761     /**
37762      * Returns the column for a specified dataIndex.
37763      * @param {String} dataIndex The column dataIndex
37764      * @return {Object|Boolean} the column or false if not found
37765      */
37766     getColumnByDataIndex: function(dataIndex){
37767         var index = this.findColumnIndex(dataIndex);
37768         return index > -1 ? this.config[index] : false;
37769     },
37770     
37771     /**
37772      * Returns the index for a specified column id.
37773      * @param {String} id The column id
37774      * @return {Number} the index, or -1 if not found
37775      */
37776     getIndexById : function(id){
37777         for(var i = 0, len = this.config.length; i < len; i++){
37778             if(this.config[i].id == id){
37779                 return i;
37780             }
37781         }
37782         return -1;
37783     },
37784     
37785     /**
37786      * Returns the index for a specified column dataIndex.
37787      * @param {String} dataIndex The column dataIndex
37788      * @return {Number} the index, or -1 if not found
37789      */
37790     
37791     findColumnIndex : function(dataIndex){
37792         for(var i = 0, len = this.config.length; i < len; i++){
37793             if(this.config[i].dataIndex == dataIndex){
37794                 return i;
37795             }
37796         }
37797         return -1;
37798     },
37799     
37800     
37801     moveColumn : function(oldIndex, newIndex){
37802         var c = this.config[oldIndex];
37803         this.config.splice(oldIndex, 1);
37804         this.config.splice(newIndex, 0, c);
37805         this.dataMap = null;
37806         this.fireEvent("columnmoved", this, oldIndex, newIndex);
37807     },
37808
37809     isLocked : function(colIndex){
37810         return this.config[colIndex].locked === true;
37811     },
37812
37813     setLocked : function(colIndex, value, suppressEvent){
37814         if(this.isLocked(colIndex) == value){
37815             return;
37816         }
37817         this.config[colIndex].locked = value;
37818         if(!suppressEvent){
37819             this.fireEvent("columnlockchange", this, colIndex, value);
37820         }
37821     },
37822
37823     getTotalLockedWidth : function(){
37824         var totalWidth = 0;
37825         for(var i = 0; i < this.config.length; i++){
37826             if(this.isLocked(i) && !this.isHidden(i)){
37827                 this.totalWidth += this.getColumnWidth(i);
37828             }
37829         }
37830         return totalWidth;
37831     },
37832
37833     getLockedCount : function(){
37834         for(var i = 0, len = this.config.length; i < len; i++){
37835             if(!this.isLocked(i)){
37836                 return i;
37837             }
37838         }
37839     },
37840
37841     /**
37842      * Returns the number of columns.
37843      * @return {Number}
37844      */
37845     getColumnCount : function(visibleOnly){
37846         if(visibleOnly === true){
37847             var c = 0;
37848             for(var i = 0, len = this.config.length; i < len; i++){
37849                 if(!this.isHidden(i)){
37850                     c++;
37851                 }
37852             }
37853             return c;
37854         }
37855         return this.config.length;
37856     },
37857
37858     /**
37859      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
37860      * @param {Function} fn
37861      * @param {Object} scope (optional)
37862      * @return {Array} result
37863      */
37864     getColumnsBy : function(fn, scope){
37865         var r = [];
37866         for(var i = 0, len = this.config.length; i < len; i++){
37867             var c = this.config[i];
37868             if(fn.call(scope||this, c, i) === true){
37869                 r[r.length] = c;
37870             }
37871         }
37872         return r;
37873     },
37874
37875     /**
37876      * Returns true if the specified column is sortable.
37877      * @param {Number} col The column index
37878      * @return {Boolean}
37879      */
37880     isSortable : function(col){
37881         if(typeof this.config[col].sortable == "undefined"){
37882             return this.defaultSortable;
37883         }
37884         return this.config[col].sortable;
37885     },
37886
37887     /**
37888      * Returns the rendering (formatting) function defined for the column.
37889      * @param {Number} col The column index.
37890      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
37891      */
37892     getRenderer : function(col){
37893         if(!this.config[col].renderer){
37894             return Roo.grid.ColumnModel.defaultRenderer;
37895         }
37896         return this.config[col].renderer;
37897     },
37898
37899     /**
37900      * Sets the rendering (formatting) function for a column.
37901      * @param {Number} col The column index
37902      * @param {Function} fn The function to use to process the cell's raw data
37903      * to return HTML markup for the grid view. The render function is called with
37904      * the following parameters:<ul>
37905      * <li>Data value.</li>
37906      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
37907      * <li>css A CSS style string to apply to the table cell.</li>
37908      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
37909      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
37910      * <li>Row index</li>
37911      * <li>Column index</li>
37912      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
37913      */
37914     setRenderer : function(col, fn){
37915         this.config[col].renderer = fn;
37916     },
37917
37918     /**
37919      * Returns the width for the specified column.
37920      * @param {Number} col The column index
37921      * @return {Number}
37922      */
37923     getColumnWidth : function(col){
37924         return this.config[col].width * 1 || this.defaultWidth;
37925     },
37926
37927     /**
37928      * Sets the width for a column.
37929      * @param {Number} col The column index
37930      * @param {Number} width The new width
37931      */
37932     setColumnWidth : function(col, width, suppressEvent){
37933         this.config[col].width = width;
37934         this.totalWidth = null;
37935         if(!suppressEvent){
37936              this.fireEvent("widthchange", this, col, width);
37937         }
37938     },
37939
37940     /**
37941      * Returns the total width of all columns.
37942      * @param {Boolean} includeHidden True to include hidden column widths
37943      * @return {Number}
37944      */
37945     getTotalWidth : function(includeHidden){
37946         if(!this.totalWidth){
37947             this.totalWidth = 0;
37948             for(var i = 0, len = this.config.length; i < len; i++){
37949                 if(includeHidden || !this.isHidden(i)){
37950                     this.totalWidth += this.getColumnWidth(i);
37951                 }
37952             }
37953         }
37954         return this.totalWidth;
37955     },
37956
37957     /**
37958      * Returns the header for the specified column.
37959      * @param {Number} col The column index
37960      * @return {String}
37961      */
37962     getColumnHeader : function(col){
37963         return this.config[col].header;
37964     },
37965
37966     /**
37967      * Sets the header for a column.
37968      * @param {Number} col The column index
37969      * @param {String} header The new header
37970      */
37971     setColumnHeader : function(col, header){
37972         this.config[col].header = header;
37973         this.fireEvent("headerchange", this, col, header);
37974     },
37975
37976     /**
37977      * Returns the tooltip for the specified column.
37978      * @param {Number} col The column index
37979      * @return {String}
37980      */
37981     getColumnTooltip : function(col){
37982             return this.config[col].tooltip;
37983     },
37984     /**
37985      * Sets the tooltip for a column.
37986      * @param {Number} col The column index
37987      * @param {String} tooltip The new tooltip
37988      */
37989     setColumnTooltip : function(col, tooltip){
37990             this.config[col].tooltip = tooltip;
37991     },
37992
37993     /**
37994      * Returns the dataIndex for the specified column.
37995      * @param {Number} col The column index
37996      * @return {Number}
37997      */
37998     getDataIndex : function(col){
37999         return this.config[col].dataIndex;
38000     },
38001
38002     /**
38003      * Sets the dataIndex for a column.
38004      * @param {Number} col The column index
38005      * @param {Number} dataIndex The new dataIndex
38006      */
38007     setDataIndex : function(col, dataIndex){
38008         this.config[col].dataIndex = dataIndex;
38009     },
38010
38011     
38012     
38013     /**
38014      * Returns true if the cell is editable.
38015      * @param {Number} colIndex The column index
38016      * @param {Number} rowIndex The row index
38017      * @return {Boolean}
38018      */
38019     isCellEditable : function(colIndex, rowIndex){
38020         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
38021     },
38022
38023     /**
38024      * Returns the editor defined for the cell/column.
38025      * return false or null to disable editing.
38026      * @param {Number} colIndex The column index
38027      * @param {Number} rowIndex The row index
38028      * @return {Object}
38029      */
38030     getCellEditor : function(colIndex, rowIndex){
38031         return this.config[colIndex].editor;
38032     },
38033
38034     /**
38035      * Sets if a column is editable.
38036      * @param {Number} col The column index
38037      * @param {Boolean} editable True if the column is editable
38038      */
38039     setEditable : function(col, editable){
38040         this.config[col].editable = editable;
38041     },
38042
38043
38044     /**
38045      * Returns true if the column is hidden.
38046      * @param {Number} colIndex The column index
38047      * @return {Boolean}
38048      */
38049     isHidden : function(colIndex){
38050         return this.config[colIndex].hidden;
38051     },
38052
38053
38054     /**
38055      * Returns true if the column width cannot be changed
38056      */
38057     isFixed : function(colIndex){
38058         return this.config[colIndex].fixed;
38059     },
38060
38061     /**
38062      * Returns true if the column can be resized
38063      * @return {Boolean}
38064      */
38065     isResizable : function(colIndex){
38066         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
38067     },
38068     /**
38069      * Sets if a column is hidden.
38070      * @param {Number} colIndex The column index
38071      * @param {Boolean} hidden True if the column is hidden
38072      */
38073     setHidden : function(colIndex, hidden){
38074         this.config[colIndex].hidden = hidden;
38075         this.totalWidth = null;
38076         this.fireEvent("hiddenchange", this, colIndex, hidden);
38077     },
38078
38079     /**
38080      * Sets the editor for a column.
38081      * @param {Number} col The column index
38082      * @param {Object} editor The editor object
38083      */
38084     setEditor : function(col, editor){
38085         this.config[col].editor = editor;
38086     }
38087 });
38088
38089 Roo.grid.ColumnModel.defaultRenderer = function(value){
38090         if(typeof value == "string" && value.length < 1){
38091             return "&#160;";
38092         }
38093         return value;
38094 };
38095
38096 // Alias for backwards compatibility
38097 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
38098 /*
38099  * Based on:
38100  * Ext JS Library 1.1.1
38101  * Copyright(c) 2006-2007, Ext JS, LLC.
38102  *
38103  * Originally Released Under LGPL - original licence link has changed is not relivant.
38104  *
38105  * Fork - LGPL
38106  * <script type="text/javascript">
38107  */
38108
38109 /**
38110  * @class Roo.grid.AbstractSelectionModel
38111  * @extends Roo.util.Observable
38112  * Abstract base class for grid SelectionModels.  It provides the interface that should be
38113  * implemented by descendant classes.  This class should not be directly instantiated.
38114  * @constructor
38115  */
38116 Roo.grid.AbstractSelectionModel = function(){
38117     this.locked = false;
38118     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
38119 };
38120
38121 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
38122     /** @ignore Called by the grid automatically. Do not call directly. */
38123     init : function(grid){
38124         this.grid = grid;
38125         this.initEvents();
38126     },
38127
38128     /**
38129      * Locks the selections.
38130      */
38131     lock : function(){
38132         this.locked = true;
38133     },
38134
38135     /**
38136      * Unlocks the selections.
38137      */
38138     unlock : function(){
38139         this.locked = false;
38140     },
38141
38142     /**
38143      * Returns true if the selections are locked.
38144      * @return {Boolean}
38145      */
38146     isLocked : function(){
38147         return this.locked;
38148     }
38149 });/*
38150  * Based on:
38151  * Ext JS Library 1.1.1
38152  * Copyright(c) 2006-2007, Ext JS, LLC.
38153  *
38154  * Originally Released Under LGPL - original licence link has changed is not relivant.
38155  *
38156  * Fork - LGPL
38157  * <script type="text/javascript">
38158  */
38159 /**
38160  * @extends Roo.grid.AbstractSelectionModel
38161  * @class Roo.grid.RowSelectionModel
38162  * The default SelectionModel used by {@link Roo.grid.Grid}.
38163  * It supports multiple selections and keyboard selection/navigation. 
38164  * @constructor
38165  * @param {Object} config
38166  */
38167 Roo.grid.RowSelectionModel = function(config){
38168     Roo.apply(this, config);
38169     this.selections = new Roo.util.MixedCollection(false, function(o){
38170         return o.id;
38171     });
38172
38173     this.last = false;
38174     this.lastActive = false;
38175
38176     this.addEvents({
38177         /**
38178              * @event selectionchange
38179              * Fires when the selection changes
38180              * @param {SelectionModel} this
38181              */
38182             "selectionchange" : true,
38183         /**
38184              * @event afterselectionchange
38185              * Fires after the selection changes (eg. by key press or clicking)
38186              * @param {SelectionModel} this
38187              */
38188             "afterselectionchange" : true,
38189         /**
38190              * @event beforerowselect
38191              * Fires when a row is selected being selected, return false to cancel.
38192              * @param {SelectionModel} this
38193              * @param {Number} rowIndex The selected index
38194              * @param {Boolean} keepExisting False if other selections will be cleared
38195              */
38196             "beforerowselect" : true,
38197         /**
38198              * @event rowselect
38199              * Fires when a row is selected.
38200              * @param {SelectionModel} this
38201              * @param {Number} rowIndex The selected index
38202              * @param {Roo.data.Record} r The record
38203              */
38204             "rowselect" : true,
38205         /**
38206              * @event rowdeselect
38207              * Fires when a row is deselected.
38208              * @param {SelectionModel} this
38209              * @param {Number} rowIndex The selected index
38210              */
38211         "rowdeselect" : true
38212     });
38213     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
38214     this.locked = false;
38215 };
38216
38217 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
38218     /**
38219      * @cfg {Boolean} singleSelect
38220      * True to allow selection of only one row at a time (defaults to false)
38221      */
38222     singleSelect : false,
38223
38224     // private
38225     initEvents : function(){
38226
38227         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
38228             this.grid.on("mousedown", this.handleMouseDown, this);
38229         }else{ // allow click to work like normal
38230             this.grid.on("rowclick", this.handleDragableRowClick, this);
38231         }
38232
38233         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
38234             "up" : function(e){
38235                 if(!e.shiftKey){
38236                     this.selectPrevious(e.shiftKey);
38237                 }else if(this.last !== false && this.lastActive !== false){
38238                     var last = this.last;
38239                     this.selectRange(this.last,  this.lastActive-1);
38240                     this.grid.getView().focusRow(this.lastActive);
38241                     if(last !== false){
38242                         this.last = last;
38243                     }
38244                 }else{
38245                     this.selectFirstRow();
38246                 }
38247                 this.fireEvent("afterselectionchange", this);
38248             },
38249             "down" : function(e){
38250                 if(!e.shiftKey){
38251                     this.selectNext(e.shiftKey);
38252                 }else if(this.last !== false && this.lastActive !== false){
38253                     var last = this.last;
38254                     this.selectRange(this.last,  this.lastActive+1);
38255                     this.grid.getView().focusRow(this.lastActive);
38256                     if(last !== false){
38257                         this.last = last;
38258                     }
38259                 }else{
38260                     this.selectFirstRow();
38261                 }
38262                 this.fireEvent("afterselectionchange", this);
38263             },
38264             scope: this
38265         });
38266
38267         var view = this.grid.view;
38268         view.on("refresh", this.onRefresh, this);
38269         view.on("rowupdated", this.onRowUpdated, this);
38270         view.on("rowremoved", this.onRemove, this);
38271     },
38272
38273     // private
38274     onRefresh : function(){
38275         var ds = this.grid.dataSource, i, v = this.grid.view;
38276         var s = this.selections;
38277         s.each(function(r){
38278             if((i = ds.indexOfId(r.id)) != -1){
38279                 v.onRowSelect(i);
38280             }else{
38281                 s.remove(r);
38282             }
38283         });
38284     },
38285
38286     // private
38287     onRemove : function(v, index, r){
38288         this.selections.remove(r);
38289     },
38290
38291     // private
38292     onRowUpdated : function(v, index, r){
38293         if(this.isSelected(r)){
38294             v.onRowSelect(index);
38295         }
38296     },
38297
38298     /**
38299      * Select records.
38300      * @param {Array} records The records to select
38301      * @param {Boolean} keepExisting (optional) True to keep existing selections
38302      */
38303     selectRecords : function(records, keepExisting){
38304         if(!keepExisting){
38305             this.clearSelections();
38306         }
38307         var ds = this.grid.dataSource;
38308         for(var i = 0, len = records.length; i < len; i++){
38309             this.selectRow(ds.indexOf(records[i]), true);
38310         }
38311     },
38312
38313     /**
38314      * Gets the number of selected rows.
38315      * @return {Number}
38316      */
38317     getCount : function(){
38318         return this.selections.length;
38319     },
38320
38321     /**
38322      * Selects the first row in the grid.
38323      */
38324     selectFirstRow : function(){
38325         this.selectRow(0);
38326     },
38327
38328     /**
38329      * Select the last row.
38330      * @param {Boolean} keepExisting (optional) True to keep existing selections
38331      */
38332     selectLastRow : function(keepExisting){
38333         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
38334     },
38335
38336     /**
38337      * Selects the row immediately following the last selected row.
38338      * @param {Boolean} keepExisting (optional) True to keep existing selections
38339      */
38340     selectNext : function(keepExisting){
38341         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
38342             this.selectRow(this.last+1, keepExisting);
38343             this.grid.getView().focusRow(this.last);
38344         }
38345     },
38346
38347     /**
38348      * Selects the row that precedes the last selected row.
38349      * @param {Boolean} keepExisting (optional) True to keep existing selections
38350      */
38351     selectPrevious : function(keepExisting){
38352         if(this.last){
38353             this.selectRow(this.last-1, keepExisting);
38354             this.grid.getView().focusRow(this.last);
38355         }
38356     },
38357
38358     /**
38359      * Returns the selected records
38360      * @return {Array} Array of selected records
38361      */
38362     getSelections : function(){
38363         return [].concat(this.selections.items);
38364     },
38365
38366     /**
38367      * Returns the first selected record.
38368      * @return {Record}
38369      */
38370     getSelected : function(){
38371         return this.selections.itemAt(0);
38372     },
38373
38374
38375     /**
38376      * Clears all selections.
38377      */
38378     clearSelections : function(fast){
38379         if(this.locked) return;
38380         if(fast !== true){
38381             var ds = this.grid.dataSource;
38382             var s = this.selections;
38383             s.each(function(r){
38384                 this.deselectRow(ds.indexOfId(r.id));
38385             }, this);
38386             s.clear();
38387         }else{
38388             this.selections.clear();
38389         }
38390         this.last = false;
38391     },
38392
38393
38394     /**
38395      * Selects all rows.
38396      */
38397     selectAll : function(){
38398         if(this.locked) return;
38399         this.selections.clear();
38400         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
38401             this.selectRow(i, true);
38402         }
38403     },
38404
38405     /**
38406      * Returns True if there is a selection.
38407      * @return {Boolean}
38408      */
38409     hasSelection : function(){
38410         return this.selections.length > 0;
38411     },
38412
38413     /**
38414      * Returns True if the specified row is selected.
38415      * @param {Number/Record} record The record or index of the record to check
38416      * @return {Boolean}
38417      */
38418     isSelected : function(index){
38419         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
38420         return (r && this.selections.key(r.id) ? true : false);
38421     },
38422
38423     /**
38424      * Returns True if the specified record id is selected.
38425      * @param {String} id The id of record to check
38426      * @return {Boolean}
38427      */
38428     isIdSelected : function(id){
38429         return (this.selections.key(id) ? true : false);
38430     },
38431
38432     // private
38433     handleMouseDown : function(e, t){
38434         var view = this.grid.getView(), rowIndex;
38435         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
38436             return;
38437         };
38438         if(e.shiftKey && this.last !== false){
38439             var last = this.last;
38440             this.selectRange(last, rowIndex, e.ctrlKey);
38441             this.last = last; // reset the last
38442             view.focusRow(rowIndex);
38443         }else{
38444             var isSelected = this.isSelected(rowIndex);
38445             if(e.button !== 0 && isSelected){
38446                 view.focusRow(rowIndex);
38447             }else if(e.ctrlKey && isSelected){
38448                 this.deselectRow(rowIndex);
38449             }else if(!isSelected){
38450                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
38451                 view.focusRow(rowIndex);
38452             }
38453         }
38454         this.fireEvent("afterselectionchange", this);
38455     },
38456     // private
38457     handleDragableRowClick :  function(grid, rowIndex, e) 
38458     {
38459         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
38460             this.selectRow(rowIndex, false);
38461             grid.view.focusRow(rowIndex);
38462              this.fireEvent("afterselectionchange", this);
38463         }
38464     },
38465     
38466     /**
38467      * Selects multiple rows.
38468      * @param {Array} rows Array of the indexes of the row to select
38469      * @param {Boolean} keepExisting (optional) True to keep existing selections
38470      */
38471     selectRows : function(rows, keepExisting){
38472         if(!keepExisting){
38473             this.clearSelections();
38474         }
38475         for(var i = 0, len = rows.length; i < len; i++){
38476             this.selectRow(rows[i], true);
38477         }
38478     },
38479
38480     /**
38481      * Selects a range of rows. All rows in between startRow and endRow are also selected.
38482      * @param {Number} startRow The index of the first row in the range
38483      * @param {Number} endRow The index of the last row in the range
38484      * @param {Boolean} keepExisting (optional) True to retain existing selections
38485      */
38486     selectRange : function(startRow, endRow, keepExisting){
38487         if(this.locked) return;
38488         if(!keepExisting){
38489             this.clearSelections();
38490         }
38491         if(startRow <= endRow){
38492             for(var i = startRow; i <= endRow; i++){
38493                 this.selectRow(i, true);
38494             }
38495         }else{
38496             for(var i = startRow; i >= endRow; i--){
38497                 this.selectRow(i, true);
38498             }
38499         }
38500     },
38501
38502     /**
38503      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
38504      * @param {Number} startRow The index of the first row in the range
38505      * @param {Number} endRow The index of the last row in the range
38506      */
38507     deselectRange : function(startRow, endRow, preventViewNotify){
38508         if(this.locked) return;
38509         for(var i = startRow; i <= endRow; i++){
38510             this.deselectRow(i, preventViewNotify);
38511         }
38512     },
38513
38514     /**
38515      * Selects a row.
38516      * @param {Number} row The index of the row to select
38517      * @param {Boolean} keepExisting (optional) True to keep existing selections
38518      */
38519     selectRow : function(index, keepExisting, preventViewNotify){
38520         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
38521         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
38522             if(!keepExisting || this.singleSelect){
38523                 this.clearSelections();
38524             }
38525             var r = this.grid.dataSource.getAt(index);
38526             this.selections.add(r);
38527             this.last = this.lastActive = index;
38528             if(!preventViewNotify){
38529                 this.grid.getView().onRowSelect(index);
38530             }
38531             this.fireEvent("rowselect", this, index, r);
38532             this.fireEvent("selectionchange", this);
38533         }
38534     },
38535
38536     /**
38537      * Deselects a row.
38538      * @param {Number} row The index of the row to deselect
38539      */
38540     deselectRow : function(index, preventViewNotify){
38541         if(this.locked) return;
38542         if(this.last == index){
38543             this.last = false;
38544         }
38545         if(this.lastActive == index){
38546             this.lastActive = false;
38547         }
38548         var r = this.grid.dataSource.getAt(index);
38549         this.selections.remove(r);
38550         if(!preventViewNotify){
38551             this.grid.getView().onRowDeselect(index);
38552         }
38553         this.fireEvent("rowdeselect", this, index);
38554         this.fireEvent("selectionchange", this);
38555     },
38556
38557     // private
38558     restoreLast : function(){
38559         if(this._last){
38560             this.last = this._last;
38561         }
38562     },
38563
38564     // private
38565     acceptsNav : function(row, col, cm){
38566         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38567     },
38568
38569     // private
38570     onEditorKey : function(field, e){
38571         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
38572         if(k == e.TAB){
38573             e.stopEvent();
38574             ed.completeEdit();
38575             if(e.shiftKey){
38576                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38577             }else{
38578                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38579             }
38580         }else if(k == e.ENTER && !e.ctrlKey){
38581             e.stopEvent();
38582             ed.completeEdit();
38583             if(e.shiftKey){
38584                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
38585             }else{
38586                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
38587             }
38588         }else if(k == e.ESC){
38589             ed.cancelEdit();
38590         }
38591         if(newCell){
38592             g.startEditing(newCell[0], newCell[1]);
38593         }
38594     }
38595 });/*
38596  * Based on:
38597  * Ext JS Library 1.1.1
38598  * Copyright(c) 2006-2007, Ext JS, LLC.
38599  *
38600  * Originally Released Under LGPL - original licence link has changed is not relivant.
38601  *
38602  * Fork - LGPL
38603  * <script type="text/javascript">
38604  */
38605 /**
38606  * @class Roo.grid.CellSelectionModel
38607  * @extends Roo.grid.AbstractSelectionModel
38608  * This class provides the basic implementation for cell selection in a grid.
38609  * @constructor
38610  * @param {Object} config The object containing the configuration of this model.
38611  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
38612  */
38613 Roo.grid.CellSelectionModel = function(config){
38614     Roo.apply(this, config);
38615
38616     this.selection = null;
38617
38618     this.addEvents({
38619         /**
38620              * @event beforerowselect
38621              * Fires before a cell is selected.
38622              * @param {SelectionModel} this
38623              * @param {Number} rowIndex The selected row index
38624              * @param {Number} colIndex The selected cell index
38625              */
38626             "beforecellselect" : true,
38627         /**
38628              * @event cellselect
38629              * Fires when a cell is selected.
38630              * @param {SelectionModel} this
38631              * @param {Number} rowIndex The selected row index
38632              * @param {Number} colIndex The selected cell index
38633              */
38634             "cellselect" : true,
38635         /**
38636              * @event selectionchange
38637              * Fires when the active selection changes.
38638              * @param {SelectionModel} this
38639              * @param {Object} selection null for no selection or an object (o) with two properties
38640                 <ul>
38641                 <li>o.record: the record object for the row the selection is in</li>
38642                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
38643                 </ul>
38644              */
38645             "selectionchange" : true,
38646         /**
38647              * @event tabend
38648              * Fires when the tab (or enter) was pressed on the last editable cell
38649              * You can use this to trigger add new row.
38650              * @param {SelectionModel} this
38651              */
38652             "tabend" : true,
38653          /**
38654              * @event beforeeditnext
38655              * Fires before the next editable sell is made active
38656              * You can use this to skip to another cell or fire the tabend
38657              *    if you set cell to false
38658              * @param {Object} eventdata object : { cell : [ row, col ] } 
38659              */
38660             "beforeeditnext" : true
38661     });
38662     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
38663 };
38664
38665 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
38666     
38667     enter_is_tab: false,
38668
38669     /** @ignore */
38670     initEvents : function(){
38671         this.grid.on("mousedown", this.handleMouseDown, this);
38672         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
38673         var view = this.grid.view;
38674         view.on("refresh", this.onViewChange, this);
38675         view.on("rowupdated", this.onRowUpdated, this);
38676         view.on("beforerowremoved", this.clearSelections, this);
38677         view.on("beforerowsinserted", this.clearSelections, this);
38678         if(this.grid.isEditor){
38679             this.grid.on("beforeedit", this.beforeEdit,  this);
38680         }
38681     },
38682
38683         //private
38684     beforeEdit : function(e){
38685         this.select(e.row, e.column, false, true, e.record);
38686     },
38687
38688         //private
38689     onRowUpdated : function(v, index, r){
38690         if(this.selection && this.selection.record == r){
38691             v.onCellSelect(index, this.selection.cell[1]);
38692         }
38693     },
38694
38695         //private
38696     onViewChange : function(){
38697         this.clearSelections(true);
38698     },
38699
38700         /**
38701          * Returns the currently selected cell,.
38702          * @return {Array} The selected cell (row, column) or null if none selected.
38703          */
38704     getSelectedCell : function(){
38705         return this.selection ? this.selection.cell : null;
38706     },
38707
38708     /**
38709      * Clears all selections.
38710      * @param {Boolean} true to prevent the gridview from being notified about the change.
38711      */
38712     clearSelections : function(preventNotify){
38713         var s = this.selection;
38714         if(s){
38715             if(preventNotify !== true){
38716                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
38717             }
38718             this.selection = null;
38719             this.fireEvent("selectionchange", this, null);
38720         }
38721     },
38722
38723     /**
38724      * Returns true if there is a selection.
38725      * @return {Boolean}
38726      */
38727     hasSelection : function(){
38728         return this.selection ? true : false;
38729     },
38730
38731     /** @ignore */
38732     handleMouseDown : function(e, t){
38733         var v = this.grid.getView();
38734         if(this.isLocked()){
38735             return;
38736         };
38737         var row = v.findRowIndex(t);
38738         var cell = v.findCellIndex(t);
38739         if(row !== false && cell !== false){
38740             this.select(row, cell);
38741         }
38742     },
38743
38744     /**
38745      * Selects a cell.
38746      * @param {Number} rowIndex
38747      * @param {Number} collIndex
38748      */
38749     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
38750         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
38751             this.clearSelections();
38752             r = r || this.grid.dataSource.getAt(rowIndex);
38753             this.selection = {
38754                 record : r,
38755                 cell : [rowIndex, colIndex]
38756             };
38757             if(!preventViewNotify){
38758                 var v = this.grid.getView();
38759                 v.onCellSelect(rowIndex, colIndex);
38760                 if(preventFocus !== true){
38761                     v.focusCell(rowIndex, colIndex);
38762                 }
38763             }
38764             this.fireEvent("cellselect", this, rowIndex, colIndex);
38765             this.fireEvent("selectionchange", this, this.selection);
38766         }
38767     },
38768
38769         //private
38770     isSelectable : function(rowIndex, colIndex, cm){
38771         return !cm.isHidden(colIndex);
38772     },
38773
38774     /** @ignore */
38775     handleKeyDown : function(e){
38776         //Roo.log('Cell Sel Model handleKeyDown');
38777         if(!e.isNavKeyPress()){
38778             return;
38779         }
38780         var g = this.grid, s = this.selection;
38781         if(!s){
38782             e.stopEvent();
38783             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
38784             if(cell){
38785                 this.select(cell[0], cell[1]);
38786             }
38787             return;
38788         }
38789         var sm = this;
38790         var walk = function(row, col, step){
38791             return g.walkCells(row, col, step, sm.isSelectable,  sm);
38792         };
38793         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
38794         var newCell;
38795
38796       
38797
38798         switch(k){
38799             case e.TAB:
38800                 // handled by onEditorKey
38801                 if (g.isEditor && g.editing) {
38802                     return;
38803                 }
38804                 if(e.shiftKey) {
38805                     newCell = walk(r, c-1, -1);
38806                 } else {
38807                     newCell = walk(r, c+1, 1);
38808                 }
38809                 break;
38810             
38811             case e.DOWN:
38812                newCell = walk(r+1, c, 1);
38813                 break;
38814             
38815             case e.UP:
38816                 newCell = walk(r-1, c, -1);
38817                 break;
38818             
38819             case e.RIGHT:
38820                 newCell = walk(r, c+1, 1);
38821                 break;
38822             
38823             case e.LEFT:
38824                 newCell = walk(r, c-1, -1);
38825                 break;
38826             
38827             case e.ENTER:
38828                 
38829                 if(g.isEditor && !g.editing){
38830                    g.startEditing(r, c);
38831                    e.stopEvent();
38832                    return;
38833                 }
38834                 
38835                 
38836              break;
38837         };
38838         if(newCell){
38839             this.select(newCell[0], newCell[1]);
38840             e.stopEvent();
38841             
38842         }
38843     },
38844
38845     acceptsNav : function(row, col, cm){
38846         return !cm.isHidden(col) && cm.isCellEditable(col, row);
38847     },
38848     /**
38849      * Selects a cell.
38850      * @param {Number} field (not used) - as it's normally used as a listener
38851      * @param {Number} e - event - fake it by using
38852      *
38853      * var e = Roo.EventObjectImpl.prototype;
38854      * e.keyCode = e.TAB
38855      *
38856      * 
38857      */
38858     onEditorKey : function(field, e){
38859         
38860         var k = e.getKey(),
38861             newCell,
38862             g = this.grid,
38863             ed = g.activeEditor,
38864             forward = false;
38865         ///Roo.log('onEditorKey' + k);
38866         
38867         
38868         if (this.enter_is_tab && k == e.ENTER) {
38869             k = e.TAB;
38870         }
38871         
38872         if(k == e.TAB){
38873             if(e.shiftKey){
38874                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
38875             }else{
38876                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38877                 forward = true;
38878             }
38879             
38880             e.stopEvent();
38881             
38882         } else if(k == e.ENTER &&  !e.ctrlKey){
38883             ed.completeEdit();
38884             e.stopEvent();
38885             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
38886         
38887                 } else if(k == e.ESC){
38888             ed.cancelEdit();
38889         }
38890                 
38891         if (newCell) {
38892             var ecall = { cell : newCell, forward : forward };
38893             this.fireEvent('beforeeditnext', ecall );
38894             newCell = ecall.cell;
38895                         forward = ecall.forward;
38896         }
38897                 
38898         if(newCell){
38899             //Roo.log('next cell after edit');
38900             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
38901         } else if (forward) {
38902             // tabbed past last
38903             this.fireEvent.defer(100, this, ['tabend',this]);
38904         }
38905     }
38906 });/*
38907  * Based on:
38908  * Ext JS Library 1.1.1
38909  * Copyright(c) 2006-2007, Ext JS, LLC.
38910  *
38911  * Originally Released Under LGPL - original licence link has changed is not relivant.
38912  *
38913  * Fork - LGPL
38914  * <script type="text/javascript">
38915  */
38916  
38917 /**
38918  * @class Roo.grid.EditorGrid
38919  * @extends Roo.grid.Grid
38920  * Class for creating and editable grid.
38921  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
38922  * The container MUST have some type of size defined for the grid to fill. The container will be 
38923  * automatically set to position relative if it isn't already.
38924  * @param {Object} dataSource The data model to bind to
38925  * @param {Object} colModel The column model with info about this grid's columns
38926  */
38927 Roo.grid.EditorGrid = function(container, config){
38928     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
38929     this.getGridEl().addClass("xedit-grid");
38930
38931     if(!this.selModel){
38932         this.selModel = new Roo.grid.CellSelectionModel();
38933     }
38934
38935     this.activeEditor = null;
38936
38937         this.addEvents({
38938             /**
38939              * @event beforeedit
38940              * Fires before cell editing is triggered. The edit event object has the following properties <br />
38941              * <ul style="padding:5px;padding-left:16px;">
38942              * <li>grid - This grid</li>
38943              * <li>record - The record being edited</li>
38944              * <li>field - The field name being edited</li>
38945              * <li>value - The value for the field being edited.</li>
38946              * <li>row - The grid row index</li>
38947              * <li>column - The grid column index</li>
38948              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38949              * </ul>
38950              * @param {Object} e An edit event (see above for description)
38951              */
38952             "beforeedit" : true,
38953             /**
38954              * @event afteredit
38955              * Fires after a cell is edited. <br />
38956              * <ul style="padding:5px;padding-left:16px;">
38957              * <li>grid - This grid</li>
38958              * <li>record - The record being edited</li>
38959              * <li>field - The field name being edited</li>
38960              * <li>value - The value being set</li>
38961              * <li>originalValue - The original value for the field, before the edit.</li>
38962              * <li>row - The grid row index</li>
38963              * <li>column - The grid column index</li>
38964              * </ul>
38965              * @param {Object} e An edit event (see above for description)
38966              */
38967             "afteredit" : true,
38968             /**
38969              * @event validateedit
38970              * Fires after a cell is edited, but before the value is set in the record. 
38971          * You can use this to modify the value being set in the field, Return false
38972              * to cancel the change. The edit event object has the following properties <br />
38973              * <ul style="padding:5px;padding-left:16px;">
38974          * <li>editor - This editor</li>
38975              * <li>grid - This grid</li>
38976              * <li>record - The record being edited</li>
38977              * <li>field - The field name being edited</li>
38978              * <li>value - The value being set</li>
38979              * <li>originalValue - The original value for the field, before the edit.</li>
38980              * <li>row - The grid row index</li>
38981              * <li>column - The grid column index</li>
38982              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
38983              * </ul>
38984              * @param {Object} e An edit event (see above for description)
38985              */
38986             "validateedit" : true
38987         });
38988     this.on("bodyscroll", this.stopEditing,  this);
38989     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
38990 };
38991
38992 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
38993     /**
38994      * @cfg {Number} clicksToEdit
38995      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
38996      */
38997     clicksToEdit: 2,
38998
38999     // private
39000     isEditor : true,
39001     // private
39002     trackMouseOver: false, // causes very odd FF errors
39003
39004     onCellDblClick : function(g, row, col){
39005         this.startEditing(row, col);
39006     },
39007
39008     onEditComplete : function(ed, value, startValue){
39009         this.editing = false;
39010         this.activeEditor = null;
39011         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
39012         var r = ed.record;
39013         var field = this.colModel.getDataIndex(ed.col);
39014         var e = {
39015             grid: this,
39016             record: r,
39017             field: field,
39018             originalValue: startValue,
39019             value: value,
39020             row: ed.row,
39021             column: ed.col,
39022             cancel:false,
39023             editor: ed
39024         };
39025         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
39026         cell.show();
39027           
39028         if(String(value) !== String(startValue)){
39029             
39030             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
39031                 r.set(field, e.value);
39032                 // if we are dealing with a combo box..
39033                 // then we also set the 'name' colum to be the displayField
39034                 if (ed.field.displayField && ed.field.name) {
39035                     r.set(ed.field.name, ed.field.el.dom.value);
39036                 }
39037                 
39038                 delete e.cancel; //?? why!!!
39039                 this.fireEvent("afteredit", e);
39040             }
39041         } else {
39042             this.fireEvent("afteredit", e); // always fire it!
39043         }
39044         this.view.focusCell(ed.row, ed.col);
39045     },
39046
39047     /**
39048      * Starts editing the specified for the specified row/column
39049      * @param {Number} rowIndex
39050      * @param {Number} colIndex
39051      */
39052     startEditing : function(row, col){
39053         this.stopEditing();
39054         if(this.colModel.isCellEditable(col, row)){
39055             this.view.ensureVisible(row, col, true);
39056           
39057             var r = this.dataSource.getAt(row);
39058             var field = this.colModel.getDataIndex(col);
39059             var cell = Roo.get(this.view.getCell(row,col));
39060             var e = {
39061                 grid: this,
39062                 record: r,
39063                 field: field,
39064                 value: r.data[field],
39065                 row: row,
39066                 column: col,
39067                 cancel:false 
39068             };
39069             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
39070                 this.editing = true;
39071                 var ed = this.colModel.getCellEditor(col, row);
39072                 
39073                 if (!ed) {
39074                     return;
39075                 }
39076                 if(!ed.rendered){
39077                     ed.render(ed.parentEl || document.body);
39078                 }
39079                 ed.field.reset();
39080                
39081                 cell.hide();
39082                 
39083                 (function(){ // complex but required for focus issues in safari, ie and opera
39084                     ed.row = row;
39085                     ed.col = col;
39086                     ed.record = r;
39087                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
39088                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
39089                     this.activeEditor = ed;
39090                     var v = r.data[field];
39091                     ed.startEdit(this.view.getCell(row, col), v);
39092                     // combo's with 'displayField and name set
39093                     if (ed.field.displayField && ed.field.name) {
39094                         ed.field.el.dom.value = r.data[ed.field.name];
39095                     }
39096                     
39097                     
39098                 }).defer(50, this);
39099             }
39100         }
39101     },
39102         
39103     /**
39104      * Stops any active editing
39105      */
39106     stopEditing : function(){
39107         if(this.activeEditor){
39108             this.activeEditor.completeEdit();
39109         }
39110         this.activeEditor = null;
39111     },
39112         
39113          /**
39114      * Called to get grid's drag proxy text, by default returns this.ddText.
39115      * @return {String}
39116      */
39117     getDragDropText : function(){
39118         var count = this.selModel.getSelectedCell() ? 1 : 0;
39119         return String.format(this.ddText, count, count == 1 ? '' : 's');
39120     }
39121         
39122 });/*
39123  * Based on:
39124  * Ext JS Library 1.1.1
39125  * Copyright(c) 2006-2007, Ext JS, LLC.
39126  *
39127  * Originally Released Under LGPL - original licence link has changed is not relivant.
39128  *
39129  * Fork - LGPL
39130  * <script type="text/javascript">
39131  */
39132
39133 // private - not really -- you end up using it !
39134 // This is a support class used internally by the Grid components
39135
39136 /**
39137  * @class Roo.grid.GridEditor
39138  * @extends Roo.Editor
39139  * Class for creating and editable grid elements.
39140  * @param {Object} config any settings (must include field)
39141  */
39142 Roo.grid.GridEditor = function(field, config){
39143     if (!config && field.field) {
39144         config = field;
39145         field = Roo.factory(config.field, Roo.form);
39146     }
39147     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
39148     field.monitorTab = false;
39149 };
39150
39151 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
39152     
39153     /**
39154      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
39155      */
39156     
39157     alignment: "tl-tl",
39158     autoSize: "width",
39159     hideEl : false,
39160     cls: "x-small-editor x-grid-editor",
39161     shim:false,
39162     shadow:"frame"
39163 });/*
39164  * Based on:
39165  * Ext JS Library 1.1.1
39166  * Copyright(c) 2006-2007, Ext JS, LLC.
39167  *
39168  * Originally Released Under LGPL - original licence link has changed is not relivant.
39169  *
39170  * Fork - LGPL
39171  * <script type="text/javascript">
39172  */
39173   
39174
39175   
39176 Roo.grid.PropertyRecord = Roo.data.Record.create([
39177     {name:'name',type:'string'},  'value'
39178 ]);
39179
39180
39181 Roo.grid.PropertyStore = function(grid, source){
39182     this.grid = grid;
39183     this.store = new Roo.data.Store({
39184         recordType : Roo.grid.PropertyRecord
39185     });
39186     this.store.on('update', this.onUpdate,  this);
39187     if(source){
39188         this.setSource(source);
39189     }
39190     Roo.grid.PropertyStore.superclass.constructor.call(this);
39191 };
39192
39193
39194
39195 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
39196     setSource : function(o){
39197         this.source = o;
39198         this.store.removeAll();
39199         var data = [];
39200         for(var k in o){
39201             if(this.isEditableValue(o[k])){
39202                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
39203             }
39204         }
39205         this.store.loadRecords({records: data}, {}, true);
39206     },
39207
39208     onUpdate : function(ds, record, type){
39209         if(type == Roo.data.Record.EDIT){
39210             var v = record.data['value'];
39211             var oldValue = record.modified['value'];
39212             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
39213                 this.source[record.id] = v;
39214                 record.commit();
39215                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
39216             }else{
39217                 record.reject();
39218             }
39219         }
39220     },
39221
39222     getProperty : function(row){
39223        return this.store.getAt(row);
39224     },
39225
39226     isEditableValue: function(val){
39227         if(val && val instanceof Date){
39228             return true;
39229         }else if(typeof val == 'object' || typeof val == 'function'){
39230             return false;
39231         }
39232         return true;
39233     },
39234
39235     setValue : function(prop, value){
39236         this.source[prop] = value;
39237         this.store.getById(prop).set('value', value);
39238     },
39239
39240     getSource : function(){
39241         return this.source;
39242     }
39243 });
39244
39245 Roo.grid.PropertyColumnModel = function(grid, store){
39246     this.grid = grid;
39247     var g = Roo.grid;
39248     g.PropertyColumnModel.superclass.constructor.call(this, [
39249         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
39250         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
39251     ]);
39252     this.store = store;
39253     this.bselect = Roo.DomHelper.append(document.body, {
39254         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
39255             {tag: 'option', value: 'true', html: 'true'},
39256             {tag: 'option', value: 'false', html: 'false'}
39257         ]
39258     });
39259     Roo.id(this.bselect);
39260     var f = Roo.form;
39261     this.editors = {
39262         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
39263         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
39264         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
39265         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
39266         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
39267     };
39268     this.renderCellDelegate = this.renderCell.createDelegate(this);
39269     this.renderPropDelegate = this.renderProp.createDelegate(this);
39270 };
39271
39272 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
39273     
39274     
39275     nameText : 'Name',
39276     valueText : 'Value',
39277     
39278     dateFormat : 'm/j/Y',
39279     
39280     
39281     renderDate : function(dateVal){
39282         return dateVal.dateFormat(this.dateFormat);
39283     },
39284
39285     renderBool : function(bVal){
39286         return bVal ? 'true' : 'false';
39287     },
39288
39289     isCellEditable : function(colIndex, rowIndex){
39290         return colIndex == 1;
39291     },
39292
39293     getRenderer : function(col){
39294         return col == 1 ?
39295             this.renderCellDelegate : this.renderPropDelegate;
39296     },
39297
39298     renderProp : function(v){
39299         return this.getPropertyName(v);
39300     },
39301
39302     renderCell : function(val){
39303         var rv = val;
39304         if(val instanceof Date){
39305             rv = this.renderDate(val);
39306         }else if(typeof val == 'boolean'){
39307             rv = this.renderBool(val);
39308         }
39309         return Roo.util.Format.htmlEncode(rv);
39310     },
39311
39312     getPropertyName : function(name){
39313         var pn = this.grid.propertyNames;
39314         return pn && pn[name] ? pn[name] : name;
39315     },
39316
39317     getCellEditor : function(colIndex, rowIndex){
39318         var p = this.store.getProperty(rowIndex);
39319         var n = p.data['name'], val = p.data['value'];
39320         
39321         if(typeof(this.grid.customEditors[n]) == 'string'){
39322             return this.editors[this.grid.customEditors[n]];
39323         }
39324         if(typeof(this.grid.customEditors[n]) != 'undefined'){
39325             return this.grid.customEditors[n];
39326         }
39327         if(val instanceof Date){
39328             return this.editors['date'];
39329         }else if(typeof val == 'number'){
39330             return this.editors['number'];
39331         }else if(typeof val == 'boolean'){
39332             return this.editors['boolean'];
39333         }else{
39334             return this.editors['string'];
39335         }
39336     }
39337 });
39338
39339 /**
39340  * @class Roo.grid.PropertyGrid
39341  * @extends Roo.grid.EditorGrid
39342  * This class represents the  interface of a component based property grid control.
39343  * <br><br>Usage:<pre><code>
39344  var grid = new Roo.grid.PropertyGrid("my-container-id", {
39345       
39346  });
39347  // set any options
39348  grid.render();
39349  * </code></pre>
39350   
39351  * @constructor
39352  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
39353  * The container MUST have some type of size defined for the grid to fill. The container will be
39354  * automatically set to position relative if it isn't already.
39355  * @param {Object} config A config object that sets properties on this grid.
39356  */
39357 Roo.grid.PropertyGrid = function(container, config){
39358     config = config || {};
39359     var store = new Roo.grid.PropertyStore(this);
39360     this.store = store;
39361     var cm = new Roo.grid.PropertyColumnModel(this, store);
39362     store.store.sort('name', 'ASC');
39363     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
39364         ds: store.store,
39365         cm: cm,
39366         enableColLock:false,
39367         enableColumnMove:false,
39368         stripeRows:false,
39369         trackMouseOver: false,
39370         clicksToEdit:1
39371     }, config));
39372     this.getGridEl().addClass('x-props-grid');
39373     this.lastEditRow = null;
39374     this.on('columnresize', this.onColumnResize, this);
39375     this.addEvents({
39376          /**
39377              * @event beforepropertychange
39378              * Fires before a property changes (return false to stop?)
39379              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
39380              * @param {String} id Record Id
39381              * @param {String} newval New Value
39382          * @param {String} oldval Old Value
39383              */
39384         "beforepropertychange": true,
39385         /**
39386              * @event propertychange
39387              * Fires after a property changes
39388              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
39389              * @param {String} id Record Id
39390              * @param {String} newval New Value
39391          * @param {String} oldval Old Value
39392              */
39393         "propertychange": true
39394     });
39395     this.customEditors = this.customEditors || {};
39396 };
39397 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
39398     
39399      /**
39400      * @cfg {Object} customEditors map of colnames=> custom editors.
39401      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
39402      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
39403      * false disables editing of the field.
39404          */
39405     
39406       /**
39407      * @cfg {Object} propertyNames map of property Names to their displayed value
39408          */
39409     
39410     render : function(){
39411         Roo.grid.PropertyGrid.superclass.render.call(this);
39412         this.autoSize.defer(100, this);
39413     },
39414
39415     autoSize : function(){
39416         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
39417         if(this.view){
39418             this.view.fitColumns();
39419         }
39420     },
39421
39422     onColumnResize : function(){
39423         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
39424         this.autoSize();
39425     },
39426     /**
39427      * Sets the data for the Grid
39428      * accepts a Key => Value object of all the elements avaiable.
39429      * @param {Object} data  to appear in grid.
39430      */
39431     setSource : function(source){
39432         this.store.setSource(source);
39433         //this.autoSize();
39434     },
39435     /**
39436      * Gets all the data from the grid.
39437      * @return {Object} data  data stored in grid
39438      */
39439     getSource : function(){
39440         return this.store.getSource();
39441     }
39442 });/*
39443  * Based on:
39444  * Ext JS Library 1.1.1
39445  * Copyright(c) 2006-2007, Ext JS, LLC.
39446  *
39447  * Originally Released Under LGPL - original licence link has changed is not relivant.
39448  *
39449  * Fork - LGPL
39450  * <script type="text/javascript">
39451  */
39452  
39453 /**
39454  * @class Roo.LoadMask
39455  * A simple utility class for generically masking elements while loading data.  If the element being masked has
39456  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
39457  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
39458  * element's UpdateManager load indicator and will be destroyed after the initial load.
39459  * @constructor
39460  * Create a new LoadMask
39461  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
39462  * @param {Object} config The config object
39463  */
39464 Roo.LoadMask = function(el, config){
39465     this.el = Roo.get(el);
39466     Roo.apply(this, config);
39467     if(this.store){
39468         this.store.on('beforeload', this.onBeforeLoad, this);
39469         this.store.on('load', this.onLoad, this);
39470         this.store.on('loadexception', this.onLoadException, this);
39471         this.removeMask = false;
39472     }else{
39473         var um = this.el.getUpdateManager();
39474         um.showLoadIndicator = false; // disable the default indicator
39475         um.on('beforeupdate', this.onBeforeLoad, this);
39476         um.on('update', this.onLoad, this);
39477         um.on('failure', this.onLoad, this);
39478         this.removeMask = true;
39479     }
39480 };
39481
39482 Roo.LoadMask.prototype = {
39483     /**
39484      * @cfg {Boolean} removeMask
39485      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
39486      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
39487      */
39488     /**
39489      * @cfg {String} msg
39490      * The text to display in a centered loading message box (defaults to 'Loading...')
39491      */
39492     msg : 'Loading...',
39493     /**
39494      * @cfg {String} msgCls
39495      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
39496      */
39497     msgCls : 'x-mask-loading',
39498
39499     /**
39500      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
39501      * @type Boolean
39502      */
39503     disabled: false,
39504
39505     /**
39506      * Disables the mask to prevent it from being displayed
39507      */
39508     disable : function(){
39509        this.disabled = true;
39510     },
39511
39512     /**
39513      * Enables the mask so that it can be displayed
39514      */
39515     enable : function(){
39516         this.disabled = false;
39517     },
39518     
39519     onLoadException : function()
39520     {
39521         Roo.log(arguments);
39522         
39523         if (typeof(arguments[3]) != 'undefined') {
39524             Roo.MessageBox.alert("Error loading",arguments[3]);
39525         } 
39526         /*
39527         try {
39528             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39529                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39530             }   
39531         } catch(e) {
39532             
39533         }
39534         */
39535     
39536         
39537         
39538         this.el.unmask(this.removeMask);
39539     },
39540     // private
39541     onLoad : function()
39542     {
39543         this.el.unmask(this.removeMask);
39544     },
39545
39546     // private
39547     onBeforeLoad : function(){
39548         if(!this.disabled){
39549             this.el.mask(this.msg, this.msgCls);
39550         }
39551     },
39552
39553     // private
39554     destroy : function(){
39555         if(this.store){
39556             this.store.un('beforeload', this.onBeforeLoad, this);
39557             this.store.un('load', this.onLoad, this);
39558             this.store.un('loadexception', this.onLoadException, this);
39559         }else{
39560             var um = this.el.getUpdateManager();
39561             um.un('beforeupdate', this.onBeforeLoad, this);
39562             um.un('update', this.onLoad, this);
39563             um.un('failure', this.onLoad, this);
39564         }
39565     }
39566 };/*
39567  * Based on:
39568  * Ext JS Library 1.1.1
39569  * Copyright(c) 2006-2007, Ext JS, LLC.
39570  *
39571  * Originally Released Under LGPL - original licence link has changed is not relivant.
39572  *
39573  * Fork - LGPL
39574  * <script type="text/javascript">
39575  */
39576
39577
39578 /**
39579  * @class Roo.XTemplate
39580  * @extends Roo.Template
39581  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
39582 <pre><code>
39583 var t = new Roo.XTemplate(
39584         '&lt;select name="{name}"&gt;',
39585                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
39586         '&lt;/select&gt;'
39587 );
39588  
39589 // then append, applying the master template values
39590  </code></pre>
39591  *
39592  * Supported features:
39593  *
39594  *  Tags:
39595
39596 <pre><code>
39597       {a_variable} - output encoded.
39598       {a_variable.format:("Y-m-d")} - call a method on the variable
39599       {a_variable:raw} - unencoded output
39600       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
39601       {a_variable:this.method_on_template(...)} - call a method on the template object.
39602  
39603 </code></pre>
39604  *  The tpl tag:
39605 <pre><code>
39606         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
39607         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
39608         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
39609         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
39610   
39611         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
39612         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
39613 </code></pre>
39614  *      
39615  */
39616 Roo.XTemplate = function()
39617 {
39618     Roo.XTemplate.superclass.constructor.apply(this, arguments);
39619     if (this.html) {
39620         this.compile();
39621     }
39622 };
39623
39624
39625 Roo.extend(Roo.XTemplate, Roo.Template, {
39626
39627     /**
39628      * The various sub templates
39629      */
39630     tpls : false,
39631     /**
39632      *
39633      * basic tag replacing syntax
39634      * WORD:WORD()
39635      *
39636      * // you can fake an object call by doing this
39637      *  x.t:(test,tesT) 
39638      * 
39639      */
39640     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
39641
39642     /**
39643      * compile the template
39644      *
39645      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
39646      *
39647      */
39648     compile: function()
39649     {
39650         var s = this.html;
39651      
39652         s = ['<tpl>', s, '</tpl>'].join('');
39653     
39654         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
39655             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
39656             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
39657             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
39658             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
39659             m,
39660             id     = 0,
39661             tpls   = [];
39662     
39663         while(true == !!(m = s.match(re))){
39664             var forMatch   = m[0].match(nameRe),
39665                 ifMatch   = m[0].match(ifRe),
39666                 execMatch   = m[0].match(execRe),
39667                 namedMatch   = m[0].match(namedRe),
39668                 
39669                 exp  = null, 
39670                 fn   = null,
39671                 exec = null,
39672                 name = forMatch && forMatch[1] ? forMatch[1] : '';
39673                 
39674             if (ifMatch) {
39675                 // if - puts fn into test..
39676                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
39677                 if(exp){
39678                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
39679                 }
39680             }
39681             
39682             if (execMatch) {
39683                 // exec - calls a function... returns empty if true is  returned.
39684                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
39685                 if(exp){
39686                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
39687                 }
39688             }
39689             
39690             
39691             if (name) {
39692                 // for = 
39693                 switch(name){
39694                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
39695                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
39696                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
39697                 }
39698             }
39699             var uid = namedMatch ? namedMatch[1] : id;
39700             
39701             
39702             tpls.push({
39703                 id:     namedMatch ? namedMatch[1] : id,
39704                 target: name,
39705                 exec:   exec,
39706                 test:   fn,
39707                 body:   m[1] || ''
39708             });
39709             if (namedMatch) {
39710                 s = s.replace(m[0], '');
39711             } else { 
39712                 s = s.replace(m[0], '{xtpl'+ id + '}');
39713             }
39714             ++id;
39715         }
39716         this.tpls = [];
39717         for(var i = tpls.length-1; i >= 0; --i){
39718             this.compileTpl(tpls[i]);
39719             this.tpls[tpls[i].id] = tpls[i];
39720         }
39721         this.master = tpls[tpls.length-1];
39722         return this;
39723     },
39724     /**
39725      * same as applyTemplate, except it's done to one of the subTemplates
39726      * when using named templates, you can do:
39727      *
39728      * var str = pl.applySubTemplate('your-name', values);
39729      *
39730      * 
39731      * @param {Number} id of the template
39732      * @param {Object} values to apply to template
39733      * @param {Object} parent (normaly the instance of this object)
39734      */
39735     applySubTemplate : function(id, values, parent)
39736     {
39737         
39738         
39739         var t = this.tpls[id];
39740         
39741         
39742         try { 
39743             if(t.test && !t.test.call(this, values, parent)){
39744                 return '';
39745             }
39746         } catch(e) {
39747             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
39748             Roo.log(e.toString());
39749             Roo.log(t.test);
39750             return ''
39751         }
39752         try { 
39753             
39754             if(t.exec && t.exec.call(this, values, parent)){
39755                 return '';
39756             }
39757         } catch(e) {
39758             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
39759             Roo.log(e.toString());
39760             Roo.log(t.exec);
39761             return ''
39762         }
39763         try {
39764             var vs = t.target ? t.target.call(this, values, parent) : values;
39765             parent = t.target ? values : parent;
39766             if(t.target && vs instanceof Array){
39767                 var buf = [];
39768                 for(var i = 0, len = vs.length; i < len; i++){
39769                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
39770                 }
39771                 return buf.join('');
39772             }
39773             return t.compiled.call(this, vs, parent);
39774         } catch (e) {
39775             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
39776             Roo.log(e.toString());
39777             Roo.log(t.compiled);
39778             return '';
39779         }
39780     },
39781
39782     compileTpl : function(tpl)
39783     {
39784         var fm = Roo.util.Format;
39785         var useF = this.disableFormats !== true;
39786         var sep = Roo.isGecko ? "+" : ",";
39787         var undef = function(str) {
39788             Roo.log("Property not found :"  + str);
39789             return '';
39790         };
39791         
39792         var fn = function(m, name, format, args)
39793         {
39794             //Roo.log(arguments);
39795             args = args ? args.replace(/\\'/g,"'") : args;
39796             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
39797             if (typeof(format) == 'undefined') {
39798                 format= 'htmlEncode';
39799             }
39800             if (format == 'raw' ) {
39801                 format = false;
39802             }
39803             
39804             if(name.substr(0, 4) == 'xtpl'){
39805                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
39806             }
39807             
39808             // build an array of options to determine if value is undefined..
39809             
39810             // basically get 'xxxx.yyyy' then do
39811             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
39812             //    (function () { Roo.log("Property not found"); return ''; })() :
39813             //    ......
39814             
39815             var udef_ar = [];
39816             var lookfor = '';
39817             Roo.each(name.split('.'), function(st) {
39818                 lookfor += (lookfor.length ? '.': '') + st;
39819                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
39820             });
39821             
39822             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
39823             
39824             
39825             if(format && useF){
39826                 
39827                 args = args ? ',' + args : "";
39828                  
39829                 if(format.substr(0, 5) != "this."){
39830                     format = "fm." + format + '(';
39831                 }else{
39832                     format = 'this.call("'+ format.substr(5) + '", ';
39833                     args = ", values";
39834                 }
39835                 
39836                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
39837             }
39838              
39839             if (args.length) {
39840                 // called with xxyx.yuu:(test,test)
39841                 // change to ()
39842                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
39843             }
39844             // raw.. - :raw modifier..
39845             return "'"+ sep + udef_st  + name + ")"+sep+"'";
39846             
39847         };
39848         var body;
39849         // branched to use + in gecko and [].join() in others
39850         if(Roo.isGecko){
39851             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
39852                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
39853                     "';};};";
39854         }else{
39855             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
39856             body.push(tpl.body.replace(/(\r\n|\n)/g,
39857                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
39858             body.push("'].join('');};};");
39859             body = body.join('');
39860         }
39861         
39862         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
39863        
39864         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
39865         eval(body);
39866         
39867         return this;
39868     },
39869
39870     applyTemplate : function(values){
39871         return this.master.compiled.call(this, values, {});
39872         //var s = this.subs;
39873     },
39874
39875     apply : function(){
39876         return this.applyTemplate.apply(this, arguments);
39877     }
39878
39879  });
39880
39881 Roo.XTemplate.from = function(el){
39882     el = Roo.getDom(el);
39883     return new Roo.XTemplate(el.value || el.innerHTML);
39884 };/*
39885  * Original code for Roojs - LGPL
39886  * <script type="text/javascript">
39887  */
39888  
39889 /**
39890  * @class Roo.XComponent
39891  * A delayed Element creator...
39892  * Or a way to group chunks of interface together.
39893  * 
39894  * Mypart.xyx = new Roo.XComponent({
39895
39896     parent : 'Mypart.xyz', // empty == document.element.!!
39897     order : '001',
39898     name : 'xxxx'
39899     region : 'xxxx'
39900     disabled : function() {} 
39901      
39902     tree : function() { // return an tree of xtype declared components
39903         var MODULE = this;
39904         return 
39905         {
39906             xtype : 'NestedLayoutPanel',
39907             // technicall
39908         }
39909      ]
39910  *})
39911  *
39912  *
39913  * It can be used to build a big heiracy, with parent etc.
39914  * or you can just use this to render a single compoent to a dom element
39915  * MYPART.render(Roo.Element | String(id) | dom_element )
39916  * 
39917  * @extends Roo.util.Observable
39918  * @constructor
39919  * @param cfg {Object} configuration of component
39920  * 
39921  */
39922 Roo.XComponent = function(cfg) {
39923     Roo.apply(this, cfg);
39924     this.addEvents({ 
39925         /**
39926              * @event built
39927              * Fires when this the componnt is built
39928              * @param {Roo.XComponent} c the component
39929              */
39930         'built' : true
39931         
39932     });
39933     this.region = this.region || 'center'; // default..
39934     Roo.XComponent.register(this);
39935     this.modules = false;
39936     this.el = false; // where the layout goes..
39937     
39938     
39939 }
39940 Roo.extend(Roo.XComponent, Roo.util.Observable, {
39941     /**
39942      * @property el
39943      * The created element (with Roo.factory())
39944      * @type {Roo.Layout}
39945      */
39946     el  : false,
39947     
39948     /**
39949      * @property el
39950      * for BC  - use el in new code
39951      * @type {Roo.Layout}
39952      */
39953     panel : false,
39954     
39955     /**
39956      * @property layout
39957      * for BC  - use el in new code
39958      * @type {Roo.Layout}
39959      */
39960     layout : false,
39961     
39962      /**
39963      * @cfg {Function|boolean} disabled
39964      * If this module is disabled by some rule, return true from the funtion
39965      */
39966     disabled : false,
39967     
39968     /**
39969      * @cfg {String} parent 
39970      * Name of parent element which it get xtype added to..
39971      */
39972     parent: false,
39973     
39974     /**
39975      * @cfg {String} order
39976      * Used to set the order in which elements are created (usefull for multiple tabs)
39977      */
39978     
39979     order : false,
39980     /**
39981      * @cfg {String} name
39982      * String to display while loading.
39983      */
39984     name : false,
39985     /**
39986      * @cfg {String} region
39987      * Region to render component to (defaults to center)
39988      */
39989     region : 'center',
39990     
39991     /**
39992      * @cfg {Array} items
39993      * A single item array - the first element is the root of the tree..
39994      * It's done this way to stay compatible with the Xtype system...
39995      */
39996     items : false,
39997     
39998     /**
39999      * @property _tree
40000      * The method that retuns the tree of parts that make up this compoennt 
40001      * @type {function}
40002      */
40003     _tree  : false,
40004     
40005      /**
40006      * render
40007      * render element to dom or tree
40008      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
40009      */
40010     
40011     render : function(el)
40012     {
40013         
40014         el = el || false;
40015         var hp = this.parent ? 1 : 0;
40016         
40017         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
40018             // if parent is a '#.....' string, then let's use that..
40019             var ename = this.parent.substr(1)
40020             this.parent = false;
40021             el = Roo.get(ename);
40022             if (!el) {
40023                 Roo.log("Warning - element can not be found :#" + ename );
40024                 return;
40025             }
40026         }
40027         
40028         
40029         if (!this.parent) {
40030             
40031             el = el ? Roo.get(el) : false;      
40032             
40033             // it's a top level one..
40034             this.parent =  {
40035                 el : new Roo.BorderLayout(el || document.body, {
40036                 
40037                      center: {
40038                          titlebar: false,
40039                          autoScroll:false,
40040                          closeOnTab: true,
40041                          tabPosition: 'top',
40042                           //resizeTabs: true,
40043                          alwaysShowTabs: el && hp? false :  true,
40044                          hideTabs: el || !hp ? true :  false,
40045                          minTabWidth: 140
40046                      }
40047                  })
40048             }
40049         }
40050         
40051                 if (!this.parent.el) {
40052                         // probably an old style ctor, which has been disabled.
40053                         return;
40054                         
40055                 }
40056                 // The 'tree' method is  '_tree now' 
40057             
40058         var tree = this._tree ? this._tree() : this.tree();
40059         tree.region = tree.region || this.region;
40060         this.el = this.parent.el.addxtype(tree);
40061         this.fireEvent('built', this);
40062         
40063         this.panel = this.el;
40064         this.layout = this.panel.layout;
40065                 this.parentLayout = this.parent.layout  || false;  
40066          
40067     }
40068     
40069 });
40070
40071 Roo.apply(Roo.XComponent, {
40072     /**
40073      * @property  hideProgress
40074      * true to disable the building progress bar.. usefull on single page renders.
40075      * @type Boolean
40076      */
40077     hideProgress : false,
40078     /**
40079      * @property  buildCompleted
40080      * True when the builder has completed building the interface.
40081      * @type Boolean
40082      */
40083     buildCompleted : false,
40084      
40085     /**
40086      * @property  topModule
40087      * the upper most module - uses document.element as it's constructor.
40088      * @type Object
40089      */
40090      
40091     topModule  : false,
40092       
40093     /**
40094      * @property  modules
40095      * array of modules to be created by registration system.
40096      * @type {Array} of Roo.XComponent
40097      */
40098     
40099     modules : [],
40100     /**
40101      * @property  elmodules
40102      * array of modules to be created by which use #ID 
40103      * @type {Array} of Roo.XComponent
40104      */
40105      
40106     elmodules : [],
40107
40108     
40109     /**
40110      * Register components to be built later.
40111      *
40112      * This solves the following issues
40113      * - Building is not done on page load, but after an authentication process has occured.
40114      * - Interface elements are registered on page load
40115      * - Parent Interface elements may not be loaded before child, so this handles that..
40116      * 
40117      *
40118      * example:
40119      * 
40120      * MyApp.register({
40121           order : '000001',
40122           module : 'Pman.Tab.projectMgr',
40123           region : 'center',
40124           parent : 'Pman.layout',
40125           disabled : false,  // or use a function..
40126         })
40127      
40128      * * @param {Object} details about module
40129      */
40130     register : function(obj) {
40131                 
40132         Roo.XComponent.event.fireEvent('register', obj);
40133         switch(typeof(obj.disabled) ) {
40134                 
40135             case 'undefined':
40136                 break;
40137             
40138             case 'function':
40139                 if ( obj.disabled() ) {
40140                         return;
40141                 }
40142                 break;
40143             
40144             default:
40145                 if (obj.disabled) {
40146                         return;
40147                 }
40148                 break;
40149         }
40150                 
40151         this.modules.push(obj);
40152          
40153     },
40154     /**
40155      * convert a string to an object..
40156      * eg. 'AAA.BBB' -> finds AAA.BBB
40157
40158      */
40159     
40160     toObject : function(str)
40161     {
40162         if (!str || typeof(str) == 'object') {
40163             return str;
40164         }
40165         if (str.substring(0,1) == '#') {
40166             return str;
40167         }
40168
40169         var ar = str.split('.');
40170         var rt, o;
40171         rt = ar.shift();
40172             /** eval:var:o */
40173         try {
40174             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
40175         } catch (e) {
40176             throw "Module not found : " + str;
40177         }
40178         
40179         if (o === false) {
40180             throw "Module not found : " + str;
40181         }
40182         Roo.each(ar, function(e) {
40183             if (typeof(o[e]) == 'undefined') {
40184                 throw "Module not found : " + str;
40185             }
40186             o = o[e];
40187         });
40188         
40189         return o;
40190         
40191     },
40192     
40193     
40194     /**
40195      * move modules into their correct place in the tree..
40196      * 
40197      */
40198     preBuild : function ()
40199     {
40200         var _t = this;
40201         Roo.each(this.modules , function (obj)
40202         {
40203             Roo.XComponent.event.fireEvent('beforebuild', obj);
40204             
40205             var opar = obj.parent;
40206             try { 
40207                 obj.parent = this.toObject(opar);
40208             } catch(e) {
40209                 Roo.log("parent:toObject failed: " + e.toString());
40210                 return;
40211             }
40212             
40213             if (!obj.parent) {
40214                 Roo.debug && Roo.log("GOT top level module");
40215                 Roo.debug && Roo.log(obj);
40216                 obj.modules = new Roo.util.MixedCollection(false, 
40217                     function(o) { return o.order + '' }
40218                 );
40219                 this.topModule = obj;
40220                 return;
40221             }
40222                         // parent is a string (usually a dom element name..)
40223             if (typeof(obj.parent) == 'string') {
40224                 this.elmodules.push(obj);
40225                 return;
40226             }
40227             if (obj.parent.constructor != Roo.XComponent) {
40228                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
40229             }
40230             if (!obj.parent.modules) {
40231                 obj.parent.modules = new Roo.util.MixedCollection(false, 
40232                     function(o) { return o.order + '' }
40233                 );
40234             }
40235             if (obj.parent.disabled) {
40236                 obj.disabled = true;
40237             }
40238             obj.parent.modules.add(obj);
40239         }, this);
40240     },
40241     
40242      /**
40243      * make a list of modules to build.
40244      * @return {Array} list of modules. 
40245      */ 
40246     
40247     buildOrder : function()
40248     {
40249         var _this = this;
40250         var cmp = function(a,b) {   
40251             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
40252         };
40253         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
40254             throw "No top level modules to build";
40255         }
40256         
40257         // make a flat list in order of modules to build.
40258         var mods = this.topModule ? [ this.topModule ] : [];
40259                 
40260         
40261         // elmodules (is a list of DOM based modules )
40262         Roo.each(this.elmodules, function(e) {
40263             mods.push(e);
40264             if (!this.topModule &&
40265                 typeof(e.parent) == 'string' &&
40266                 e.parent.substring(0,1) == '#' &&
40267                 Roo.get(e.parent.substr(1))
40268                ) {
40269                 
40270                 _this.topModule = e;
40271             }
40272             
40273         });
40274
40275         
40276         // add modules to their parents..
40277         var addMod = function(m) {
40278             Roo.debug && Roo.log("build Order: add: " + m.name);
40279                 
40280             mods.push(m);
40281             if (m.modules && !m.disabled) {
40282                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
40283                 m.modules.keySort('ASC',  cmp );
40284                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
40285     
40286                 m.modules.each(addMod);
40287             } else {
40288                 Roo.debug && Roo.log("build Order: no child modules");
40289             }
40290             // not sure if this is used any more..
40291             if (m.finalize) {
40292                 m.finalize.name = m.name + " (clean up) ";
40293                 mods.push(m.finalize);
40294             }
40295             
40296         }
40297         if (this.topModule && this.topModule.modules) { 
40298             this.topModule.modules.keySort('ASC',  cmp );
40299             this.topModule.modules.each(addMod);
40300         } 
40301         return mods;
40302     },
40303     
40304      /**
40305      * Build the registered modules.
40306      * @param {Object} parent element.
40307      * @param {Function} optional method to call after module has been added.
40308      * 
40309      */ 
40310    
40311     build : function() 
40312     {
40313         
40314         this.preBuild();
40315         var mods = this.buildOrder();
40316       
40317         //this.allmods = mods;
40318         //Roo.debug && Roo.log(mods);
40319         //return;
40320         if (!mods.length) { // should not happen
40321             throw "NO modules!!!";
40322         }
40323         
40324         
40325         var msg = "Building Interface...";
40326         // flash it up as modal - so we store the mask!?
40327         if (!this.hideProgress) {
40328             Roo.MessageBox.show({ title: 'loading' });
40329             Roo.MessageBox.show({
40330                title: "Please wait...",
40331                msg: msg,
40332                width:450,
40333                progress:true,
40334                closable:false,
40335                modal: false
40336               
40337             });
40338         }
40339         var total = mods.length;
40340         
40341         var _this = this;
40342         var progressRun = function() {
40343             if (!mods.length) {
40344                 Roo.debug && Roo.log('hide?');
40345                 if (!this.hideProgress) {
40346                     Roo.MessageBox.hide();
40347                 }
40348                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
40349                 
40350                 // THE END...
40351                 return false;   
40352             }
40353             
40354             var m = mods.shift();
40355             
40356             
40357             Roo.debug && Roo.log(m);
40358             // not sure if this is supported any more.. - modules that are are just function
40359             if (typeof(m) == 'function') { 
40360                 m.call(this);
40361                 return progressRun.defer(10, _this);
40362             } 
40363             
40364             
40365             msg = "Building Interface " + (total  - mods.length) + 
40366                     " of " + total + 
40367                     (m.name ? (' - ' + m.name) : '');
40368                         Roo.debug && Roo.log(msg);
40369             if (!this.hideProgress) { 
40370                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
40371             }
40372             
40373          
40374             // is the module disabled?
40375             var disabled = (typeof(m.disabled) == 'function') ?
40376                 m.disabled.call(m.module.disabled) : m.disabled;    
40377             
40378             
40379             if (disabled) {
40380                 return progressRun(); // we do not update the display!
40381             }
40382             
40383             // now build 
40384             
40385                         
40386                         
40387             m.render();
40388             // it's 10 on top level, and 1 on others??? why...
40389             return progressRun.defer(10, _this);
40390              
40391         }
40392         progressRun.defer(1, _this);
40393      
40394         
40395         
40396     },
40397         
40398         
40399         /**
40400          * Event Object.
40401          *
40402          *
40403          */
40404         event: false, 
40405     /**
40406          * wrapper for event.on - aliased later..  
40407          * Typically use to register a event handler for register:
40408          *
40409          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
40410          *
40411          */
40412     on : false
40413    
40414     
40415     
40416 });
40417
40418 Roo.XComponent.event = new Roo.util.Observable({
40419                 events : { 
40420                         /**
40421                          * @event register
40422                          * Fires when an Component is registered,
40423                          * set the disable property on the Component to stop registration.
40424                          * @param {Roo.XComponent} c the component being registerd.
40425                          * 
40426                          */
40427                         'register' : true,
40428             /**
40429                          * @event beforebuild
40430                          * Fires before each Component is built
40431                          * can be used to apply permissions.
40432                          * @param {Roo.XComponent} c the component being registerd.
40433                          * 
40434                          */
40435                         'beforebuild' : true,
40436                         /**
40437                          * @event buildcomplete
40438                          * Fires on the top level element when all elements have been built
40439                          * @param {Roo.XComponent} the top level component.
40440                          */
40441                         'buildcomplete' : true
40442                         
40443                 }
40444 });
40445
40446 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
40447